[rfw] Remote Flutter Widgets package (#452)
diff --git a/.cirrus.yml b/.cirrus.yml
index 7267667..78b6a46 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -8,7 +8,7 @@
- git fetch origin master # To set FETCH_HEAD for "git merge-base" to work
# Pinned version of the plugin tools, to avoid breakage in this repository
# when pushing updates from flutter/plugins.
- - dart pub global activate flutter_plugin_tools 0.6.0+1
+ - dart pub global activate flutter_plugin_tools 0.7.1
flutter_upgrade_template: &FLUTTER_UPGRADE_TEMPLATE
upgrade_flutter_script:
@@ -41,7 +41,7 @@
always:
format_script: ./script/tool_runner.sh format --fail-on-change --clang-format=clang-format-5.0
license_script: dart pub global run flutter_plugin_tools license-check
- analyze_script: ./script/tool_runner.sh analyze --custom-analysis=web_benchmarks/testing/test_app,flutter_lints/example
+ analyze_script: ./script/tool_runner.sh analyze --custom-analysis=web_benchmarks/testing/test_app,flutter_lints/example,rfw/example
pubspec_script: ./script/tool_runner.sh pubspec-check
- name: publishable
version_script: ./script/tool_runner.sh version-check
@@ -61,9 +61,18 @@
script:
# extension_google_sign_in_as_googleapis_auth is currently not building, see
# https://github.com/flutter/flutter/issues/89301
- - ./script/tool_runner.sh build-examples --apk --exclude=extension_google_sign_in_as_googleapis_auth
- # Must come after apk build:
- - ./script/tool_runner.sh native-test --android --no-integration
+ # rfw is excluded until the next Flutter stable release because it depends
+ # on features that have never shipped to stable. (The rfw package has
+ # never worked on stable so this is not going to break anyone.)
+ # When updating this, also look at the ios tests below.
+ # When updating this, also update the `rfw/run_tests.sh` file.
+ - if [[ "$CHANNEL" == "master" ]]; then
+ - ./script/tool_runner.sh build-examples --apk --exclude=extension_google_sign_in_as_googleapis_auth
+ - ./script/tool_runner.sh native-test --android --no-integration
+ - else
+ - ./script/tool_runner.sh build-examples --apk --exclude=extension_google_sign_in_as_googleapis_auth,rfw
+ - ./script/tool_runner.sh native-test --android --no-integration --exclude=rfw
+ - fi
depends_on:
- format+analyze
- name: web_benchmarks_test
@@ -89,7 +98,16 @@
CHANNEL: "stable"
<< : *FLUTTER_UPGRADE_TEMPLATE
build_script:
- - ./script/tool_runner.sh build-examples --ios
+ # Exclude rfw until the next Flutter stable release because it depends
+ # on features that have never shipped to stable. (The rfw package has
+ # never worked on stable so this is not going to break anyone.)
+ # When updating this, also look at the android tests above.
+ # When updating this, also update the `rfw/run_tests.sh` file.
+ - if [[ "$CHANNEL" == "master" ]]; then
+ - ./script/tool_runner.sh build-examples --ios
+ - else
+ - ./script/tool_runner.sh build-examples --ios --exclude=rfw
+ - fi
task:
name: local_tests
diff --git a/customer_testing.bat b/customer_testing.bat
index f7d81ec..375f20d 100644
--- a/customer_testing.bat
+++ b/customer_testing.bat
@@ -11,3 +11,6 @@
CD packages/animations
CALL flutter analyze --no-fatal-infos
CALL flutter test
+
+REM We don't run the tests in packages/rfw because those tests are
+REM platform-sensitive and only work reliably on Linux.
diff --git a/customer_testing.sh b/customer_testing.sh
index b04edf9..16147f0 100755
--- a/customer_testing.sh
+++ b/customer_testing.sh
@@ -15,6 +15,21 @@
set -e
-cd packages/animations
+pushd packages/animations
flutter analyze --no-fatal-infos
flutter test
+popd
+
+pushd packages/rfw
+flutter analyze --no-fatal-infos
+if [[ "$OSTYPE" == "linux-gnu" ]]; then
+ # We only run the full tests on Linux because golden files differ
+ # from platform to platform.
+ flutter test
+fi
+# The next script verifies that the coverage is not regressed; it does
+# not verify goldens. (It does run all the tests though, so it still
+# catches logic issues on other platforms, just not issue that only
+# affect golden files.)
+./run_tests.sh
+popd
diff --git a/packages/rfw/.gitignore b/packages/rfw/.gitignore
new file mode 100644
index 0000000..a247422
--- /dev/null
+++ b/packages/rfw/.gitignore
@@ -0,0 +1,75 @@
+# 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
+.flutter-plugins-dependencies
+.packages
+.pub-cache/
+.pub/
+build/
+
+# Android related
+**/android/**/gradle-wrapper.jar
+**/android/.gradle
+**/android/captures/
+**/android/gradlew
+**/android/gradlew.bat
+**/android/local.properties
+**/android/**/GeneratedPluginRegistrant.java
+
+# iOS/XCode related
+**/ios/**/*.mode1v3
+**/ios/**/*.mode2v3
+**/ios/**/*.moved-aside
+**/ios/**/*.pbxuser
+**/ios/**/*.perspectivev3
+**/ios/**/*sync/
+**/ios/**/.sconsign.dblite
+**/ios/**/.tags*
+**/ios/**/.vagrant/
+**/ios/**/DerivedData/
+**/ios/**/Icon?
+**/ios/**/Pods/
+**/ios/**/.symlinks/
+**/ios/**/profile
+**/ios/**/xcuserdata
+**/ios/.generated/
+**/ios/Flutter/App.framework
+**/ios/Flutter/Flutter.framework
+**/ios/Flutter/Flutter.podspec
+**/ios/Flutter/Generated.xcconfig
+**/ios/Flutter/ephemeral
+**/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
diff --git a/packages/rfw/.metadata b/packages/rfw/.metadata
new file mode 100644
index 0000000..068d7b0
--- /dev/null
+++ b/packages/rfw/.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: fbc4e9bcc5feaa037864a5beb6b40f4cb630330b
+ channel: master
+
+project_type: package
diff --git a/packages/rfw/CHANGELOG.md b/packages/rfw/CHANGELOG.md
new file mode 100644
index 0000000..0d8803f
--- /dev/null
+++ b/packages/rfw/CHANGELOG.md
@@ -0,0 +1,3 @@
+## 1.0.0
+
+* Initial release.
diff --git a/packages/rfw/LICENSE b/packages/rfw/LICENSE
new file mode 100644
index 0000000..c6823b8
--- /dev/null
+++ b/packages/rfw/LICENSE
@@ -0,0 +1,25 @@
+Copyright 2013 The Flutter Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of Google Inc. nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/packages/rfw/README.md b/packages/rfw/README.md
new file mode 100644
index 0000000..ce6cff5
--- /dev/null
+++ b/packages/rfw/README.md
@@ -0,0 +1,242 @@
+# Remote Flutter Widgets
+
+This package provides a mechanism for rendering widgets based on
+declarative UI descriptions that can be obtained at runtime.
+
+## Getting started
+
+A Flutter application can render remote widgets using the
+`RemoteWidget` widget, as in the following snippet:
+
+```dart
+// see example/hello
+
+class Example extends StatefulWidget {
+ const Example({Key? key}) : super(key: key);
+
+ @override
+ State<Example> createState() => _ExampleState();
+}
+
+class _ExampleState extends State<Example> {
+ final Runtime _runtime = Runtime();
+ final DynamicContent _data = DynamicContent();
+
+ // Normally this would be obtained dynamically, but for this example
+ // we hard-code the "remote" widgets into the app.
+ //
+ // Also, normally we would decode this with [decodeLibraryBlob] rather than
+ // parsing the text version using [parseLibraryFile]. However, to make it
+ // easier to demo, this uses the slower text format.
+ static final RemoteWidgetLibrary _remoteWidgets = parseLibraryFile('''
+ // The "import" keyword is used to specify dependencies, in this case,
+ // the built-in widgets that are added by initState below.
+ import core.widgets;
+ // The "widget" keyword is used to define a new widget constructor.
+ // The "root" widget is specified as the one to render in the build
+ // method below.
+ widget root = Container(
+ color: 0xFF002211,
+ child: Center(
+ child: Text(text: ["Hello, ", data.greet.name, "!"], textDirection: "ltr"),
+ ),
+ );
+ ''');
+
+ @override
+ void initState() {
+ super.initState();
+ _runtime.update(const LibraryName(<String>['core', 'widgets']), createCoreWidgets());
+ _runtime.update(const LibraryName(<String>['main']), _remoteWidgets);
+ _data.update('greet', <String, Object>{ 'name': 'World' });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return RemoteWidget(
+ runtime: _runtime,
+ data: _data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['main']), 'root'),
+ onEvent: (String name, DynamicMap arguments) {
+ // The example above does not have any way to trigger events, but if it
+ // did, they would result in this callback being invoked.
+ debugPrint('user triggered event "$name" with data: $arguments');
+ },
+ );
+ }
+}
+```
+
+In this example, the "remote" widgets are hard-coded into the application.
+
+## Usage
+
+In typical usage, the remote widgets come from a server at runtime,
+either through HTTP or some other network transport. Separately, the
+`DynamicContent` data is updated, either from the server or based on
+local data.
+
+It is recommended that servers send binary data, decoded using
+`decodeLibraryBlob` and `decodeDataBlob`, when providing updates for
+the remote widget libraries and data.
+
+Events (`onEvent`) are signalled by the user's interactions with the
+remote widgets. The client is responsible for handling them, either by
+sending the data to the server for the server to update the data, or
+directly, on the user's device.
+
+## Limitations
+
+Once you realize that you can ship UI (and maybe logic, e.g. using
+Wasm; see the example below) you will slowly be tempted to move your
+whole application to this model.
+
+This won't work.
+
+Flutter proper lets you create widgets for compelling UIs with
+gestures and animations and so forth. With RFW you can use those
+widgets, but it doesn't let you _create_ those widgets.
+
+For example, you don't want to use RFW to create a UI that involves
+page transitions. You don't want to use RFW to create new widgets that
+involve drag and drop. You don't want to use RFW to create widgets
+that involve custom painters.
+
+Rather, RFW is best suited for interfaces made out of prebuilt
+components. For example, a database front-end could use this to
+describe bespoke UIs for editing different types of objects in the
+database. Message-of-the-day announcements could be built using this
+mechanism. Search interfaces could use this mechanism for rich result
+cards.
+
+RFW is well-suited for describing custom UIs from a potentially
+infinite set of UIs that could not possibly have been known when the
+application was created. On the other hand, updating the application's
+look and feel, changing how navigation works in an application, or
+adding new features, are all changes that are best made in Flutter
+itself, creating a new application and shipping that through normal
+update channels.
+
+## Developing new local widget libraries
+
+A "local" widget library is one that describes the built-in widgets
+that your "remote" widgets are built out of. The RFW package comes
+with some preprepared libraries, available through [createCoreWidgets]
+and [createMaterialWidgets]. You can also create your own.
+
+When developing new local widget libraries, it is convenient to hook
+into the `reassemble` method to update the local widgets. That way,
+changes can be seen in real time when hot reloading.
+
+```dart
+// see example/local
+
+class Example extends StatefulWidget {
+ const Example({Key? key}) : super(key: key);
+
+ @override
+ State<Example> createState() => _ExampleState();
+}
+
+class _ExampleState extends State<Example> {
+ final Runtime _runtime = Runtime();
+ final DynamicContent _data = DynamicContent();
+
+ @override
+ void initState() {
+ super.initState();
+ _update();
+ }
+
+ @override
+ void reassemble() {
+ super.reassemble();
+ _update();
+ }
+
+ static WidgetLibrary _createLocalWidgets() => LocalWidgetLibrary(<String, LocalWidgetBuilder>{
+ 'GreenBox': (BuildContext context, DataSource source) {
+ return Container(color: const Color(0xFF002211), child: source.child(<Object>['child']));
+ },
+ 'Hello': (BuildContext context, DataSource source) {
+ return Center(child: Text('Hello, ${source.v<String>(<Object>["name"])}!', textDirection: TextDirection.ltr));
+ },
+ });
+
+
+ void _update() {
+ _runtime.update(const LibraryName(<String>['local']), _createLocalWidgets());
+ _runtime.update(const LibraryName(<String>['remote']), parseLibraryFile('''
+ import local;
+ widget root = GreenBox(
+ child: Hello(name: "World"),
+ );
+ '''));
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return RemoteWidget(
+ runtime: _runtime,
+ data: _data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['remote']), 'root'),
+ onEvent: (String name, DynamicMap arguments) {
+ debugPrint('user triggered event "$name" with data: $arguments');
+ },
+ );
+ }
+}
+```
+
+## Fetching remote widget libraries remotely
+
+The example in `example/remote` shows how a program could fetch
+different user interfaces at runtime. In this example, the interface
+used on startup is the one last cached locally. Each time the program
+is run, after displaying the currently-cached interface, the
+application fetches a new interface over the network, overwriting the
+one in the cache, so that a different interface is used the next time
+the app is run.
+
+This example also shows how an application can implement custom local
+code for events; in this case, incrementing a counter (both of the
+"remote" widgets are just different ways of implementing a counter).
+
+## Integrating with scripting language runtimes
+
+The example in `example/wasm` shows how a program could fetch logic in
+addition to UI, in this case using Wasm compiled from C (and let us
+briefly appreciate the absurdity of using C as a scripting language
+for an application written in Dart).
+
+In this example, as written, the Dart client could support any
+application whose data model consisted of a single integer and whose
+logic could be expressed in C without external dependencies.
+
+This example could be extended to have the C program export data in
+the Remote Flutter Widgets binary data blob format which could be
+parsed using `decodeDataBlob` and passed to `DynamicContent.update`
+(thus allowing any structured data supported by RFW), and similarly
+arguments could be passed to the Wasm code using the same format
+(encoding using `encodeDataBlob`) to allow arbitrary structured data
+to be communicated from the interface to the Wasm logic. In addition,
+the Wasm logic could be provided with WASI interface bindings or with
+custom bindings that expose platform capabilities (e.g. from Flutter
+plugins), greatly extending the scope of what could be implemented in
+the Wasm logic.
+
+As of the time of writing, `package:wasm` does not support Android,
+iOS, or web, so this demo is limited to desktop environments. The
+underlying Wasmer runtime supports Android and iOS already, and
+obviously Wasm in general is supported by web browsers, so it is
+expected that these limitations are only temporary (modulo policy
+concerns on iOS, anyway).
+
+## Contributing
+
+Adding more widgets to `lib/flutter/material_widgets.dart` is welcome.
+
+When contributing code, ensure that `flutter test --coverage; lcov
+--list coverage/lcov.info` continues to show 100% test coverage, and
+update `run_tests.sh` with the appropriate expectations to prevent
+future coverage regressions.
diff --git a/packages/rfw/example/analysis_options.yaml b/packages/rfw/example/analysis_options.yaml
new file mode 100644
index 0000000..4f212b0
--- /dev/null
+++ b/packages/rfw/example/analysis_options.yaml
@@ -0,0 +1,8 @@
+# Use the analysis options settings from the top level of the repo (not
+# the ones from above, which include the `public_member_api_docs` rule).
+
+include: ../../../analysis_options.yaml
+
+linter:
+ rules:
+ public_member_api_docs: false
diff --git a/packages/rfw/example/hello/.gitignore b/packages/rfw/example/hello/.gitignore
new file mode 100644
index 0000000..0fa6b67
--- /dev/null
+++ b/packages/rfw/example/hello/.gitignore
@@ -0,0 +1,46 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+**/ios/Flutter/.last_build_id
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+.packages
+.pub-cache/
+.pub/
+/build/
+
+# Web related
+lib/generated_plugin_registrant.dart
+
+# Symbolication related
+app.*.symbols
+
+# Obfuscation related
+app.*.map.json
+
+# Android Studio will place build artifacts here
+/android/app/debug
+/android/app/profile
+/android/app/release
diff --git a/packages/rfw/example/hello/.metadata b/packages/rfw/example/hello/.metadata
new file mode 100644
index 0000000..45c494e
--- /dev/null
+++ b/packages/rfw/example/hello/.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: a1cd3f45e02c2429e14630d6cc6de120c3d24bd0
+ channel: master
+
+project_type: app
diff --git a/packages/rfw/example/hello/README.md b/packages/rfw/example/hello/README.md
new file mode 100644
index 0000000..7e2dcff
--- /dev/null
+++ b/packages/rfw/example/hello/README.md
@@ -0,0 +1,4 @@
+# Hello World for RFW
+
+This example shows the simplest use of the `RemoteWidget` widget. In
+this example, the "remote" widget is hard-coded into the application.
diff --git a/packages/rfw/example/hello/android/.gitignore b/packages/rfw/example/hello/android/.gitignore
new file mode 100644
index 0000000..6f56801
--- /dev/null
+++ b/packages/rfw/example/hello/android/.gitignore
@@ -0,0 +1,13 @@
+gradle-wrapper.jar
+/.gradle
+/captures/
+/gradlew
+/gradlew.bat
+/local.properties
+GeneratedPluginRegistrant.java
+
+# Remember to never publicly share your keystore.
+# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
+key.properties
+**/*.keystore
+**/*.jks
diff --git a/packages/rfw/example/hello/android/app/build.gradle b/packages/rfw/example/hello/android/app/build.gradle
new file mode 100644
index 0000000..7a06a22
--- /dev/null
+++ b/packages/rfw/example/hello/android/app/build.gradle
@@ -0,0 +1,68 @@
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+ localPropertiesFile.withReader('UTF-8') { reader ->
+ localProperties.load(reader)
+ }
+}
+
+def flutterRoot = localProperties.getProperty('flutter.sdk')
+if (flutterRoot == null) {
+ throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
+}
+
+def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
+if (flutterVersionCode == null) {
+ flutterVersionCode = '1'
+}
+
+def flutterVersionName = localProperties.getProperty('flutter.versionName')
+if (flutterVersionName == null) {
+ flutterVersionName = '1.0'
+}
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
+
+android {
+ compileSdkVersion 30
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+
+ defaultConfig {
+ // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
+ applicationId "dev.flutter.rfw.examples.hello"
+ minSdkVersion 16
+ targetSdkVersion 30
+ versionCode flutterVersionCode.toInteger()
+ versionName flutterVersionName
+ }
+
+ buildTypes {
+ release {
+ // TODO: Add your own signing config for the release build.
+ // Signing with the debug keys for now, so `flutter run --release` works.
+ signingConfig signingConfigs.debug
+ }
+ }
+}
+
+flutter {
+ source '../..'
+}
+
+dependencies {
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+}
diff --git a/packages/rfw/example/hello/android/app/src/debug/AndroidManifest.xml b/packages/rfw/example/hello/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 0000000..91a24f1
--- /dev/null
+++ b/packages/rfw/example/hello/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,7 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dev.flutter.rfw.examples.hello">
+ <!-- Flutter needs it to communicate with the running application
+ to allow setting breakpoints, to provide hot reload, etc.
+ -->
+ <uses-permission android:name="android.permission.INTERNET"/>
+</manifest>
diff --git a/packages/rfw/example/hello/android/app/src/main/AndroidManifest.xml b/packages/rfw/example/hello/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..a528520
--- /dev/null
+++ b/packages/rfw/example/hello/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dev.flutter.rfw.examples.hello">
+ <application
+ android:label="hello"
+ android:icon="@mipmap/ic_launcher">
+ <activity
+ android:name=".MainActivity"
+ android:launchMode="singleTop"
+ android:theme="@style/LaunchTheme"
+ android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
+ android:hardwareAccelerated="true"
+ android:windowSoftInputMode="adjustResize">
+ <!-- Specifies an Android theme to apply to this Activity as soon as
+ the Android process has started. This theme is visible to the user
+ while the Flutter UI initializes. After that, this theme continues
+ to determine the Window background behind the Flutter UI. -->
+ <meta-data
+ android:name="io.flutter.embedding.android.NormalTheme"
+ android:resource="@style/NormalTheme"
+ />
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <!-- Don't delete the meta-data below.
+ This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
+ <meta-data
+ android:name="flutterEmbedding"
+ android:value="2" />
+ </application>
+</manifest>
diff --git a/packages/rfw/example/hello/android/app/src/main/kotlin/dev/flutter/rfw/examples/hello/MainActivity.kt b/packages/rfw/example/hello/android/app/src/main/kotlin/dev/flutter/rfw/examples/hello/MainActivity.kt
new file mode 100644
index 0000000..7f9aee8
--- /dev/null
+++ b/packages/rfw/example/hello/android/app/src/main/kotlin/dev/flutter/rfw/examples/hello/MainActivity.kt
@@ -0,0 +1,6 @@
+package dev.flutter.rfw.examples.hello
+
+import io.flutter.embedding.android.FlutterActivity
+
+class MainActivity: FlutterActivity() {
+}
diff --git a/packages/rfw/example/hello/android/app/src/main/res/drawable-v21/launch_background.xml b/packages/rfw/example/hello/android/app/src/main/res/drawable-v21/launch_background.xml
new file mode 100644
index 0000000..f74085f
--- /dev/null
+++ b/packages/rfw/example/hello/android/app/src/main/res/drawable-v21/launch_background.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Modify this file to customize your launch splash screen -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="?android:colorBackground" />
+
+ <!-- You can insert your own image assets here -->
+ <!-- <item>
+ <bitmap
+ android:gravity="center"
+ android:src="@mipmap/launch_image" />
+ </item> -->
+</layer-list>
diff --git a/packages/rfw/example/hello/android/app/src/main/res/drawable/launch_background.xml b/packages/rfw/example/hello/android/app/src/main/res/drawable/launch_background.xml
new file mode 100644
index 0000000..304732f
--- /dev/null
+++ b/packages/rfw/example/hello/android/app/src/main/res/drawable/launch_background.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Modify this file to customize your launch splash screen -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@android:color/white" />
+
+ <!-- You can insert your own image assets here -->
+ <!-- <item>
+ <bitmap
+ android:gravity="center"
+ android:src="@mipmap/launch_image" />
+ </item> -->
+</layer-list>
diff --git a/packages/rfw/example/hello/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/rfw/example/hello/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..db77bb4
--- /dev/null
+++ b/packages/rfw/example/hello/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/packages/rfw/example/hello/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/rfw/example/hello/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..17987b7
--- /dev/null
+++ b/packages/rfw/example/hello/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/packages/rfw/example/hello/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/packages/rfw/example/hello/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..09d4391
--- /dev/null
+++ b/packages/rfw/example/hello/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/packages/rfw/example/hello/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/rfw/example/hello/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..d5f1c8d
--- /dev/null
+++ b/packages/rfw/example/hello/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/packages/rfw/example/hello/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/rfw/example/hello/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4d6372e
--- /dev/null
+++ b/packages/rfw/example/hello/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/packages/rfw/example/hello/android/app/src/main/res/values-night/styles.xml b/packages/rfw/example/hello/android/app/src/main/res/values-night/styles.xml
new file mode 100644
index 0000000..449a9f9
--- /dev/null
+++ b/packages/rfw/example/hello/android/app/src/main/res/values-night/styles.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
+ <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
+ <!-- Show a splash screen on the activity. Automatically removed when
+ Flutter draws its first frame -->
+ <item name="android:windowBackground">@drawable/launch_background</item>
+ </style>
+ <!-- Theme applied to the Android Window as soon as the process has started.
+ This theme determines the color of the Android Window while your
+ Flutter UI initializes, as well as behind your Flutter UI while its
+ running.
+
+ This Theme is only used starting with V2 of Flutter's Android embedding. -->
+ <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
+ <item name="android:windowBackground">?android:colorBackground</item>
+ </style>
+</resources>
diff --git a/packages/rfw/example/hello/android/app/src/main/res/values/styles.xml b/packages/rfw/example/hello/android/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..d74aa35
--- /dev/null
+++ b/packages/rfw/example/hello/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
+ <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
+ <!-- Show a splash screen on the activity. Automatically removed when
+ Flutter draws its first frame -->
+ <item name="android:windowBackground">@drawable/launch_background</item>
+ </style>
+ <!-- Theme applied to the Android Window as soon as the process has started.
+ This theme determines the color of the Android Window while your
+ Flutter UI initializes, as well as behind your Flutter UI while its
+ running.
+
+ This Theme is only used starting with V2 of Flutter's Android embedding. -->
+ <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
+ <item name="android:windowBackground">?android:colorBackground</item>
+ </style>
+</resources>
diff --git a/packages/rfw/example/hello/android/app/src/profile/AndroidManifest.xml b/packages/rfw/example/hello/android/app/src/profile/AndroidManifest.xml
new file mode 100644
index 0000000..91a24f1
--- /dev/null
+++ b/packages/rfw/example/hello/android/app/src/profile/AndroidManifest.xml
@@ -0,0 +1,7 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dev.flutter.rfw.examples.hello">
+ <!-- Flutter needs it to communicate with the running application
+ to allow setting breakpoints, to provide hot reload, etc.
+ -->
+ <uses-permission android:name="android.permission.INTERNET"/>
+</manifest>
diff --git a/packages/rfw/example/hello/android/build.gradle b/packages/rfw/example/hello/android/build.gradle
new file mode 100644
index 0000000..ed45c65
--- /dev/null
+++ b/packages/rfw/example/hello/android/build.gradle
@@ -0,0 +1,29 @@
+buildscript {
+ ext.kotlin_version = '1.3.50'
+ repositories {
+ google()
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:4.1.0'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.buildDir = '../build'
+subprojects {
+ project.buildDir = "${rootProject.buildDir}/${project.name}"
+ project.evaluationDependsOn(':app')
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/packages/rfw/example/hello/android/gradle.properties b/packages/rfw/example/hello/android/gradle.properties
new file mode 100644
index 0000000..94adc3a
--- /dev/null
+++ b/packages/rfw/example/hello/android/gradle.properties
@@ -0,0 +1,3 @@
+org.gradle.jvmargs=-Xmx1536M
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/packages/rfw/example/hello/android/gradle/wrapper/gradle-wrapper.properties b/packages/rfw/example/hello/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..bc6a58a
--- /dev/null
+++ b/packages/rfw/example/hello/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-6.7-all.zip
diff --git a/packages/rfw/example/hello/android/settings.gradle b/packages/rfw/example/hello/android/settings.gradle
new file mode 100644
index 0000000..44e62bc
--- /dev/null
+++ b/packages/rfw/example/hello/android/settings.gradle
@@ -0,0 +1,11 @@
+include ':app'
+
+def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
+def properties = new Properties()
+
+assert localPropertiesFile.exists()
+localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
+
+def flutterSdkPath = properties.getProperty("flutter.sdk")
+assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
diff --git a/packages/rfw/example/hello/ios/.gitignore b/packages/rfw/example/hello/ios/.gitignore
new file mode 100644
index 0000000..151026b
--- /dev/null
+++ b/packages/rfw/example/hello/ios/.gitignore
@@ -0,0 +1,33 @@
+*.mode1v3
+*.mode2v3
+*.moved-aside
+*.pbxuser
+*.perspectivev3
+**/*sync/
+.sconsign.dblite
+.tags*
+**/.vagrant/
+**/DerivedData/
+Icon?
+**/Pods/
+**/.symlinks/
+profile
+xcuserdata
+**/.generated/
+Flutter/App.framework
+Flutter/Flutter.framework
+Flutter/Flutter.podspec
+Flutter/Generated.xcconfig
+Flutter/ephemeral/
+Flutter/app.flx
+Flutter/app.zip
+Flutter/flutter_assets/
+Flutter/flutter_export_environment.sh
+ServiceDefinitions.json
+Runner/GeneratedPluginRegistrant.*
+
+# Exceptions to above rules.
+!default.mode1v3
+!default.mode2v3
+!default.pbxuser
+!default.perspectivev3
diff --git a/packages/rfw/example/hello/ios/Flutter/AppFrameworkInfo.plist b/packages/rfw/example/hello/ios/Flutter/AppFrameworkInfo.plist
new file mode 100644
index 0000000..8d4492f
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Flutter/AppFrameworkInfo.plist
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleExecutable</key>
+ <string>App</string>
+ <key>CFBundleIdentifier</key>
+ <string>io.flutter.flutter.app</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>App</string>
+ <key>CFBundlePackageType</key>
+ <string>FMWK</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>MinimumOSVersion</key>
+ <string>9.0</string>
+</dict>
+</plist>
diff --git a/packages/rfw/example/hello/ios/Flutter/Debug.xcconfig b/packages/rfw/example/hello/ios/Flutter/Debug.xcconfig
new file mode 100644
index 0000000..592ceee
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Flutter/Debug.xcconfig
@@ -0,0 +1 @@
+#include "Generated.xcconfig"
diff --git a/packages/rfw/example/hello/ios/Flutter/Release.xcconfig b/packages/rfw/example/hello/ios/Flutter/Release.xcconfig
new file mode 100644
index 0000000..592ceee
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Flutter/Release.xcconfig
@@ -0,0 +1 @@
+#include "Generated.xcconfig"
diff --git a/packages/rfw/example/hello/ios/Runner.xcodeproj/project.pbxproj b/packages/rfw/example/hello/ios/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..e77cf43
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,481 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 50;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
+ 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
+ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 97C146EB1CF9000F007C117D /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 9740EEB11CF90186004384FC /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */,
+ );
+ name = Flutter;
+ sourceTree = "<group>";
+ };
+ 97C146E51CF9000F007C117D = {
+ isa = PBXGroup;
+ children = (
+ 9740EEB11CF90186004384FC /* Flutter */,
+ 97C146F01CF9000F007C117D /* Runner */,
+ 97C146EF1CF9000F007C117D /* Products */,
+ );
+ sourceTree = "<group>";
+ };
+ 97C146EF1CF9000F007C117D /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146EE1CF9000F007C117D /* Runner.app */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 97C146F01CF9000F007C117D /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146FA1CF9000F007C117D /* Main.storyboard */,
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */,
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
+ 97C147021CF9000F007C117D /* Info.plist */,
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
+ );
+ path = Runner;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 97C146ED1CF9000F007C117D /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ 9740EEB61CF901F6004384FC /* Run Script */,
+ 97C146EA1CF9000F007C117D /* Sources */,
+ 97C146EB1CF9000F007C117D /* Frameworks */,
+ 97C146EC1CF9000F007C117D /* Resources */,
+ 9705A1C41CF9048500538489 /* Embed Frameworks */,
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = Runner;
+ productName = Runner;
+ productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 97C146E61CF9000F007C117D /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 1300;
+ ORGANIZATIONNAME = "";
+ TargetAttributes = {
+ 97C146ED1CF9000F007C117D = {
+ CreatedOnToolsVersion = 7.3.1;
+ LastSwiftMigration = 1100;
+ };
+ };
+ };
+ buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 97C146E51CF9000F007C117D;
+ productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 97C146ED1CF9000F007C117D /* Runner */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 97C146EC1CF9000F007C117D /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Thin Binary";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
+ };
+ 9740EEB61CF901F6004384FC /* Run Script */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Run Script";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 97C146EA1CF9000F007C117D /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C146FB1CF9000F007C117D /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "<group>";
+ };
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C147001CF9000F007C117D /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 249021D3217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Profile;
+ };
+ 249021D4217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.rfw.examples.hello;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Profile;
+ };
+ 97C147031CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 97C147041CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 97C147061CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.rfw.examples.hello;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Debug;
+ };
+ 97C147071CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.rfw.examples.hello;
+ 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/packages/rfw/example/hello/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/rfw/example/hello/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..919434a
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "self:">
+ </FileRef>
+</Workspace>
diff --git a/packages/rfw/example/hello/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/rfw/example/hello/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IDEDidComputeMac32BitWarning</key>
+ <true/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/hello/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/rfw/example/hello/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 0000000..f9b0d7c
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>PreviewsEnabled</key>
+ <false/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/hello/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/rfw/example/hello/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 0000000..c87d15a
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "1300"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+ BuildableName = "Runner.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+ BuildableName = "Runner.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <Testables>
+ </Testables>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+ BuildableName = "Runner.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Profile"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+ BuildableName = "Runner.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/packages/rfw/example/hello/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/rfw/example/hello/ios/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..1d526a1
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "group:Runner.xcodeproj">
+ </FileRef>
+</Workspace>
diff --git a/packages/rfw/example/hello/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/rfw/example/hello/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IDEDidComputeMac32BitWarning</key>
+ <true/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/hello/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/rfw/example/hello/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 0000000..f9b0d7c
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>PreviewsEnabled</key>
+ <false/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/hello/ios/Runner/AppDelegate.swift b/packages/rfw/example/hello/ios/Runner/AppDelegate.swift
new file mode 100644
index 0000000..caf9983
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Runner/AppDelegate.swift
@@ -0,0 +1,17 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+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/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..d36b1fa
--- /dev/null
+++ b/packages/rfw/example/hello/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/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
new file mode 100644
index 0000000..dc9ada4
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
Binary files differ
diff --git a/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
new file mode 100644
index 0000000..28c6bf0
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
Binary files differ
diff --git a/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
new file mode 100644
index 0000000..2ccbfd9
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
Binary files differ
diff --git a/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
new file mode 100644
index 0000000..f091b6b
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
Binary files differ
diff --git a/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
new file mode 100644
index 0000000..4cde121
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
Binary files differ
diff --git a/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
new file mode 100644
index 0000000..d0ef06e
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
Binary files differ
diff --git a/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
new file mode 100644
index 0000000..dcdc230
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
Binary files differ
diff --git a/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
new file mode 100644
index 0000000..2ccbfd9
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
Binary files differ
diff --git a/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
new file mode 100644
index 0000000..c8f9ed8
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
Binary files differ
diff --git a/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
new file mode 100644
index 0000000..a6d6b86
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
Binary files differ
diff --git a/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
new file mode 100644
index 0000000..a6d6b86
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
Binary files differ
diff --git a/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
new file mode 100644
index 0000000..75b2d16
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
Binary files differ
diff --git a/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
new file mode 100644
index 0000000..c4df70d
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
Binary files differ
diff --git a/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
new file mode 100644
index 0000000..6a84f41
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
Binary files differ
diff --git a/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
new file mode 100644
index 0000000..d0e1f58
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
Binary files differ
diff --git a/packages/rfw/example/hello/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
new file mode 100644
index 0000000..0bedcf2
--- /dev/null
+++ b/packages/rfw/example/hello/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/packages/rfw/example/hello/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
new file mode 100644
index 0000000..9da19ea
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
Binary files differ
diff --git a/packages/rfw/example/hello/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
new file mode 100644
index 0000000..9da19ea
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
Binary files differ
diff --git a/packages/rfw/example/hello/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
new file mode 100644
index 0000000..9da19ea
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
Binary files differ
diff --git a/packages/rfw/example/hello/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/packages/rfw/example/hello/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
new file mode 100644
index 0000000..89c2725
--- /dev/null
+++ b/packages/rfw/example/hello/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/packages/rfw/example/hello/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/rfw/example/hello/ios/Runner/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000..f2e259c
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
+ <dependencies>
+ <deployment identifier="iOS"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
+ </dependencies>
+ <scenes>
+ <!--View Controller-->
+ <scene sceneID="EHf-IW-A2E">
+ <objects>
+ <viewController id="01J-lp-oVM" sceneMemberID="viewController">
+ <layoutGuides>
+ <viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
+ <viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
+ </layoutGuides>
+ <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <subviews>
+ <imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
+ </imageView>
+ </subviews>
+ <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+ <constraints>
+ <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
+ <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
+ </constraints>
+ </view>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="53" y="375"/>
+ </scene>
+ </scenes>
+ <resources>
+ <image name="LaunchImage" width="168" height="185"/>
+ </resources>
+</document>
diff --git a/packages/rfw/example/hello/ios/Runner/Base.lproj/Main.storyboard b/packages/rfw/example/hello/ios/Runner/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..f3c2851
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Runner/Base.lproj/Main.storyboard
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
+ <dependencies>
+ <deployment identifier="iOS"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
+ </dependencies>
+ <scenes>
+ <!--Flutter View Controller-->
+ <scene sceneID="tne-QT-ifu">
+ <objects>
+ <viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
+ <layoutGuides>
+ <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
+ <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
+ </layoutGuides>
+ <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
+ <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+ </view>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
+ </objects>
+ </scene>
+ </scenes>
+</document>
diff --git a/packages/rfw/example/hello/ios/Runner/Info.plist b/packages/rfw/example/hello/ios/Runner/Info.plist
new file mode 100644
index 0000000..cd36e6d
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Runner/Info.plist
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>$(DEVELOPMENT_LANGUAGE)</string>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>hello</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>$(FLUTTER_BUILD_NAME)</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>$(FLUTTER_BUILD_NUMBER)</string>
+ <key>LSRequiresIPhoneOS</key>
+ <true/>
+ <key>UILaunchStoryboardName</key>
+ <string>LaunchScreen</string>
+ <key>UIMainStoryboardFile</key>
+ <string>Main</string>
+ <key>UISupportedInterfaceOrientations</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+ <key>UISupportedInterfaceOrientations~ipad</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationPortraitUpsideDown</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+ <key>UIViewControllerBasedStatusBarAppearance</key>
+ <false/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/hello/ios/Runner/Runner-Bridging-Header.h b/packages/rfw/example/hello/ios/Runner/Runner-Bridging-Header.h
new file mode 100644
index 0000000..eb7e8ba
--- /dev/null
+++ b/packages/rfw/example/hello/ios/Runner/Runner-Bridging-Header.h
@@ -0,0 +1,5 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "GeneratedPluginRegistrant.h"
diff --git a/packages/rfw/example/hello/lib/main.dart b/packages/rfw/example/hello/lib/main.dart
new file mode 100644
index 0000000..dd035a0
--- /dev/null
+++ b/packages/rfw/example/hello/lib/main.dart
@@ -0,0 +1,75 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/widgets.dart';
+
+/// For clarity, this example uses the text representation of the sample remote
+/// widget library, and parses it locally. To do this, [parseLibraryFile] is
+/// used. In production, this is strongly discouraged since it is 10x slower
+/// than using [decodeLibraryBlob] to parse the binary version of the format.
+import 'package:rfw/formats.dart' show parseLibraryFile;
+
+import 'package:rfw/rfw.dart';
+
+void main() {
+ runApp(const Example());
+}
+
+class Example extends StatefulWidget {
+ const Example({Key? key}) : super(key: key);
+
+ @override
+ State<Example> createState() => _ExampleState();
+}
+
+class _ExampleState extends State<Example> {
+ final Runtime _runtime = Runtime();
+ final DynamicContent _data = DynamicContent();
+
+ // Normally this would be obtained dynamically, but for this example
+ // we hard-code the "remote" widgets into the app.
+ //
+ // Also, normally we would decode this with [decodeLibraryBlob] rather than
+ // parsing the text version using [parseLibraryFile]. However, to make it
+ // easier to demo, this uses the slower text format.
+ static final RemoteWidgetLibrary _remoteWidgets = parseLibraryFile('''
+ // The "import" keyword is used to specify dependencies, in this case,
+ // the built-in widgets that are added by initState below.
+ import core.widgets;
+ // The "widget" keyword is used to define a new widget constructor.
+ // The "root" widget is specified as the one to render in the build
+ // method below.
+ widget root = Container(
+ color: 0xFF002211,
+ child: Center(
+ child: Text(text: ["Hello, ", data.greet.name, "!"], textDirection: "ltr"),
+ ),
+ );
+ ''');
+
+ static const LibraryName coreName = LibraryName(<String>['core', 'widgets']);
+ static const LibraryName mainName = LibraryName(<String>['main']);
+
+ @override
+ void initState() {
+ super.initState();
+ _runtime.update(coreName, createCoreWidgets());
+ _runtime.update(mainName, _remoteWidgets);
+ _data.update('greet', <String, Object>{'name': 'World'});
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return RemoteWidget(
+ runtime: _runtime,
+ data: _data,
+ widget: const FullyQualifiedWidgetName(mainName, 'root'),
+ onEvent: (String name, DynamicMap arguments) {
+ // The example above does not have any way to trigger events, but if it
+ // did, they would result in this callback being invoked.
+ debugPrint('user triggered event "$name" with data: $arguments');
+ },
+ );
+ }
+}
diff --git a/packages/rfw/example/hello/linux/.gitignore b/packages/rfw/example/hello/linux/.gitignore
new file mode 100644
index 0000000..d3896c9
--- /dev/null
+++ b/packages/rfw/example/hello/linux/.gitignore
@@ -0,0 +1 @@
+flutter/ephemeral
diff --git a/packages/rfw/example/hello/linux/CMakeLists.txt b/packages/rfw/example/hello/linux/CMakeLists.txt
new file mode 100644
index 0000000..e298375
--- /dev/null
+++ b/packages/rfw/example/hello/linux/CMakeLists.txt
@@ -0,0 +1,116 @@
+cmake_minimum_required(VERSION 3.10)
+project(runner LANGUAGES CXX)
+
+set(BINARY_NAME "hello")
+set(APPLICATION_ID "dev.flutter.rfw.examples.hello")
+
+cmake_policy(SET CMP0063 NEW)
+
+set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
+
+# Root filesystem for cross-building.
+if(FLUTTER_TARGET_PLATFORM_SYSROOT)
+ set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
+ set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
+ set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
+ set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+ set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+endif()
+
+# Configure build options.
+if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+ set(CMAKE_BUILD_TYPE "Debug" CACHE
+ STRING "Flutter build mode" FORCE)
+ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
+ "Debug" "Profile" "Release")
+endif()
+
+# Compilation settings that should be applied to most targets.
+function(APPLY_STANDARD_SETTINGS TARGET)
+ target_compile_features(${TARGET} PUBLIC cxx_std_14)
+ target_compile_options(${TARGET} PRIVATE -Wall -Werror)
+ target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
+ target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
+endfunction()
+
+set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
+
+# Flutter library and tool build rules.
+add_subdirectory(${FLUTTER_MANAGED_DIR})
+
+# System-level dependencies.
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
+
+add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
+
+# Application build
+add_executable(${BINARY_NAME}
+ "main.cc"
+ "my_application.cc"
+ "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
+)
+apply_standard_settings(${BINARY_NAME})
+target_link_libraries(${BINARY_NAME} PRIVATE flutter)
+target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
+add_dependencies(${BINARY_NAME} flutter_assemble)
+# Only the install-generated bundle's copy of the executable will launch
+# correctly, since the resources must in the right relative locations. To avoid
+# people trying to run the unbundled copy, put it in a subdirectory instead of
+# the default top-level location.
+set_target_properties(${BINARY_NAME}
+ PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
+)
+
+# Generated plugin build rules, which manage building the plugins and adding
+# them to the application.
+include(flutter/generated_plugins.cmake)
+
+
+# === Installation ===
+# By default, "installing" just makes a relocatable bundle in the build
+# directory.
+set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
+if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+ set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
+endif()
+
+# Start with a clean build bundle directory every time.
+install(CODE "
+ file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
+ " COMPONENT Runtime)
+
+set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
+set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
+
+install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+
+if(PLUGIN_BUNDLED_LIBRARIES)
+ install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
+ DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endif()
+
+# Fully re-copy the assets directory on each build to avoid having stale files
+# from a previous install.
+set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
+install(CODE "
+ file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
+ " COMPONENT Runtime)
+install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
+ DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
+
+# Install the AOT library on non-Debug builds only.
+if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
+ install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endif()
diff --git a/packages/rfw/example/hello/linux/flutter/CMakeLists.txt b/packages/rfw/example/hello/linux/flutter/CMakeLists.txt
new file mode 100644
index 0000000..33fd580
--- /dev/null
+++ b/packages/rfw/example/hello/linux/flutter/CMakeLists.txt
@@ -0,0 +1,87 @@
+cmake_minimum_required(VERSION 3.10)
+
+set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
+
+# Configuration provided via flutter tool.
+include(${EPHEMERAL_DIR}/generated_config.cmake)
+
+# TODO: Move the rest of this into files in ephemeral. See
+# https://github.com/flutter/flutter/issues/57146.
+
+# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
+# which isn't available in 3.10.
+function(list_prepend LIST_NAME PREFIX)
+ set(NEW_LIST "")
+ foreach(element ${${LIST_NAME}})
+ list(APPEND NEW_LIST "${PREFIX}${element}")
+ endforeach(element)
+ set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
+endfunction()
+
+# === Flutter Library ===
+# System-level dependencies.
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
+pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
+pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
+
+set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
+
+# Published to parent scope for install step.
+set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
+set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
+set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
+set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
+
+list(APPEND FLUTTER_LIBRARY_HEADERS
+ "fl_basic_message_channel.h"
+ "fl_binary_codec.h"
+ "fl_binary_messenger.h"
+ "fl_dart_project.h"
+ "fl_engine.h"
+ "fl_json_message_codec.h"
+ "fl_json_method_codec.h"
+ "fl_message_codec.h"
+ "fl_method_call.h"
+ "fl_method_channel.h"
+ "fl_method_codec.h"
+ "fl_method_response.h"
+ "fl_plugin_registrar.h"
+ "fl_plugin_registry.h"
+ "fl_standard_message_codec.h"
+ "fl_standard_method_codec.h"
+ "fl_string_codec.h"
+ "fl_value.h"
+ "fl_view.h"
+ "flutter_linux.h"
+)
+list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
+add_library(flutter INTERFACE)
+target_include_directories(flutter INTERFACE
+ "${EPHEMERAL_DIR}"
+)
+target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
+target_link_libraries(flutter INTERFACE
+ PkgConfig::GTK
+ PkgConfig::GLIB
+ PkgConfig::GIO
+)
+add_dependencies(flutter flutter_assemble)
+
+# === Flutter tool backend ===
+# _phony_ is a non-existent file to force this command to run every time,
+# since currently there's no way to get a full input/output list from the
+# flutter tool.
+add_custom_command(
+ OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
+ ${CMAKE_CURRENT_BINARY_DIR}/_phony_
+ COMMAND ${CMAKE_COMMAND} -E env
+ ${FLUTTER_TOOL_ENVIRONMENT}
+ "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
+ ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}
+ VERBATIM
+)
+add_custom_target(flutter_assemble DEPENDS
+ "${FLUTTER_LIBRARY}"
+ ${FLUTTER_LIBRARY_HEADERS}
+)
diff --git a/packages/rfw/example/hello/linux/flutter/generated_plugins.cmake b/packages/rfw/example/hello/linux/flutter/generated_plugins.cmake
new file mode 100644
index 0000000..51436ae
--- /dev/null
+++ b/packages/rfw/example/hello/linux/flutter/generated_plugins.cmake
@@ -0,0 +1,15 @@
+#
+# Generated file, do not edit.
+#
+
+list(APPEND FLUTTER_PLUGIN_LIST
+)
+
+set(PLUGIN_BUNDLED_LIBRARIES)
+
+foreach(plugin ${FLUTTER_PLUGIN_LIST})
+ add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
+ target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
+endforeach(plugin)
diff --git a/packages/rfw/example/hello/linux/main.cc b/packages/rfw/example/hello/linux/main.cc
new file mode 100644
index 0000000..1507d02
--- /dev/null
+++ b/packages/rfw/example/hello/linux/main.cc
@@ -0,0 +1,10 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "my_application.h"
+
+int main(int argc, char** argv) {
+ g_autoptr(MyApplication) app = my_application_new();
+ return g_application_run(G_APPLICATION(app), argc, argv);
+}
diff --git a/packages/rfw/example/hello/linux/my_application.cc b/packages/rfw/example/hello/linux/my_application.cc
new file mode 100644
index 0000000..f33831f
--- /dev/null
+++ b/packages/rfw/example/hello/linux/my_application.cc
@@ -0,0 +1,111 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "my_application.h"
+
+#include <flutter_linux/flutter_linux.h>
+#ifdef GDK_WINDOWING_X11
+#include <gdk/gdkx.h>
+#endif
+
+#include "flutter/generated_plugin_registrant.h"
+
+struct _MyApplication {
+ GtkApplication parent_instance;
+ char** dart_entrypoint_arguments;
+};
+
+G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
+
+// Implements GApplication::activate.
+static void my_application_activate(GApplication* application) {
+ MyApplication* self = MY_APPLICATION(application);
+ GtkWindow* window =
+ GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
+
+ // Use a header bar when running in GNOME as this is the common style used
+ // by applications and is the setup most users will be using (e.g. Ubuntu
+ // desktop).
+ // If running on X and not using GNOME then just use a traditional title bar
+ // in case the window manager does more exotic layout, e.g. tiling.
+ // If running on Wayland assume the header bar will work (may need changing
+ // if future cases occur).
+ gboolean use_header_bar = TRUE;
+#ifdef GDK_WINDOWING_X11
+ GdkScreen* screen = gtk_window_get_screen(window);
+ if (GDK_IS_X11_SCREEN(screen)) {
+ const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
+ if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
+ use_header_bar = FALSE;
+ }
+ }
+#endif
+ if (use_header_bar) {
+ GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
+ gtk_widget_show(GTK_WIDGET(header_bar));
+ gtk_header_bar_set_title(header_bar, "hello");
+ gtk_header_bar_set_show_close_button(header_bar, TRUE);
+ gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
+ } else {
+ gtk_window_set_title(window, "hello");
+ }
+
+ gtk_window_set_default_size(window, 1280, 720);
+ gtk_widget_show(GTK_WIDGET(window));
+
+ g_autoptr(FlDartProject) project = fl_dart_project_new();
+ fl_dart_project_set_dart_entrypoint_arguments(
+ project, self->dart_entrypoint_arguments);
+
+ FlView* view = fl_view_new(project);
+ gtk_widget_show(GTK_WIDGET(view));
+ gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
+
+ fl_register_plugins(FL_PLUGIN_REGISTRY(view));
+
+ gtk_widget_grab_focus(GTK_WIDGET(view));
+}
+
+// Implements GApplication::local_command_line.
+static gboolean my_application_local_command_line(GApplication* application,
+ gchar*** arguments,
+ int* exit_status) {
+ MyApplication* self = MY_APPLICATION(application);
+ // Strip out the first argument as it is the binary name.
+ self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
+
+ g_autoptr(GError) error = nullptr;
+ if (!g_application_register(application, nullptr, &error)) {
+ g_warning("Failed to register: %s", error->message);
+ *exit_status = 1;
+ return TRUE;
+ }
+
+ g_application_activate(application);
+ *exit_status = 0;
+
+ return TRUE;
+}
+
+// Implements GObject::dispose.
+static void my_application_dispose(GObject* object) {
+ MyApplication* self = MY_APPLICATION(object);
+ g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
+ G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
+}
+
+static void my_application_class_init(MyApplicationClass* klass) {
+ G_APPLICATION_CLASS(klass)->activate = my_application_activate;
+ G_APPLICATION_CLASS(klass)->local_command_line =
+ my_application_local_command_line;
+ G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
+}
+
+static void my_application_init(MyApplication* self) {}
+
+MyApplication* my_application_new() {
+ return MY_APPLICATION(g_object_new(my_application_get_type(),
+ "application-id", APPLICATION_ID, "flags",
+ G_APPLICATION_NON_UNIQUE, nullptr));
+}
diff --git a/packages/rfw/example/hello/linux/my_application.h b/packages/rfw/example/hello/linux/my_application.h
new file mode 100644
index 0000000..6e9f0c3
--- /dev/null
+++ b/packages/rfw/example/hello/linux/my_application.h
@@ -0,0 +1,22 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_MY_APPLICATION_H_
+#define FLUTTER_MY_APPLICATION_H_
+
+#include <gtk/gtk.h>
+
+G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
+ GtkApplication)
+
+/**
+ * my_application_new:
+ *
+ * Creates a new Flutter-based application.
+ *
+ * Returns: a new #MyApplication.
+ */
+MyApplication* my_application_new();
+
+#endif // FLUTTER_MY_APPLICATION_H_
diff --git a/packages/rfw/example/hello/macos/.gitignore b/packages/rfw/example/hello/macos/.gitignore
new file mode 100644
index 0000000..d2fd377
--- /dev/null
+++ b/packages/rfw/example/hello/macos/.gitignore
@@ -0,0 +1,6 @@
+# Flutter-related
+**/Flutter/ephemeral/
+**/Pods/
+
+# Xcode-related
+**/xcuserdata/
diff --git a/packages/rfw/example/hello/macos/Flutter/Flutter-Debug.xcconfig b/packages/rfw/example/hello/macos/Flutter/Flutter-Debug.xcconfig
new file mode 100644
index 0000000..c2efd0b
--- /dev/null
+++ b/packages/rfw/example/hello/macos/Flutter/Flutter-Debug.xcconfig
@@ -0,0 +1 @@
+#include "ephemeral/Flutter-Generated.xcconfig"
diff --git a/packages/rfw/example/hello/macos/Flutter/Flutter-Release.xcconfig b/packages/rfw/example/hello/macos/Flutter/Flutter-Release.xcconfig
new file mode 100644
index 0000000..c2efd0b
--- /dev/null
+++ b/packages/rfw/example/hello/macos/Flutter/Flutter-Release.xcconfig
@@ -0,0 +1 @@
+#include "ephemeral/Flutter-Generated.xcconfig"
diff --git a/packages/rfw/example/hello/macos/Runner.xcodeproj/project.pbxproj b/packages/rfw/example/hello/macos/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..e3be739
--- /dev/null
+++ b/packages/rfw/example/hello/macos/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,572 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 51;
+ objects = {
+
+/* Begin PBXAggregateTarget section */
+ 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */;
+ buildPhases = (
+ 33CC111E2044C6BF0003C045 /* ShellScript */,
+ );
+ dependencies = (
+ );
+ name = "Flutter Assemble";
+ productName = FLX;
+ };
+/* End PBXAggregateTarget section */
+
+/* Begin PBXBuildFile section */
+ 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
+ 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
+ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
+ 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
+ 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 33CC111A2044C6BA0003C045;
+ remoteInfo = FLX;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 33CC110E2044A8840003C045 /* Bundle Framework */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Bundle Framework";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
+ 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
+ 33CC10ED2044A3C60003C045 /* hello.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "hello.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
+ 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
+ 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
+ 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = "<group>"; };
+ 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = "<group>"; };
+ 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = "<group>"; };
+ 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = "<group>"; };
+ 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = "<group>"; };
+ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
+ 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
+ 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 33CC10EA2044A3C60003C045 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 33BA886A226E78AF003329D5 /* Configs */ = {
+ isa = PBXGroup;
+ children = (
+ 33E5194F232828860026EE4D /* AppInfo.xcconfig */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 333000ED22D3DE5D00554162 /* Warnings.xcconfig */,
+ );
+ path = Configs;
+ sourceTree = "<group>";
+ };
+ 33CC10E42044A3C60003C045 = {
+ isa = PBXGroup;
+ children = (
+ 33FAB671232836740065AC1E /* Runner */,
+ 33CEB47122A05771004F2AC0 /* Flutter */,
+ 33CC10EE2044A3C60003C045 /* Products */,
+ D73912EC22F37F3D000D13A0 /* Frameworks */,
+ );
+ sourceTree = "<group>";
+ };
+ 33CC10EE2044A3C60003C045 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10ED2044A3C60003C045 /* hello.app */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 33CC11242044D66E0003C045 /* Resources */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10F22044A3C60003C045 /* Assets.xcassets */,
+ 33CC10F42044A3C60003C045 /* MainMenu.xib */,
+ 33CC10F72044A3C60003C045 /* Info.plist */,
+ );
+ name = Resources;
+ path = ..;
+ sourceTree = "<group>";
+ };
+ 33CEB47122A05771004F2AC0 /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,
+ 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,
+ 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,
+ 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,
+ );
+ path = Flutter;
+ sourceTree = "<group>";
+ };
+ 33FAB671232836740065AC1E /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10F02044A3C60003C045 /* AppDelegate.swift */,
+ 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,
+ 33E51913231747F40026EE4D /* DebugProfile.entitlements */,
+ 33E51914231749380026EE4D /* Release.entitlements */,
+ 33CC11242044D66E0003C045 /* Resources */,
+ 33BA886A226E78AF003329D5 /* Configs */,
+ );
+ path = Runner;
+ sourceTree = "<group>";
+ };
+ D73912EC22F37F3D000D13A0 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 33CC10EC2044A3C60003C045 /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ 33CC10E92044A3C60003C045 /* Sources */,
+ 33CC10EA2044A3C60003C045 /* Frameworks */,
+ 33CC10EB2044A3C60003C045 /* Resources */,
+ 33CC110E2044A8840003C045 /* Bundle Framework */,
+ 3399D490228B24CF009A79C7 /* ShellScript */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 33CC11202044C79F0003C045 /* PBXTargetDependency */,
+ );
+ name = Runner;
+ productName = Runner;
+ productReference = 33CC10ED2044A3C60003C045 /* hello.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 33CC10E52044A3C60003C045 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastSwiftUpdateCheck = 0920;
+ LastUpgradeCheck = 1300;
+ ORGANIZATIONNAME = "";
+ TargetAttributes = {
+ 33CC10EC2044A3C60003C045 = {
+ CreatedOnToolsVersion = 9.2;
+ LastSwiftMigration = 1100;
+ ProvisioningStyle = Automatic;
+ SystemCapabilities = {
+ com.apple.Sandbox = {
+ enabled = 1;
+ };
+ };
+ };
+ 33CC111A2044C6BA0003C045 = {
+ CreatedOnToolsVersion = 9.2;
+ ProvisioningStyle = Manual;
+ };
+ };
+ };
+ buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 33CC10E42044A3C60003C045;
+ productRefGroup = 33CC10EE2044A3C60003C045 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 33CC10EC2044A3C60003C045 /* Runner */,
+ 33CC111A2044C6BA0003C045 /* Flutter Assemble */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 33CC10EB2044A3C60003C045 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,
+ 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 3399D490228B24CF009A79C7 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ );
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n";
+ };
+ 33CC111E2044C6BF0003C045 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ Flutter/ephemeral/FlutterInputs.xcfilelist,
+ );
+ inputPaths = (
+ Flutter/ephemeral/tripwire,
+ );
+ outputFileListPaths = (
+ Flutter/ephemeral/FlutterOutputs.xcfilelist,
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 33CC10E92044A3C60003C045 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,
+ 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,
+ 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 33CC11202044C79F0003C045 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;
+ targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+ 33CC10F42044A3C60003C045 /* MainMenu.xib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 33CC10F52044A3C60003C045 /* Base */,
+ );
+ name = MainMenu.xib;
+ path = Runner;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 338D0CE9231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = macosx;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ };
+ name = Profile;
+ };
+ 338D0CEA231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Profile;
+ };
+ 338D0CEB231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Manual;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Profile;
+ };
+ 33CC10F92044A3C60003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ 33CC10FA2044A3C60003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = macosx;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ };
+ name = Release;
+ };
+ 33CC10FC2044A3C60003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Debug;
+ };
+ 33CC10FD2044A3C60003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Release;
+ };
+ 33CC111C2044C6BA0003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Manual;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ 33CC111D2044C6BA0003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Automatic;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC10F92044A3C60003C045 /* Debug */,
+ 33CC10FA2044A3C60003C045 /* Release */,
+ 338D0CE9231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC10FC2044A3C60003C045 /* Debug */,
+ 33CC10FD2044A3C60003C045 /* Release */,
+ 338D0CEA231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC111C2044C6BA0003C045 /* Debug */,
+ 33CC111D2044C6BA0003C045 /* Release */,
+ 338D0CEB231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 33CC10E52044A3C60003C045 /* Project object */;
+}
diff --git a/packages/rfw/example/hello/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/rfw/example/hello/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/packages/rfw/example/hello/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IDEDidComputeMac32BitWarning</key>
+ <true/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/hello/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/rfw/example/hello/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 0000000..8347df2
--- /dev/null
+++ b/packages/rfw/example/hello/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "1300"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "33CC10EC2044A3C60003C045"
+ BuildableName = "hello.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "33CC10EC2044A3C60003C045"
+ BuildableName = "hello.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <Testables>
+ </Testables>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "33CC10EC2044A3C60003C045"
+ BuildableName = "hello.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Profile"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "33CC10EC2044A3C60003C045"
+ BuildableName = "hello.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/packages/rfw/example/hello/macos/Runner.xcworkspace/contents.xcworkspacedata b/packages/rfw/example/hello/macos/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..1d526a1
--- /dev/null
+++ b/packages/rfw/example/hello/macos/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "group:Runner.xcodeproj">
+ </FileRef>
+</Workspace>
diff --git a/packages/rfw/example/hello/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/rfw/example/hello/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/packages/rfw/example/hello/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IDEDidComputeMac32BitWarning</key>
+ <true/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/hello/macos/Runner/AppDelegate.swift b/packages/rfw/example/hello/macos/Runner/AppDelegate.swift
new file mode 100644
index 0000000..5cec4c4
--- /dev/null
+++ b/packages/rfw/example/hello/macos/Runner/AppDelegate.swift
@@ -0,0 +1,13 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import Cocoa
+import FlutterMacOS
+
+@NSApplicationMain
+class AppDelegate: FlutterAppDelegate {
+ override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
+ return true
+ }
+}
diff --git a/packages/rfw/example/hello/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/rfw/example/hello/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..a2ec33f
--- /dev/null
+++ b/packages/rfw/example/hello/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,68 @@
+{
+ "images" : [
+ {
+ "size" : "16x16",
+ "idiom" : "mac",
+ "filename" : "app_icon_16.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "16x16",
+ "idiom" : "mac",
+ "filename" : "app_icon_32.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "32x32",
+ "idiom" : "mac",
+ "filename" : "app_icon_32.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "32x32",
+ "idiom" : "mac",
+ "filename" : "app_icon_64.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "128x128",
+ "idiom" : "mac",
+ "filename" : "app_icon_128.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "128x128",
+ "idiom" : "mac",
+ "filename" : "app_icon_256.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "256x256",
+ "idiom" : "mac",
+ "filename" : "app_icon_256.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "256x256",
+ "idiom" : "mac",
+ "filename" : "app_icon_512.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "app_icon_512.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "app_icon_1024.png",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/packages/rfw/example/hello/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/packages/rfw/example/hello/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
new file mode 100644
index 0000000..3c4935a
--- /dev/null
+++ b/packages/rfw/example/hello/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
Binary files differ
diff --git a/packages/rfw/example/hello/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/packages/rfw/example/hello/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png
new file mode 100644
index 0000000..ed4cc16
--- /dev/null
+++ b/packages/rfw/example/hello/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png
Binary files differ
diff --git a/packages/rfw/example/hello/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/packages/rfw/example/hello/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png
new file mode 100644
index 0000000..483be61
--- /dev/null
+++ b/packages/rfw/example/hello/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png
Binary files differ
diff --git a/packages/rfw/example/hello/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/packages/rfw/example/hello/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png
new file mode 100644
index 0000000..bcbf36d
--- /dev/null
+++ b/packages/rfw/example/hello/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png
Binary files differ
diff --git a/packages/rfw/example/hello/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/packages/rfw/example/hello/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png
new file mode 100644
index 0000000..9c0a652
--- /dev/null
+++ b/packages/rfw/example/hello/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png
Binary files differ
diff --git a/packages/rfw/example/hello/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/packages/rfw/example/hello/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png
new file mode 100644
index 0000000..e71a726
--- /dev/null
+++ b/packages/rfw/example/hello/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png
Binary files differ
diff --git a/packages/rfw/example/hello/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/packages/rfw/example/hello/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png
new file mode 100644
index 0000000..8a31fe2
--- /dev/null
+++ b/packages/rfw/example/hello/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png
Binary files differ
diff --git a/packages/rfw/example/hello/macos/Runner/Base.lproj/MainMenu.xib b/packages/rfw/example/hello/macos/Runner/Base.lproj/MainMenu.xib
new file mode 100644
index 0000000..537341a
--- /dev/null
+++ b/packages/rfw/example/hello/macos/Runner/Base.lproj/MainMenu.xib
@@ -0,0 +1,339 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+ <dependencies>
+ <deployment identifier="macosx"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+ </dependencies>
+ <objects>
+ <customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
+ <connections>
+ <outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
+ </connections>
+ </customObject>
+ <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+ <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+ <customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="Runner" customModuleProvider="target">
+ <connections>
+ <outlet property="applicationMenu" destination="uQy-DD-JDr" id="XBo-yE-nKs"/>
+ <outlet property="mainFlutterWindow" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/>
+ </connections>
+ </customObject>
+ <customObject id="YLy-65-1bz" customClass="NSFontManager"/>
+ <menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
+ <items>
+ <menuItem title="APP_NAME" id="1Xt-HY-uBw">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="APP_NAME" systemMenu="apple" id="uQy-DD-JDr">
+ <items>
+ <menuItem title="About APP_NAME" id="5kV-Vb-QxS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
+ <menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
+ <menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
+ <menuItem title="Services" id="NMo-om-nkz">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
+ <menuItem title="Hide APP_NAME" keyEquivalent="h" id="Olw-nP-bQN">
+ <connections>
+ <action selector="hide:" target="-1" id="PnN-Uc-m68"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Show All" id="Kd2-mp-pUS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
+ <menuItem title="Quit APP_NAME" keyEquivalent="q" id="4sb-4s-VLi">
+ <connections>
+ <action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Edit" id="5QF-Oa-p0T">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Edit" id="W48-6f-4Dl">
+ <items>
+ <menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
+ <connections>
+ <action selector="undo:" target="-1" id="M6e-cu-g7V"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
+ <connections>
+ <action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
+ <menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
+ <connections>
+ <action selector="cut:" target="-1" id="YJe-68-I9s"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
+ <connections>
+ <action selector="copy:" target="-1" id="G1f-GL-Joy"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
+ <connections>
+ <action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Delete" id="pa3-QI-u2k">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
+ <connections>
+ <action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
+ <menuItem title="Find" id="4EN-yA-p0u">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Find" id="1b7-l0-nxx">
+ <items>
+ <menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
+ <connections>
+ <action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
+ <connections>
+ <action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
+ <connections>
+ <action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
+ <connections>
+ <action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
+ <connections>
+ <action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
+ <items>
+ <menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
+ <connections>
+ <action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
+ <connections>
+ <action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
+ <menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Substitutions" id="9ic-FL-obx">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
+ <items>
+ <menuItem title="Show Substitutions" id="z6F-FW-3nz">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
+ <menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smart Quotes" id="hQb-2v-fYv">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smart Dashes" id="rgM-f4-ycn">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smart Links" id="cwL-P1-jid">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Data Detectors" id="tRr-pd-1PS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Text Replacement" id="HFQ-gK-NFA">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Transformations" id="2oI-Rn-ZJC">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Transformations" id="c8a-y6-VQd">
+ <items>
+ <menuItem title="Make Upper Case" id="vmV-6d-7jI">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Make Lower Case" id="d9M-CD-aMd">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Capitalize" id="UEZ-Bs-lqG">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Speech" id="xrE-MZ-jX0">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Speech" id="3rS-ZA-NoH">
+ <items>
+ <menuItem title="Start Speaking" id="Ynk-f8-cLZ">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Stop Speaking" id="Oyz-dy-DGm">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="View" id="H8h-7b-M4v">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="View" id="HyV-fh-RgO">
+ <items>
+ <menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
+ <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+ <connections>
+ <action selector="toggleFullScreen:" target="-1" id="dU3-MA-1Rq"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Window" id="aUF-d1-5bR">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
+ <items>
+ <menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
+ <connections>
+ <action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Zoom" id="R4o-n2-Eq4">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
+ <menuItem title="Bring All to Front" id="LE2-aR-0XJ">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ </items>
+ <point key="canvasLocation" x="142" y="-258"/>
+ </menu>
+ <window title="APP_NAME" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g" customClass="MainFlutterWindow" customModule="Runner" customModuleProvider="target">
+ <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
+ <rect key="contentRect" x="335" y="390" width="800" height="600"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="2560" height="1577"/>
+ <view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
+ <rect key="frame" x="0.0" y="0.0" width="800" height="600"/>
+ <autoresizingMask key="autoresizingMask"/>
+ </view>
+ </window>
+ </objects>
+</document>
diff --git a/packages/rfw/example/hello/macos/Runner/Configs/AppInfo.xcconfig b/packages/rfw/example/hello/macos/Runner/Configs/AppInfo.xcconfig
new file mode 100644
index 0000000..549e362
--- /dev/null
+++ b/packages/rfw/example/hello/macos/Runner/Configs/AppInfo.xcconfig
@@ -0,0 +1,14 @@
+// Application-level settings for the Runner target.
+//
+// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the
+// future. If not, the values below would default to using the project name when this becomes a
+// 'flutter create' template.
+
+// The application's name. By default this is also the title of the Flutter window.
+PRODUCT_NAME = hello
+
+// The application's bundle identifier
+PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.rfw.examples.hello
+
+// The copyright displayed in application information
+PRODUCT_COPYRIGHT = Copyright © 2021 dev.flutter.rfw.examples. All rights reserved.
diff --git a/packages/rfw/example/hello/macos/Runner/Configs/Debug.xcconfig b/packages/rfw/example/hello/macos/Runner/Configs/Debug.xcconfig
new file mode 100644
index 0000000..36b0fd9
--- /dev/null
+++ b/packages/rfw/example/hello/macos/Runner/Configs/Debug.xcconfig
@@ -0,0 +1,2 @@
+#include "../../Flutter/Flutter-Debug.xcconfig"
+#include "Warnings.xcconfig"
diff --git a/packages/rfw/example/hello/macos/Runner/Configs/Release.xcconfig b/packages/rfw/example/hello/macos/Runner/Configs/Release.xcconfig
new file mode 100644
index 0000000..dff4f49
--- /dev/null
+++ b/packages/rfw/example/hello/macos/Runner/Configs/Release.xcconfig
@@ -0,0 +1,2 @@
+#include "../../Flutter/Flutter-Release.xcconfig"
+#include "Warnings.xcconfig"
diff --git a/packages/rfw/example/hello/macos/Runner/Configs/Warnings.xcconfig b/packages/rfw/example/hello/macos/Runner/Configs/Warnings.xcconfig
new file mode 100644
index 0000000..42bcbf4
--- /dev/null
+++ b/packages/rfw/example/hello/macos/Runner/Configs/Warnings.xcconfig
@@ -0,0 +1,13 @@
+WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings
+GCC_WARN_UNDECLARED_SELECTOR = YES
+CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES
+CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
+CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
+CLANG_WARN_PRAGMA_PACK = YES
+CLANG_WARN_STRICT_PROTOTYPES = YES
+CLANG_WARN_COMMA = YES
+GCC_WARN_STRICT_SELECTOR_MATCH = YES
+CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES
+CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
+GCC_WARN_SHADOW = YES
+CLANG_WARN_UNREACHABLE_CODE = YES
diff --git a/packages/rfw/example/hello/macos/Runner/DebugProfile.entitlements b/packages/rfw/example/hello/macos/Runner/DebugProfile.entitlements
new file mode 100644
index 0000000..dddb8a3
--- /dev/null
+++ b/packages/rfw/example/hello/macos/Runner/DebugProfile.entitlements
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.security.app-sandbox</key>
+ <true/>
+ <key>com.apple.security.cs.allow-jit</key>
+ <true/>
+ <key>com.apple.security.network.server</key>
+ <true/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/hello/macos/Runner/Info.plist b/packages/rfw/example/hello/macos/Runner/Info.plist
new file mode 100644
index 0000000..4789daa
--- /dev/null
+++ b/packages/rfw/example/hello/macos/Runner/Info.plist
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>$(DEVELOPMENT_LANGUAGE)</string>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>$(PRODUCT_NAME)</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>$(FLUTTER_BUILD_NAME)</string>
+ <key>CFBundleVersion</key>
+ <string>$(FLUTTER_BUILD_NUMBER)</string>
+ <key>LSMinimumSystemVersion</key>
+ <string>$(MACOSX_DEPLOYMENT_TARGET)</string>
+ <key>NSHumanReadableCopyright</key>
+ <string>$(PRODUCT_COPYRIGHT)</string>
+ <key>NSMainNibFile</key>
+ <string>MainMenu</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+</dict>
+</plist>
diff --git a/packages/rfw/example/hello/macos/Runner/MainFlutterWindow.swift b/packages/rfw/example/hello/macos/Runner/MainFlutterWindow.swift
new file mode 100644
index 0000000..32aaeed
--- /dev/null
+++ b/packages/rfw/example/hello/macos/Runner/MainFlutterWindow.swift
@@ -0,0 +1,19 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import Cocoa
+import FlutterMacOS
+
+class MainFlutterWindow: NSWindow {
+ override func awakeFromNib() {
+ let flutterViewController = FlutterViewController.init()
+ let windowFrame = self.frame
+ self.contentViewController = flutterViewController
+ self.setFrame(windowFrame, display: true)
+
+ RegisterGeneratedPlugins(registry: flutterViewController)
+
+ super.awakeFromNib()
+ }
+}
diff --git a/packages/rfw/example/hello/macos/Runner/Release.entitlements b/packages/rfw/example/hello/macos/Runner/Release.entitlements
new file mode 100644
index 0000000..852fa1a
--- /dev/null
+++ b/packages/rfw/example/hello/macos/Runner/Release.entitlements
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.security.app-sandbox</key>
+ <true/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/hello/pubspec.yaml b/packages/rfw/example/hello/pubspec.yaml
new file mode 100644
index 0000000..5bb2930
--- /dev/null
+++ b/packages/rfw/example/hello/pubspec.yaml
@@ -0,0 +1,13 @@
+name: hello
+description: Hello World example for RFW
+publish_to: 'none' # Remove this line if you wish to publish to pub.dev
+version: 1.0.0+1
+
+environment:
+ sdk: ">=2.13.0 <3.0.0"
+
+dependencies:
+ flutter:
+ sdk: flutter
+ rfw:
+ path: ../../
diff --git a/packages/rfw/example/hello/web/favicon.png b/packages/rfw/example/hello/web/favicon.png
new file mode 100644
index 0000000..8aaa46a
--- /dev/null
+++ b/packages/rfw/example/hello/web/favicon.png
Binary files differ
diff --git a/packages/rfw/example/hello/web/icons/Icon-192.png b/packages/rfw/example/hello/web/icons/Icon-192.png
new file mode 100644
index 0000000..b749bfe
--- /dev/null
+++ b/packages/rfw/example/hello/web/icons/Icon-192.png
Binary files differ
diff --git a/packages/rfw/example/hello/web/icons/Icon-512.png b/packages/rfw/example/hello/web/icons/Icon-512.png
new file mode 100644
index 0000000..88cfd48
--- /dev/null
+++ b/packages/rfw/example/hello/web/icons/Icon-512.png
Binary files differ
diff --git a/packages/rfw/example/hello/web/icons/Icon-maskable-192.png b/packages/rfw/example/hello/web/icons/Icon-maskable-192.png
new file mode 100644
index 0000000..eb9b4d7
--- /dev/null
+++ b/packages/rfw/example/hello/web/icons/Icon-maskable-192.png
Binary files differ
diff --git a/packages/rfw/example/hello/web/icons/Icon-maskable-512.png b/packages/rfw/example/hello/web/icons/Icon-maskable-512.png
new file mode 100644
index 0000000..d69c566
--- /dev/null
+++ b/packages/rfw/example/hello/web/icons/Icon-maskable-512.png
Binary files differ
diff --git a/packages/rfw/example/hello/web/index.html b/packages/rfw/example/hello/web/index.html
new file mode 100644
index 0000000..f8a21fb
--- /dev/null
+++ b/packages/rfw/example/hello/web/index.html
@@ -0,0 +1,107 @@
+<!DOCTYPE html>
+<!-- Copyright 2013 The Flutter Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file. -->
+<html>
+<head>
+ <!--
+ If you are serving your web app in a path other than the root, change the
+ href value below to reflect the base path you are serving from.
+
+ The path provided below has to start and end with a slash "/" in order for
+ it to work correctly.
+
+ For more details:
+ * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
+
+ This is a placeholder for base href that will be replaced by the value of
+ the `--base-href` argument provided to `flutter build`.
+ -->
+ <base href="$FLUTTER_BASE_HREF">
+
+ <meta charset="UTF-8">
+ <meta content="IE=Edge" http-equiv="X-UA-Compatible">
+ <meta name="description" content="Hello World example for RFW">
+
+ <!-- iOS meta tags & icons -->
+ <meta name="apple-mobile-web-app-capable" content="yes">
+ <meta name="apple-mobile-web-app-status-bar-style" content="black">
+ <meta name="apple-mobile-web-app-title" content="hello">
+ <link rel="apple-touch-icon" href="icons/Icon-192.png">
+
+ <!-- Favicon -->
+ <link rel="icon" type="image/png" href="favicon.png"/>
+
+ <title>hello</title>
+ <link rel="manifest" href="manifest.json">
+</head>
+<body>
+ <!-- This script installs service_worker.js to provide PWA functionality to
+ application. For more information, see:
+ https://developers.google.com/web/fundamentals/primers/service-workers -->
+ <script>
+ var serviceWorkerVersion = null;
+ var scriptLoaded = false;
+ function loadMainDartJs() {
+ if (scriptLoaded) {
+ return;
+ }
+ scriptLoaded = true;
+ var scriptTag = document.createElement('script');
+ scriptTag.src = 'main.dart.js';
+ scriptTag.type = 'application/javascript';
+ document.body.append(scriptTag);
+ }
+
+ if ('serviceWorker' in navigator) {
+ // Service workers are supported. Use them.
+ window.addEventListener('load', function () {
+ // Wait for registration to finish before dropping the <script> tag.
+ // Otherwise, the browser will load the script multiple times,
+ // potentially different versions.
+ var serviceWorkerUrl = 'flutter_service_worker.js?v=' + serviceWorkerVersion;
+ navigator.serviceWorker.register(serviceWorkerUrl)
+ .then((reg) => {
+ function waitForActivation(serviceWorker) {
+ serviceWorker.addEventListener('statechange', () => {
+ if (serviceWorker.state == 'activated') {
+ console.log('Installed new service worker.');
+ loadMainDartJs();
+ }
+ });
+ }
+ if (!reg.active && (reg.installing || reg.waiting)) {
+ // No active web worker and we have installed or are installing
+ // one for the first time. Simply wait for it to activate.
+ waitForActivation(reg.installing || reg.waiting);
+ } else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {
+ // When the app updates the serviceWorkerVersion changes, so we
+ // need to ask the service worker to update.
+ console.log('New service worker available.');
+ reg.update();
+ waitForActivation(reg.installing);
+ } else {
+ // Existing service worker is still good.
+ console.log('Loading app from service worker.');
+ loadMainDartJs();
+ }
+ });
+
+ // If service worker doesn't succeed in a reasonable amount of time,
+ // fallback to plaint <script> tag.
+ setTimeout(() => {
+ if (!scriptLoaded) {
+ console.warn(
+ 'Failed to load app from service worker. Falling back to plain <script> tag.',
+ );
+ loadMainDartJs();
+ }
+ }, 4000);
+ });
+ } else {
+ // Service workers not supported. Just drop the <script> tag.
+ loadMainDartJs();
+ }
+ </script>
+</body>
+</html>
diff --git a/packages/rfw/example/hello/web/manifest.json b/packages/rfw/example/hello/web/manifest.json
new file mode 100644
index 0000000..9b3857a
--- /dev/null
+++ b/packages/rfw/example/hello/web/manifest.json
@@ -0,0 +1,35 @@
+{
+ "name": "hello",
+ "short_name": "hello",
+ "start_url": ".",
+ "display": "standalone",
+ "background_color": "#0175C2",
+ "theme_color": "#0175C2",
+ "description": "Hello World example for RFW",
+ "orientation": "portrait-primary",
+ "prefer_related_applications": false,
+ "icons": [
+ {
+ "src": "icons/Icon-192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ },
+ {
+ "src": "icons/Icon-512.png",
+ "sizes": "512x512",
+ "type": "image/png"
+ },
+ {
+ "src": "icons/Icon-maskable-192.png",
+ "sizes": "192x192",
+ "type": "image/png",
+ "purpose": "maskable"
+ },
+ {
+ "src": "icons/Icon-maskable-512.png",
+ "sizes": "512x512",
+ "type": "image/png",
+ "purpose": "maskable"
+ }
+ ]
+}
diff --git a/packages/rfw/example/hello/windows/.gitignore b/packages/rfw/example/hello/windows/.gitignore
new file mode 100644
index 0000000..d492d0d
--- /dev/null
+++ b/packages/rfw/example/hello/windows/.gitignore
@@ -0,0 +1,17 @@
+flutter/ephemeral/
+
+# Visual Studio user-specific files.
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# Visual Studio build-related files.
+x64/
+x86/
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
diff --git a/packages/rfw/example/hello/windows/CMakeLists.txt b/packages/rfw/example/hello/windows/CMakeLists.txt
new file mode 100644
index 0000000..2a5a61c
--- /dev/null
+++ b/packages/rfw/example/hello/windows/CMakeLists.txt
@@ -0,0 +1,95 @@
+cmake_minimum_required(VERSION 3.15)
+project(hello LANGUAGES CXX)
+
+set(BINARY_NAME "hello")
+
+cmake_policy(SET CMP0063 NEW)
+
+set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
+
+# Configure build options.
+get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+if(IS_MULTICONFIG)
+ set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
+ CACHE STRING "" FORCE)
+else()
+ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+ set(CMAKE_BUILD_TYPE "Debug" CACHE
+ STRING "Flutter build mode" FORCE)
+ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
+ "Debug" "Profile" "Release")
+ endif()
+endif()
+
+set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
+set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
+set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
+set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
+
+# Use Unicode for all projects.
+add_definitions(-DUNICODE -D_UNICODE)
+
+# Compilation settings that should be applied to most targets.
+function(APPLY_STANDARD_SETTINGS TARGET)
+ target_compile_features(${TARGET} PUBLIC cxx_std_17)
+ target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
+ target_compile_options(${TARGET} PRIVATE /EHsc)
+ target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
+ target_compile_definitions(${TARGET} PRIVATE "$<$<CONFIG:Debug>:_DEBUG>")
+endfunction()
+
+set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
+
+# Flutter library and tool build rules.
+add_subdirectory(${FLUTTER_MANAGED_DIR})
+
+# Application build
+add_subdirectory("runner")
+
+# Generated plugin build rules, which manage building the plugins and adding
+# them to the application.
+include(flutter/generated_plugins.cmake)
+
+
+# === Installation ===
+# Support files are copied into place next to the executable, so that it can
+# run in place. This is done instead of making a separate bundle (as on Linux)
+# so that building and running from within Visual Studio will work.
+set(BUILD_BUNDLE_DIR "$<TARGET_FILE_DIR:${BINARY_NAME}>")
+# Make the "install" step default, as it's required to run.
+set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
+if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+ set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
+endif()
+
+set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
+set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
+
+install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+
+if(PLUGIN_BUNDLED_LIBRARIES)
+ install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
+ DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endif()
+
+# Fully re-copy the assets directory on each build to avoid having stale files
+# from a previous install.
+set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
+install(CODE "
+ file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
+ " COMPONENT Runtime)
+install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
+ DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
+
+# Install the AOT library on non-Debug builds only.
+install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ CONFIGURATIONS Profile;Release
+ COMPONENT Runtime)
diff --git a/packages/rfw/example/hello/windows/flutter/CMakeLists.txt b/packages/rfw/example/hello/windows/flutter/CMakeLists.txt
new file mode 100644
index 0000000..b02c548
--- /dev/null
+++ b/packages/rfw/example/hello/windows/flutter/CMakeLists.txt
@@ -0,0 +1,103 @@
+cmake_minimum_required(VERSION 3.15)
+
+set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
+
+# Configuration provided via flutter tool.
+include(${EPHEMERAL_DIR}/generated_config.cmake)
+
+# TODO: Move the rest of this into files in ephemeral. See
+# https://github.com/flutter/flutter/issues/57146.
+set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
+
+# === Flutter Library ===
+set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
+
+# Published to parent scope for install step.
+set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
+set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
+set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
+set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE)
+
+list(APPEND FLUTTER_LIBRARY_HEADERS
+ "flutter_export.h"
+ "flutter_windows.h"
+ "flutter_messenger.h"
+ "flutter_plugin_registrar.h"
+ "flutter_texture_registrar.h"
+)
+list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/")
+add_library(flutter INTERFACE)
+target_include_directories(flutter INTERFACE
+ "${EPHEMERAL_DIR}"
+)
+target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib")
+add_dependencies(flutter flutter_assemble)
+
+# === Wrapper ===
+list(APPEND CPP_WRAPPER_SOURCES_CORE
+ "core_implementations.cc"
+ "standard_codec.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
+list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
+ "plugin_registrar.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
+list(APPEND CPP_WRAPPER_SOURCES_APP
+ "flutter_engine.cc"
+ "flutter_view_controller.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
+
+# Wrapper sources needed for a plugin.
+add_library(flutter_wrapper_plugin STATIC
+ ${CPP_WRAPPER_SOURCES_CORE}
+ ${CPP_WRAPPER_SOURCES_PLUGIN}
+)
+apply_standard_settings(flutter_wrapper_plugin)
+set_target_properties(flutter_wrapper_plugin PROPERTIES
+ POSITION_INDEPENDENT_CODE ON)
+set_target_properties(flutter_wrapper_plugin PROPERTIES
+ CXX_VISIBILITY_PRESET hidden)
+target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
+target_include_directories(flutter_wrapper_plugin PUBLIC
+ "${WRAPPER_ROOT}/include"
+)
+add_dependencies(flutter_wrapper_plugin flutter_assemble)
+
+# Wrapper sources needed for the runner.
+add_library(flutter_wrapper_app STATIC
+ ${CPP_WRAPPER_SOURCES_CORE}
+ ${CPP_WRAPPER_SOURCES_APP}
+)
+apply_standard_settings(flutter_wrapper_app)
+target_link_libraries(flutter_wrapper_app PUBLIC flutter)
+target_include_directories(flutter_wrapper_app PUBLIC
+ "${WRAPPER_ROOT}/include"
+)
+add_dependencies(flutter_wrapper_app flutter_assemble)
+
+# === Flutter tool backend ===
+# _phony_ is a non-existent file to force this command to run every time,
+# since currently there's no way to get a full input/output list from the
+# flutter tool.
+set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_")
+set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE)
+add_custom_command(
+ OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
+ ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
+ ${CPP_WRAPPER_SOURCES_APP}
+ ${PHONY_OUTPUT}
+ COMMAND ${CMAKE_COMMAND} -E env
+ ${FLUTTER_TOOL_ENVIRONMENT}
+ "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
+ windows-x64 $<CONFIG>
+ VERBATIM
+)
+add_custom_target(flutter_assemble DEPENDS
+ "${FLUTTER_LIBRARY}"
+ ${FLUTTER_LIBRARY_HEADERS}
+ ${CPP_WRAPPER_SOURCES_CORE}
+ ${CPP_WRAPPER_SOURCES_PLUGIN}
+ ${CPP_WRAPPER_SOURCES_APP}
+)
diff --git a/packages/rfw/example/hello/windows/flutter/generated_plugins.cmake b/packages/rfw/example/hello/windows/flutter/generated_plugins.cmake
new file mode 100644
index 0000000..4d10c25
--- /dev/null
+++ b/packages/rfw/example/hello/windows/flutter/generated_plugins.cmake
@@ -0,0 +1,15 @@
+#
+# Generated file, do not edit.
+#
+
+list(APPEND FLUTTER_PLUGIN_LIST
+)
+
+set(PLUGIN_BUNDLED_LIBRARIES)
+
+foreach(plugin ${FLUTTER_PLUGIN_LIST})
+ add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})
+ target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
+endforeach(plugin)
diff --git a/packages/rfw/example/hello/windows/runner/CMakeLists.txt b/packages/rfw/example/hello/windows/runner/CMakeLists.txt
new file mode 100644
index 0000000..0b899a0
--- /dev/null
+++ b/packages/rfw/example/hello/windows/runner/CMakeLists.txt
@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 3.15)
+project(runner LANGUAGES CXX)
+
+add_executable(${BINARY_NAME} WIN32
+ "flutter_window.cpp"
+ "main.cpp"
+ "utils.cpp"
+ "win32_window.cpp"
+ "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
+ "Runner.rc"
+ "runner.exe.manifest"
+)
+apply_standard_settings(${BINARY_NAME})
+target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
+target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
+target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
+add_dependencies(${BINARY_NAME} flutter_assemble)
diff --git a/packages/rfw/example/hello/windows/runner/Runner.rc b/packages/rfw/example/hello/windows/runner/Runner.rc
new file mode 100644
index 0000000..c9091ea
--- /dev/null
+++ b/packages/rfw/example/hello/windows/runner/Runner.rc
@@ -0,0 +1,121 @@
+// Microsoft Visual C++ generated resource script.
+//
+#pragma code_page(65001)
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (United States) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""winres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_APP_ICON ICON "resources\\app_icon.ico"
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+#ifdef FLUTTER_BUILD_NUMBER
+#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER
+#else
+#define VERSION_AS_NUMBER 1,0,0
+#endif
+
+#ifdef FLUTTER_BUILD_NAME
+#define VERSION_AS_STRING #FLUTTER_BUILD_NAME
+#else
+#define VERSION_AS_STRING "1.0.0"
+#endif
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VERSION_AS_NUMBER
+ PRODUCTVERSION VERSION_AS_NUMBER
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+#ifdef _DEBUG
+ FILEFLAGS VS_FF_DEBUG
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_APP
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "CompanyName", "dev.flutter.rfw.examples" "\0"
+ VALUE "FileDescription", "Hello World example for RFW" "\0"
+ VALUE "FileVersion", VERSION_AS_STRING "\0"
+ VALUE "InternalName", "hello" "\0"
+ VALUE "LegalCopyright", "Copyright (C) 2021 dev.flutter.rfw.examples. All rights reserved." "\0"
+ VALUE "OriginalFilename", "hello.exe" "\0"
+ VALUE "ProductName", "hello" "\0"
+ VALUE "ProductVersion", VERSION_AS_STRING "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252
+ END
+END
+
+#endif // English (United States) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
diff --git a/packages/rfw/example/hello/windows/runner/flutter_window.cpp b/packages/rfw/example/hello/windows/runner/flutter_window.cpp
new file mode 100644
index 0000000..8254bd9
--- /dev/null
+++ b/packages/rfw/example/hello/windows/runner/flutter_window.cpp
@@ -0,0 +1,65 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "flutter_window.h"
+
+#include <optional>
+
+#include "flutter/generated_plugin_registrant.h"
+
+FlutterWindow::FlutterWindow(const flutter::DartProject& project)
+ : project_(project) {}
+
+FlutterWindow::~FlutterWindow() {}
+
+bool FlutterWindow::OnCreate() {
+ if (!Win32Window::OnCreate()) {
+ return false;
+ }
+
+ RECT frame = GetClientArea();
+
+ // The size here must match the window dimensions to avoid unnecessary surface
+ // creation / destruction in the startup path.
+ flutter_controller_ = std::make_unique<flutter::FlutterViewController>(
+ frame.right - frame.left, frame.bottom - frame.top, project_);
+ // Ensure that basic setup of the controller was successful.
+ if (!flutter_controller_->engine() || !flutter_controller_->view()) {
+ return false;
+ }
+ RegisterPlugins(flutter_controller_->engine());
+ SetChildContent(flutter_controller_->view()->GetNativeWindow());
+ return true;
+}
+
+void FlutterWindow::OnDestroy() {
+ if (flutter_controller_) {
+ flutter_controller_ = nullptr;
+ }
+
+ Win32Window::OnDestroy();
+}
+
+LRESULT
+FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept {
+ // Give Flutter, including plugins, an opportunity to handle window messages.
+ if (flutter_controller_) {
+ std::optional<LRESULT> result =
+ flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
+ lparam);
+ if (result) {
+ return *result;
+ }
+ }
+
+ switch (message) {
+ case WM_FONTCHANGE:
+ flutter_controller_->engine()->ReloadSystemFonts();
+ break;
+ }
+
+ return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
+}
diff --git a/packages/rfw/example/hello/windows/runner/flutter_window.h b/packages/rfw/example/hello/windows/runner/flutter_window.h
new file mode 100644
index 0000000..f1fc669
--- /dev/null
+++ b/packages/rfw/example/hello/windows/runner/flutter_window.h
@@ -0,0 +1,37 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef RUNNER_FLUTTER_WINDOW_H_
+#define RUNNER_FLUTTER_WINDOW_H_
+
+#include <flutter/dart_project.h>
+#include <flutter/flutter_view_controller.h>
+
+#include <memory>
+
+#include "win32_window.h"
+
+// A window that does nothing but host a Flutter view.
+class FlutterWindow : public Win32Window {
+ public:
+ // Creates a new FlutterWindow hosting a Flutter view running |project|.
+ explicit FlutterWindow(const flutter::DartProject& project);
+ virtual ~FlutterWindow();
+
+ protected:
+ // Win32Window:
+ bool OnCreate() override;
+ void OnDestroy() override;
+ LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
+ LPARAM const lparam) noexcept override;
+
+ private:
+ // The project to run.
+ flutter::DartProject project_;
+
+ // The Flutter instance hosted by this window.
+ std::unique_ptr<flutter::FlutterViewController> flutter_controller_;
+};
+
+#endif // RUNNER_FLUTTER_WINDOW_H_
diff --git a/packages/rfw/example/hello/windows/runner/main.cpp b/packages/rfw/example/hello/windows/runner/main.cpp
new file mode 100644
index 0000000..7ba735d
--- /dev/null
+++ b/packages/rfw/example/hello/windows/runner/main.cpp
@@ -0,0 +1,46 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <flutter/dart_project.h>
+#include <flutter/flutter_view_controller.h>
+#include <windows.h>
+
+#include "flutter_window.h"
+#include "utils.h"
+
+int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
+ _In_ wchar_t *command_line, _In_ int show_command) {
+ // Attach to console when present (e.g., 'flutter run') or create a
+ // new console when running with a debugger.
+ if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
+ CreateAndAttachConsole();
+ }
+
+ // Initialize COM, so that it is available for use in the library and/or
+ // plugins.
+ ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
+
+ flutter::DartProject project(L"data");
+
+ std::vector<std::string> command_line_arguments = GetCommandLineArguments();
+
+ project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
+
+ FlutterWindow window(project);
+ Win32Window::Point origin(10, 10);
+ Win32Window::Size size(1280, 720);
+ if (!window.CreateAndShow(L"hello", origin, size)) {
+ return EXIT_FAILURE;
+ }
+ window.SetQuitOnClose(true);
+
+ ::MSG msg;
+ while (::GetMessage(&msg, nullptr, 0, 0)) {
+ ::TranslateMessage(&msg);
+ ::DispatchMessage(&msg);
+ }
+
+ ::CoUninitialize();
+ return EXIT_SUCCESS;
+}
diff --git a/packages/rfw/example/hello/windows/runner/resource.h b/packages/rfw/example/hello/windows/runner/resource.h
new file mode 100644
index 0000000..d5d958d
--- /dev/null
+++ b/packages/rfw/example/hello/windows/runner/resource.h
@@ -0,0 +1,16 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by Runner.rc
+//
+#define IDI_APP_ICON 101
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 102
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/packages/rfw/example/hello/windows/runner/resources/app_icon.ico b/packages/rfw/example/hello/windows/runner/resources/app_icon.ico
new file mode 100644
index 0000000..c04e20c
--- /dev/null
+++ b/packages/rfw/example/hello/windows/runner/resources/app_icon.ico
Binary files differ
diff --git a/packages/rfw/example/hello/windows/runner/runner.exe.manifest b/packages/rfw/example/hello/windows/runner/runner.exe.manifest
new file mode 100644
index 0000000..c977c4a
--- /dev/null
+++ b/packages/rfw/example/hello/windows/runner/runner.exe.manifest
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <application xmlns="urn:schemas-microsoft-com:asm.v3">
+ <windowsSettings>
+ <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
+ </windowsSettings>
+ </application>
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <!-- Windows 10 -->
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
+ <!-- Windows 8.1 -->
+ <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
+ <!-- Windows 8 -->
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
+ <!-- Windows 7 -->
+ <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
+ </application>
+ </compatibility>
+</assembly>
diff --git a/packages/rfw/example/hello/windows/runner/utils.cpp b/packages/rfw/example/hello/windows/runner/utils.cpp
new file mode 100644
index 0000000..fb7e945
--- /dev/null
+++ b/packages/rfw/example/hello/windows/runner/utils.cpp
@@ -0,0 +1,67 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "utils.h"
+
+#include <flutter_windows.h>
+#include <io.h>
+#include <stdio.h>
+#include <windows.h>
+
+#include <iostream>
+
+void CreateAndAttachConsole() {
+ if (::AllocConsole()) {
+ FILE* unused;
+ if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
+ _dup2(_fileno(stdout), 1);
+ }
+ if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
+ _dup2(_fileno(stdout), 2);
+ }
+ std::ios::sync_with_stdio();
+ FlutterDesktopResyncOutputStreams();
+ }
+}
+
+std::vector<std::string> GetCommandLineArguments() {
+ // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.
+ int argc;
+ wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
+ if (argv == nullptr) {
+ return std::vector<std::string>();
+ }
+
+ std::vector<std::string> command_line_arguments;
+
+ // Skip the first argument as it's the binary name.
+ for (int i = 1; i < argc; i++) {
+ command_line_arguments.push_back(Utf8FromUtf16(argv[i]));
+ }
+
+ ::LocalFree(argv);
+
+ return command_line_arguments;
+}
+
+std::string Utf8FromUtf16(const wchar_t* utf16_string) {
+ if (utf16_string == nullptr) {
+ return std::string();
+ }
+ int target_length =
+ ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1,
+ nullptr, 0, nullptr, nullptr);
+ if (target_length == 0) {
+ return std::string();
+ }
+ std::string utf8_string;
+ utf8_string.resize(target_length);
+ int converted_length = ::WideCharToMultiByte(
+ CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, utf8_string.data(),
+ target_length, nullptr, nullptr);
+ if (converted_length == 0) {
+ return std::string();
+ }
+ return utf8_string;
+}
diff --git a/packages/rfw/example/hello/windows/runner/utils.h b/packages/rfw/example/hello/windows/runner/utils.h
new file mode 100644
index 0000000..bd81e1e
--- /dev/null
+++ b/packages/rfw/example/hello/windows/runner/utils.h
@@ -0,0 +1,23 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef RUNNER_UTILS_H_
+#define RUNNER_UTILS_H_
+
+#include <string>
+#include <vector>
+
+// Creates a console for the process, and redirects stdout and stderr to
+// it for both the runner and the Flutter library.
+void CreateAndAttachConsole();
+
+// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string
+// encoded in UTF-8. Returns an empty std::string on failure.
+std::string Utf8FromUtf16(const wchar_t* utf16_string);
+
+// Gets the command line arguments passed in as a std::vector<std::string>,
+// encoded in UTF-8. Returns an empty std::vector<std::string> on failure.
+std::vector<std::string> GetCommandLineArguments();
+
+#endif // RUNNER_UTILS_H_
diff --git a/packages/rfw/example/hello/windows/runner/win32_window.cpp b/packages/rfw/example/hello/windows/runner/win32_window.cpp
new file mode 100644
index 0000000..85aa361
--- /dev/null
+++ b/packages/rfw/example/hello/windows/runner/win32_window.cpp
@@ -0,0 +1,241 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "win32_window.h"
+
+#include <flutter_windows.h>
+
+#include "resource.h"
+
+namespace {
+
+constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
+
+// The number of Win32Window objects that currently exist.
+static int g_active_window_count = 0;
+
+using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
+
+// Scale helper to convert logical scaler values to physical using passed in
+// scale factor
+int Scale(int source, double scale_factor) {
+ return static_cast<int>(source * scale_factor);
+}
+
+// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
+// This API is only needed for PerMonitor V1 awareness mode.
+void EnableFullDpiSupportIfAvailable(HWND hwnd) {
+ HMODULE user32_module = LoadLibraryA("User32.dll");
+ if (!user32_module) {
+ return;
+ }
+ auto enable_non_client_dpi_scaling =
+ reinterpret_cast<EnableNonClientDpiScaling*>(
+ GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
+ if (enable_non_client_dpi_scaling != nullptr) {
+ enable_non_client_dpi_scaling(hwnd);
+ FreeLibrary(user32_module);
+ }
+}
+
+} // namespace
+
+// Manages the Win32Window's window class registration.
+class WindowClassRegistrar {
+ public:
+ ~WindowClassRegistrar() = default;
+
+ // Returns the singleton registar instance.
+ static WindowClassRegistrar* GetInstance() {
+ if (!instance_) {
+ instance_ = new WindowClassRegistrar();
+ }
+ return instance_;
+ }
+
+ // Returns the name of the window class, registering the class if it hasn't
+ // previously been registered.
+ const wchar_t* GetWindowClass();
+
+ // Unregisters the window class. Should only be called if there are no
+ // instances of the window.
+ void UnregisterWindowClass();
+
+ private:
+ WindowClassRegistrar() = default;
+
+ static WindowClassRegistrar* instance_;
+
+ bool class_registered_ = false;
+};
+
+WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;
+
+const wchar_t* WindowClassRegistrar::GetWindowClass() {
+ if (!class_registered_) {
+ WNDCLASS window_class{};
+ window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
+ window_class.lpszClassName = kWindowClassName;
+ window_class.style = CS_HREDRAW | CS_VREDRAW;
+ window_class.cbClsExtra = 0;
+ window_class.cbWndExtra = 0;
+ window_class.hInstance = GetModuleHandle(nullptr);
+ window_class.hIcon =
+ LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
+ window_class.hbrBackground = 0;
+ window_class.lpszMenuName = nullptr;
+ window_class.lpfnWndProc = Win32Window::WndProc;
+ RegisterClass(&window_class);
+ class_registered_ = true;
+ }
+ return kWindowClassName;
+}
+
+void WindowClassRegistrar::UnregisterWindowClass() {
+ UnregisterClass(kWindowClassName, nullptr);
+ class_registered_ = false;
+}
+
+Win32Window::Win32Window() { ++g_active_window_count; }
+
+Win32Window::~Win32Window() {
+ --g_active_window_count;
+ Destroy();
+}
+
+bool Win32Window::CreateAndShow(const std::wstring& title, const Point& origin,
+ const Size& size) {
+ Destroy();
+
+ const wchar_t* window_class =
+ WindowClassRegistrar::GetInstance()->GetWindowClass();
+
+ const POINT target_point = {static_cast<LONG>(origin.x),
+ static_cast<LONG>(origin.y)};
+ HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
+ UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
+ double scale_factor = dpi / 96.0;
+
+ HWND window = CreateWindow(
+ window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
+ Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
+ Scale(size.width, scale_factor), Scale(size.height, scale_factor),
+ nullptr, nullptr, GetModuleHandle(nullptr), this);
+
+ if (!window) {
+ return false;
+ }
+
+ return OnCreate();
+}
+
+// static
+LRESULT CALLBACK Win32Window::WndProc(HWND const window, UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept {
+ if (message == WM_NCCREATE) {
+ auto window_struct = reinterpret_cast<CREATESTRUCT*>(lparam);
+ SetWindowLongPtr(window, GWLP_USERDATA,
+ reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));
+
+ auto that = static_cast<Win32Window*>(window_struct->lpCreateParams);
+ EnableFullDpiSupportIfAvailable(window);
+ that->window_handle_ = window;
+ } else if (Win32Window* that = GetThisFromHandle(window)) {
+ return that->MessageHandler(window, message, wparam, lparam);
+ }
+
+ return DefWindowProc(window, message, wparam, lparam);
+}
+
+LRESULT
+Win32Window::MessageHandler(HWND hwnd, UINT const message, WPARAM const wparam,
+ LPARAM const lparam) noexcept {
+ switch (message) {
+ case WM_DESTROY:
+ window_handle_ = nullptr;
+ Destroy();
+ if (quit_on_close_) {
+ PostQuitMessage(0);
+ }
+ return 0;
+
+ case WM_DPICHANGED: {
+ auto newRectSize = reinterpret_cast<RECT*>(lparam);
+ LONG newWidth = newRectSize->right - newRectSize->left;
+ LONG newHeight = newRectSize->bottom - newRectSize->top;
+
+ SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,
+ newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
+
+ return 0;
+ }
+ case WM_SIZE: {
+ RECT rect = GetClientArea();
+ if (child_content_ != nullptr) {
+ // Size and position the child window.
+ MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,
+ rect.bottom - rect.top, TRUE);
+ }
+ return 0;
+ }
+
+ case WM_ACTIVATE:
+ if (child_content_ != nullptr) {
+ SetFocus(child_content_);
+ }
+ return 0;
+ }
+
+ return DefWindowProc(window_handle_, message, wparam, lparam);
+}
+
+void Win32Window::Destroy() {
+ OnDestroy();
+
+ if (window_handle_) {
+ DestroyWindow(window_handle_);
+ window_handle_ = nullptr;
+ }
+ if (g_active_window_count == 0) {
+ WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
+ }
+}
+
+Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
+ return reinterpret_cast<Win32Window*>(
+ GetWindowLongPtr(window, GWLP_USERDATA));
+}
+
+void Win32Window::SetChildContent(HWND content) {
+ child_content_ = content;
+ SetParent(content, window_handle_);
+ RECT frame = GetClientArea();
+
+ MoveWindow(content, frame.left, frame.top, frame.right - frame.left,
+ frame.bottom - frame.top, true);
+
+ SetFocus(child_content_);
+}
+
+RECT Win32Window::GetClientArea() {
+ RECT frame;
+ GetClientRect(window_handle_, &frame);
+ return frame;
+}
+
+HWND Win32Window::GetHandle() { return window_handle_; }
+
+void Win32Window::SetQuitOnClose(bool quit_on_close) {
+ quit_on_close_ = quit_on_close;
+}
+
+bool Win32Window::OnCreate() {
+ // No-op; provided for subclasses.
+ return true;
+}
+
+void Win32Window::OnDestroy() {
+ // No-op; provided for subclasses.
+}
diff --git a/packages/rfw/example/hello/windows/runner/win32_window.h b/packages/rfw/example/hello/windows/runner/win32_window.h
new file mode 100644
index 0000000..d2a7300
--- /dev/null
+++ b/packages/rfw/example/hello/windows/runner/win32_window.h
@@ -0,0 +1,99 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef RUNNER_WIN32_WINDOW_H_
+#define RUNNER_WIN32_WINDOW_H_
+
+#include <windows.h>
+
+#include <functional>
+#include <memory>
+#include <string>
+
+// A class abstraction for a high DPI-aware Win32 Window. Intended to be
+// inherited from by classes that wish to specialize with custom
+// rendering and input handling
+class Win32Window {
+ public:
+ struct Point {
+ unsigned int x;
+ unsigned int y;
+ Point(unsigned int x, unsigned int y) : x(x), y(y) {}
+ };
+
+ struct Size {
+ unsigned int width;
+ unsigned int height;
+ Size(unsigned int width, unsigned int height)
+ : width(width), height(height) {}
+ };
+
+ Win32Window();
+ virtual ~Win32Window();
+
+ // Creates and shows a win32 window with |title| and position and size using
+ // |origin| and |size|. New windows are created on the default monitor. Window
+ // sizes are specified to the OS in physical pixels, hence to ensure a
+ // consistent size to will treat the width height passed in to this function
+ // as logical pixels and scale to appropriate for the default monitor. Returns
+ // true if the window was created successfully.
+ bool CreateAndShow(const std::wstring& title, const Point& origin,
+ const Size& size);
+
+ // Release OS resources associated with window.
+ void Destroy();
+
+ // Inserts |content| into the window tree.
+ void SetChildContent(HWND content);
+
+ // Returns the backing Window handle to enable clients to set icon and other
+ // window properties. Returns nullptr if the window has been destroyed.
+ HWND GetHandle();
+
+ // If true, closing this window will quit the application.
+ void SetQuitOnClose(bool quit_on_close);
+
+ // Return a RECT representing the bounds of the current client area.
+ RECT GetClientArea();
+
+ protected:
+ // Processes and route salient window messages for mouse handling,
+ // size change and DPI. Delegates handling of these to member overloads that
+ // inheriting classes can handle.
+ virtual LRESULT MessageHandler(HWND window, UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept;
+
+ // Called when CreateAndShow is called, allowing subclass window-related
+ // setup. Subclasses should return false if setup fails.
+ virtual bool OnCreate();
+
+ // Called when Destroy is called.
+ virtual void OnDestroy();
+
+ private:
+ friend class WindowClassRegistrar;
+
+ // OS callback called by message pump. Handles the WM_NCCREATE message which
+ // is passed when the non-client area is being created and enables automatic
+ // non-client DPI scaling so that the non-client area automatically
+ // responsponds to changes in DPI. All other messages are handled by
+ // MessageHandler.
+ static LRESULT CALLBACK WndProc(HWND const window, UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept;
+
+ // Retrieves a class instance pointer for |window|
+ static Win32Window* GetThisFromHandle(HWND const window) noexcept;
+
+ bool quit_on_close_ = false;
+
+ // window handle for top level window.
+ HWND window_handle_ = nullptr;
+
+ // window handle for hosted content.
+ HWND child_content_ = nullptr;
+};
+
+#endif // RUNNER_WIN32_WINDOW_H_
diff --git a/packages/rfw/example/local/.gitignore b/packages/rfw/example/local/.gitignore
new file mode 100644
index 0000000..0fa6b67
--- /dev/null
+++ b/packages/rfw/example/local/.gitignore
@@ -0,0 +1,46 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+**/ios/Flutter/.last_build_id
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+.packages
+.pub-cache/
+.pub/
+/build/
+
+# Web related
+lib/generated_plugin_registrant.dart
+
+# Symbolication related
+app.*.symbols
+
+# Obfuscation related
+app.*.map.json
+
+# Android Studio will place build artifacts here
+/android/app/debug
+/android/app/profile
+/android/app/release
diff --git a/packages/rfw/example/local/.metadata b/packages/rfw/example/local/.metadata
new file mode 100644
index 0000000..45c494e
--- /dev/null
+++ b/packages/rfw/example/local/.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: a1cd3f45e02c2429e14630d6cc6de120c3d24bd0
+ channel: master
+
+project_type: app
diff --git a/packages/rfw/example/local/README.md b/packages/rfw/example/local/README.md
new file mode 100644
index 0000000..f8dc992
--- /dev/null
+++ b/packages/rfw/example/local/README.md
@@ -0,0 +1,4 @@
+# Example of new custom local widgets for RFW
+
+This example shows how one can create custom widgets in an RFW client,
+for use by remote widgets.
diff --git a/packages/rfw/example/local/android/.gitignore b/packages/rfw/example/local/android/.gitignore
new file mode 100644
index 0000000..6f56801
--- /dev/null
+++ b/packages/rfw/example/local/android/.gitignore
@@ -0,0 +1,13 @@
+gradle-wrapper.jar
+/.gradle
+/captures/
+/gradlew
+/gradlew.bat
+/local.properties
+GeneratedPluginRegistrant.java
+
+# Remember to never publicly share your keystore.
+# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
+key.properties
+**/*.keystore
+**/*.jks
diff --git a/packages/rfw/example/local/android/app/build.gradle b/packages/rfw/example/local/android/app/build.gradle
new file mode 100644
index 0000000..de9e40e
--- /dev/null
+++ b/packages/rfw/example/local/android/app/build.gradle
@@ -0,0 +1,68 @@
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+ localPropertiesFile.withReader('UTF-8') { reader ->
+ localProperties.load(reader)
+ }
+}
+
+def flutterRoot = localProperties.getProperty('flutter.sdk')
+if (flutterRoot == null) {
+ throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
+}
+
+def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
+if (flutterVersionCode == null) {
+ flutterVersionCode = '1'
+}
+
+def flutterVersionName = localProperties.getProperty('flutter.versionName')
+if (flutterVersionName == null) {
+ flutterVersionName = '1.0'
+}
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
+
+android {
+ compileSdkVersion 30
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+
+ defaultConfig {
+ // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
+ applicationId "dev.flutter.rfw.examples.local"
+ minSdkVersion 16
+ targetSdkVersion 30
+ versionCode flutterVersionCode.toInteger()
+ versionName flutterVersionName
+ }
+
+ buildTypes {
+ release {
+ // TODO: Add your own signing config for the release build.
+ // Signing with the debug keys for now, so `flutter run --release` works.
+ signingConfig signingConfigs.debug
+ }
+ }
+}
+
+flutter {
+ source '../..'
+}
+
+dependencies {
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+}
diff --git a/packages/rfw/example/local/android/app/src/debug/AndroidManifest.xml b/packages/rfw/example/local/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 0000000..2fd5378
--- /dev/null
+++ b/packages/rfw/example/local/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,7 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dev.flutter.rfw.examples.local">
+ <!-- Flutter needs it to communicate with the running application
+ to allow setting breakpoints, to provide hot reload, etc.
+ -->
+ <uses-permission android:name="android.permission.INTERNET"/>
+</manifest>
diff --git a/packages/rfw/example/local/android/app/src/main/AndroidManifest.xml b/packages/rfw/example/local/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..95ddb98
--- /dev/null
+++ b/packages/rfw/example/local/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dev.flutter.rfw.examples.local">
+ <application
+ android:label="local"
+ android:icon="@mipmap/ic_launcher">
+ <activity
+ android:name=".MainActivity"
+ android:launchMode="singleTop"
+ android:theme="@style/LaunchTheme"
+ android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
+ android:hardwareAccelerated="true"
+ android:windowSoftInputMode="adjustResize">
+ <!-- Specifies an Android theme to apply to this Activity as soon as
+ the Android process has started. This theme is visible to the user
+ while the Flutter UI initializes. After that, this theme continues
+ to determine the Window background behind the Flutter UI. -->
+ <meta-data
+ android:name="io.flutter.embedding.android.NormalTheme"
+ android:resource="@style/NormalTheme"
+ />
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <!-- Don't delete the meta-data below.
+ This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
+ <meta-data
+ android:name="flutterEmbedding"
+ android:value="2" />
+ </application>
+</manifest>
diff --git a/packages/rfw/example/local/android/app/src/main/kotlin/dev/flutter/rfw/examples/local/MainActivity.kt b/packages/rfw/example/local/android/app/src/main/kotlin/dev/flutter/rfw/examples/local/MainActivity.kt
new file mode 100644
index 0000000..2eb31b4
--- /dev/null
+++ b/packages/rfw/example/local/android/app/src/main/kotlin/dev/flutter/rfw/examples/local/MainActivity.kt
@@ -0,0 +1,6 @@
+package dev.flutter.rfw.examples.local
+
+import io.flutter.embedding.android.FlutterActivity
+
+class MainActivity: FlutterActivity() {
+}
diff --git a/packages/rfw/example/local/android/app/src/main/res/drawable-v21/launch_background.xml b/packages/rfw/example/local/android/app/src/main/res/drawable-v21/launch_background.xml
new file mode 100644
index 0000000..f74085f
--- /dev/null
+++ b/packages/rfw/example/local/android/app/src/main/res/drawable-v21/launch_background.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Modify this file to customize your launch splash screen -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="?android:colorBackground" />
+
+ <!-- You can insert your own image assets here -->
+ <!-- <item>
+ <bitmap
+ android:gravity="center"
+ android:src="@mipmap/launch_image" />
+ </item> -->
+</layer-list>
diff --git a/packages/rfw/example/local/android/app/src/main/res/drawable/launch_background.xml b/packages/rfw/example/local/android/app/src/main/res/drawable/launch_background.xml
new file mode 100644
index 0000000..304732f
--- /dev/null
+++ b/packages/rfw/example/local/android/app/src/main/res/drawable/launch_background.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Modify this file to customize your launch splash screen -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@android:color/white" />
+
+ <!-- You can insert your own image assets here -->
+ <!-- <item>
+ <bitmap
+ android:gravity="center"
+ android:src="@mipmap/launch_image" />
+ </item> -->
+</layer-list>
diff --git a/packages/rfw/example/local/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/rfw/example/local/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..db77bb4
--- /dev/null
+++ b/packages/rfw/example/local/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/packages/rfw/example/local/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/rfw/example/local/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..17987b7
--- /dev/null
+++ b/packages/rfw/example/local/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/packages/rfw/example/local/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/packages/rfw/example/local/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..09d4391
--- /dev/null
+++ b/packages/rfw/example/local/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/packages/rfw/example/local/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/rfw/example/local/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..d5f1c8d
--- /dev/null
+++ b/packages/rfw/example/local/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/packages/rfw/example/local/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/rfw/example/local/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4d6372e
--- /dev/null
+++ b/packages/rfw/example/local/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/packages/rfw/example/local/android/app/src/main/res/values-night/styles.xml b/packages/rfw/example/local/android/app/src/main/res/values-night/styles.xml
new file mode 100644
index 0000000..449a9f9
--- /dev/null
+++ b/packages/rfw/example/local/android/app/src/main/res/values-night/styles.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
+ <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
+ <!-- Show a splash screen on the activity. Automatically removed when
+ Flutter draws its first frame -->
+ <item name="android:windowBackground">@drawable/launch_background</item>
+ </style>
+ <!-- Theme applied to the Android Window as soon as the process has started.
+ This theme determines the color of the Android Window while your
+ Flutter UI initializes, as well as behind your Flutter UI while its
+ running.
+
+ This Theme is only used starting with V2 of Flutter's Android embedding. -->
+ <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
+ <item name="android:windowBackground">?android:colorBackground</item>
+ </style>
+</resources>
diff --git a/packages/rfw/example/local/android/app/src/main/res/values/styles.xml b/packages/rfw/example/local/android/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..d74aa35
--- /dev/null
+++ b/packages/rfw/example/local/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
+ <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
+ <!-- Show a splash screen on the activity. Automatically removed when
+ Flutter draws its first frame -->
+ <item name="android:windowBackground">@drawable/launch_background</item>
+ </style>
+ <!-- Theme applied to the Android Window as soon as the process has started.
+ This theme determines the color of the Android Window while your
+ Flutter UI initializes, as well as behind your Flutter UI while its
+ running.
+
+ This Theme is only used starting with V2 of Flutter's Android embedding. -->
+ <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
+ <item name="android:windowBackground">?android:colorBackground</item>
+ </style>
+</resources>
diff --git a/packages/rfw/example/local/android/app/src/profile/AndroidManifest.xml b/packages/rfw/example/local/android/app/src/profile/AndroidManifest.xml
new file mode 100644
index 0000000..2fd5378
--- /dev/null
+++ b/packages/rfw/example/local/android/app/src/profile/AndroidManifest.xml
@@ -0,0 +1,7 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dev.flutter.rfw.examples.local">
+ <!-- Flutter needs it to communicate with the running application
+ to allow setting breakpoints, to provide hot reload, etc.
+ -->
+ <uses-permission android:name="android.permission.INTERNET"/>
+</manifest>
diff --git a/packages/rfw/example/local/android/build.gradle b/packages/rfw/example/local/android/build.gradle
new file mode 100644
index 0000000..ed45c65
--- /dev/null
+++ b/packages/rfw/example/local/android/build.gradle
@@ -0,0 +1,29 @@
+buildscript {
+ ext.kotlin_version = '1.3.50'
+ repositories {
+ google()
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:4.1.0'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.buildDir = '../build'
+subprojects {
+ project.buildDir = "${rootProject.buildDir}/${project.name}"
+ project.evaluationDependsOn(':app')
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/packages/rfw/example/local/android/gradle.properties b/packages/rfw/example/local/android/gradle.properties
new file mode 100644
index 0000000..94adc3a
--- /dev/null
+++ b/packages/rfw/example/local/android/gradle.properties
@@ -0,0 +1,3 @@
+org.gradle.jvmargs=-Xmx1536M
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/packages/rfw/example/local/android/gradle/wrapper/gradle-wrapper.properties b/packages/rfw/example/local/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..bc6a58a
--- /dev/null
+++ b/packages/rfw/example/local/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-6.7-all.zip
diff --git a/packages/rfw/example/local/android/settings.gradle b/packages/rfw/example/local/android/settings.gradle
new file mode 100644
index 0000000..44e62bc
--- /dev/null
+++ b/packages/rfw/example/local/android/settings.gradle
@@ -0,0 +1,11 @@
+include ':app'
+
+def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
+def properties = new Properties()
+
+assert localPropertiesFile.exists()
+localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
+
+def flutterSdkPath = properties.getProperty("flutter.sdk")
+assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
diff --git a/packages/rfw/example/local/ios/.gitignore b/packages/rfw/example/local/ios/.gitignore
new file mode 100644
index 0000000..151026b
--- /dev/null
+++ b/packages/rfw/example/local/ios/.gitignore
@@ -0,0 +1,33 @@
+*.mode1v3
+*.mode2v3
+*.moved-aside
+*.pbxuser
+*.perspectivev3
+**/*sync/
+.sconsign.dblite
+.tags*
+**/.vagrant/
+**/DerivedData/
+Icon?
+**/Pods/
+**/.symlinks/
+profile
+xcuserdata
+**/.generated/
+Flutter/App.framework
+Flutter/Flutter.framework
+Flutter/Flutter.podspec
+Flutter/Generated.xcconfig
+Flutter/ephemeral/
+Flutter/app.flx
+Flutter/app.zip
+Flutter/flutter_assets/
+Flutter/flutter_export_environment.sh
+ServiceDefinitions.json
+Runner/GeneratedPluginRegistrant.*
+
+# Exceptions to above rules.
+!default.mode1v3
+!default.mode2v3
+!default.pbxuser
+!default.perspectivev3
diff --git a/packages/rfw/example/local/ios/Flutter/AppFrameworkInfo.plist b/packages/rfw/example/local/ios/Flutter/AppFrameworkInfo.plist
new file mode 100644
index 0000000..8d4492f
--- /dev/null
+++ b/packages/rfw/example/local/ios/Flutter/AppFrameworkInfo.plist
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleExecutable</key>
+ <string>App</string>
+ <key>CFBundleIdentifier</key>
+ <string>io.flutter.flutter.app</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>App</string>
+ <key>CFBundlePackageType</key>
+ <string>FMWK</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>MinimumOSVersion</key>
+ <string>9.0</string>
+</dict>
+</plist>
diff --git a/packages/rfw/example/local/ios/Flutter/Debug.xcconfig b/packages/rfw/example/local/ios/Flutter/Debug.xcconfig
new file mode 100644
index 0000000..592ceee
--- /dev/null
+++ b/packages/rfw/example/local/ios/Flutter/Debug.xcconfig
@@ -0,0 +1 @@
+#include "Generated.xcconfig"
diff --git a/packages/rfw/example/local/ios/Flutter/Release.xcconfig b/packages/rfw/example/local/ios/Flutter/Release.xcconfig
new file mode 100644
index 0000000..592ceee
--- /dev/null
+++ b/packages/rfw/example/local/ios/Flutter/Release.xcconfig
@@ -0,0 +1 @@
+#include "Generated.xcconfig"
diff --git a/packages/rfw/example/local/ios/Runner.xcodeproj/project.pbxproj b/packages/rfw/example/local/ios/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..d231a20
--- /dev/null
+++ b/packages/rfw/example/local/ios/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,481 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 50;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
+ 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
+ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 97C146EB1CF9000F007C117D /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 9740EEB11CF90186004384FC /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */,
+ );
+ name = Flutter;
+ sourceTree = "<group>";
+ };
+ 97C146E51CF9000F007C117D = {
+ isa = PBXGroup;
+ children = (
+ 9740EEB11CF90186004384FC /* Flutter */,
+ 97C146F01CF9000F007C117D /* Runner */,
+ 97C146EF1CF9000F007C117D /* Products */,
+ );
+ sourceTree = "<group>";
+ };
+ 97C146EF1CF9000F007C117D /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146EE1CF9000F007C117D /* Runner.app */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 97C146F01CF9000F007C117D /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146FA1CF9000F007C117D /* Main.storyboard */,
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */,
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
+ 97C147021CF9000F007C117D /* Info.plist */,
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
+ );
+ path = Runner;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 97C146ED1CF9000F007C117D /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ 9740EEB61CF901F6004384FC /* Run Script */,
+ 97C146EA1CF9000F007C117D /* Sources */,
+ 97C146EB1CF9000F007C117D /* Frameworks */,
+ 97C146EC1CF9000F007C117D /* Resources */,
+ 9705A1C41CF9048500538489 /* Embed Frameworks */,
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = Runner;
+ productName = Runner;
+ productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 97C146E61CF9000F007C117D /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 1300;
+ ORGANIZATIONNAME = "";
+ TargetAttributes = {
+ 97C146ED1CF9000F007C117D = {
+ CreatedOnToolsVersion = 7.3.1;
+ LastSwiftMigration = 1100;
+ };
+ };
+ };
+ buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 97C146E51CF9000F007C117D;
+ productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 97C146ED1CF9000F007C117D /* Runner */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 97C146EC1CF9000F007C117D /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Thin Binary";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
+ };
+ 9740EEB61CF901F6004384FC /* Run Script */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Run Script";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 97C146EA1CF9000F007C117D /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C146FB1CF9000F007C117D /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "<group>";
+ };
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C147001CF9000F007C117D /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 249021D3217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Profile;
+ };
+ 249021D4217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.rfw.examples.local;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Profile;
+ };
+ 97C147031CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 97C147041CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 97C147061CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.rfw.examples.local;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Debug;
+ };
+ 97C147071CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.rfw.examples.local;
+ 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/packages/rfw/example/local/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/rfw/example/local/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..919434a
--- /dev/null
+++ b/packages/rfw/example/local/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "self:">
+ </FileRef>
+</Workspace>
diff --git a/packages/rfw/example/local/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/rfw/example/local/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/packages/rfw/example/local/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IDEDidComputeMac32BitWarning</key>
+ <true/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/local/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/rfw/example/local/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 0000000..f9b0d7c
--- /dev/null
+++ b/packages/rfw/example/local/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>PreviewsEnabled</key>
+ <false/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/local/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/rfw/example/local/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 0000000..c87d15a
--- /dev/null
+++ b/packages/rfw/example/local/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "1300"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+ BuildableName = "Runner.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+ BuildableName = "Runner.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <Testables>
+ </Testables>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+ BuildableName = "Runner.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Profile"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+ BuildableName = "Runner.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/packages/rfw/example/local/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/rfw/example/local/ios/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..1d526a1
--- /dev/null
+++ b/packages/rfw/example/local/ios/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "group:Runner.xcodeproj">
+ </FileRef>
+</Workspace>
diff --git a/packages/rfw/example/local/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/rfw/example/local/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/packages/rfw/example/local/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IDEDidComputeMac32BitWarning</key>
+ <true/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/local/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/rfw/example/local/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 0000000..f9b0d7c
--- /dev/null
+++ b/packages/rfw/example/local/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>PreviewsEnabled</key>
+ <false/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/local/ios/Runner/AppDelegate.swift b/packages/rfw/example/local/ios/Runner/AppDelegate.swift
new file mode 100644
index 0000000..caf9983
--- /dev/null
+++ b/packages/rfw/example/local/ios/Runner/AppDelegate.swift
@@ -0,0 +1,17 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+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/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..d36b1fa
--- /dev/null
+++ b/packages/rfw/example/local/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/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
new file mode 100644
index 0000000..dc9ada4
--- /dev/null
+++ b/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
Binary files differ
diff --git a/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
new file mode 100644
index 0000000..28c6bf0
--- /dev/null
+++ b/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
Binary files differ
diff --git a/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
new file mode 100644
index 0000000..2ccbfd9
--- /dev/null
+++ b/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
Binary files differ
diff --git a/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
new file mode 100644
index 0000000..f091b6b
--- /dev/null
+++ b/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
Binary files differ
diff --git a/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
new file mode 100644
index 0000000..4cde121
--- /dev/null
+++ b/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
Binary files differ
diff --git a/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
new file mode 100644
index 0000000..d0ef06e
--- /dev/null
+++ b/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
Binary files differ
diff --git a/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
new file mode 100644
index 0000000..dcdc230
--- /dev/null
+++ b/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
Binary files differ
diff --git a/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
new file mode 100644
index 0000000..2ccbfd9
--- /dev/null
+++ b/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
Binary files differ
diff --git a/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
new file mode 100644
index 0000000..c8f9ed8
--- /dev/null
+++ b/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
Binary files differ
diff --git a/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
new file mode 100644
index 0000000..a6d6b86
--- /dev/null
+++ b/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
Binary files differ
diff --git a/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
new file mode 100644
index 0000000..a6d6b86
--- /dev/null
+++ b/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
Binary files differ
diff --git a/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
new file mode 100644
index 0000000..75b2d16
--- /dev/null
+++ b/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
Binary files differ
diff --git a/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
new file mode 100644
index 0000000..c4df70d
--- /dev/null
+++ b/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
Binary files differ
diff --git a/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
new file mode 100644
index 0000000..6a84f41
--- /dev/null
+++ b/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
Binary files differ
diff --git a/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
new file mode 100644
index 0000000..d0e1f58
--- /dev/null
+++ b/packages/rfw/example/local/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
Binary files differ
diff --git a/packages/rfw/example/local/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/packages/rfw/example/local/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
new file mode 100644
index 0000000..0bedcf2
--- /dev/null
+++ b/packages/rfw/example/local/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/packages/rfw/example/local/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/packages/rfw/example/local/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
new file mode 100644
index 0000000..9da19ea
--- /dev/null
+++ b/packages/rfw/example/local/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
Binary files differ
diff --git a/packages/rfw/example/local/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/rfw/example/local/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
new file mode 100644
index 0000000..9da19ea
--- /dev/null
+++ b/packages/rfw/example/local/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
Binary files differ
diff --git a/packages/rfw/example/local/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/rfw/example/local/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
new file mode 100644
index 0000000..9da19ea
--- /dev/null
+++ b/packages/rfw/example/local/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
Binary files differ
diff --git a/packages/rfw/example/local/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/packages/rfw/example/local/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
new file mode 100644
index 0000000..89c2725
--- /dev/null
+++ b/packages/rfw/example/local/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/packages/rfw/example/local/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/rfw/example/local/ios/Runner/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000..f2e259c
--- /dev/null
+++ b/packages/rfw/example/local/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
+ <dependencies>
+ <deployment identifier="iOS"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
+ </dependencies>
+ <scenes>
+ <!--View Controller-->
+ <scene sceneID="EHf-IW-A2E">
+ <objects>
+ <viewController id="01J-lp-oVM" sceneMemberID="viewController">
+ <layoutGuides>
+ <viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
+ <viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
+ </layoutGuides>
+ <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <subviews>
+ <imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
+ </imageView>
+ </subviews>
+ <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+ <constraints>
+ <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
+ <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
+ </constraints>
+ </view>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="53" y="375"/>
+ </scene>
+ </scenes>
+ <resources>
+ <image name="LaunchImage" width="168" height="185"/>
+ </resources>
+</document>
diff --git a/packages/rfw/example/local/ios/Runner/Base.lproj/Main.storyboard b/packages/rfw/example/local/ios/Runner/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..f3c2851
--- /dev/null
+++ b/packages/rfw/example/local/ios/Runner/Base.lproj/Main.storyboard
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
+ <dependencies>
+ <deployment identifier="iOS"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
+ </dependencies>
+ <scenes>
+ <!--Flutter View Controller-->
+ <scene sceneID="tne-QT-ifu">
+ <objects>
+ <viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
+ <layoutGuides>
+ <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
+ <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
+ </layoutGuides>
+ <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
+ <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+ </view>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
+ </objects>
+ </scene>
+ </scenes>
+</document>
diff --git a/packages/rfw/example/local/ios/Runner/Info.plist b/packages/rfw/example/local/ios/Runner/Info.plist
new file mode 100644
index 0000000..2150ab0
--- /dev/null
+++ b/packages/rfw/example/local/ios/Runner/Info.plist
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>$(DEVELOPMENT_LANGUAGE)</string>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>local</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>$(FLUTTER_BUILD_NAME)</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>$(FLUTTER_BUILD_NUMBER)</string>
+ <key>LSRequiresIPhoneOS</key>
+ <true/>
+ <key>UILaunchStoryboardName</key>
+ <string>LaunchScreen</string>
+ <key>UIMainStoryboardFile</key>
+ <string>Main</string>
+ <key>UISupportedInterfaceOrientations</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+ <key>UISupportedInterfaceOrientations~ipad</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationPortraitUpsideDown</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+ <key>UIViewControllerBasedStatusBarAppearance</key>
+ <false/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/local/ios/Runner/Runner-Bridging-Header.h b/packages/rfw/example/local/ios/Runner/Runner-Bridging-Header.h
new file mode 100644
index 0000000..eb7e8ba
--- /dev/null
+++ b/packages/rfw/example/local/ios/Runner/Runner-Bridging-Header.h
@@ -0,0 +1,5 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "GeneratedPluginRegistrant.h"
diff --git a/packages/rfw/example/local/lib/main.dart b/packages/rfw/example/local/lib/main.dart
new file mode 100644
index 0000000..d9f36b7
--- /dev/null
+++ b/packages/rfw/example/local/lib/main.dart
@@ -0,0 +1,89 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/widgets.dart';
+
+/// For clarity, this example uses the text representation of the sample remote
+/// widget library, and parses it locally. To do this, [parseLibraryFile] is
+/// used. In production, this is strongly discouraged since it is 10x slower
+/// than using [decodeLibraryBlob] to parse the binary version of the format.
+import 'package:rfw/formats.dart' show parseLibraryFile;
+
+import 'package:rfw/rfw.dart';
+
+void main() {
+ runApp(const Example());
+}
+
+class Example extends StatefulWidget {
+ const Example({Key? key}) : super(key: key);
+
+ @override
+ State<Example> createState() => _ExampleState();
+}
+
+class _ExampleState extends State<Example> {
+ final Runtime _runtime = Runtime();
+ final DynamicContent _data = DynamicContent();
+
+ @override
+ void initState() {
+ super.initState();
+ _update();
+ }
+
+ @override
+ void reassemble() {
+ super.reassemble();
+ _update();
+ }
+
+ static WidgetLibrary _createLocalWidgets() {
+ return LocalWidgetLibrary(<String, LocalWidgetBuilder>{
+ 'GreenBox': (BuildContext context, DataSource source) {
+ return Container(
+ color: const Color(0xFF002211),
+ child: source.child(<Object>['child']),
+ );
+ },
+ 'Hello': (BuildContext context, DataSource source) {
+ return Center(
+ child: Text(
+ 'Hello, ${source.v<String>(<Object>["name"])}!',
+ textDirection: TextDirection.ltr,
+ ),
+ );
+ },
+ });
+ }
+
+ static const LibraryName localName = LibraryName(<String>['local']);
+ static const LibraryName remoteName = LibraryName(<String>['remote']);
+
+ void _update() {
+ _runtime.update(localName, _createLocalWidgets());
+ // Normally we would obtain the remote widget library in binary form from a
+ // server, and decode it with [decodeLibraryBlob] rather than parsing the
+ // text version using [parseLibraryFile]. However, to make it easier to
+ // play with this sample, this uses the slower text format.
+ _runtime.update(remoteName, parseLibraryFile('''
+ import local;
+ widget root = GreenBox(
+ child: Hello(name: "World"),
+ );
+ '''));
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return RemoteWidget(
+ runtime: _runtime,
+ data: _data,
+ widget: const FullyQualifiedWidgetName(remoteName, 'root'),
+ onEvent: (String name, DynamicMap arguments) {
+ debugPrint('user triggered event "$name" with data: $arguments');
+ },
+ );
+ }
+}
diff --git a/packages/rfw/example/local/linux/.gitignore b/packages/rfw/example/local/linux/.gitignore
new file mode 100644
index 0000000..d3896c9
--- /dev/null
+++ b/packages/rfw/example/local/linux/.gitignore
@@ -0,0 +1 @@
+flutter/ephemeral
diff --git a/packages/rfw/example/local/linux/CMakeLists.txt b/packages/rfw/example/local/linux/CMakeLists.txt
new file mode 100644
index 0000000..a88ab3c
--- /dev/null
+++ b/packages/rfw/example/local/linux/CMakeLists.txt
@@ -0,0 +1,116 @@
+cmake_minimum_required(VERSION 3.10)
+project(runner LANGUAGES CXX)
+
+set(BINARY_NAME "local")
+set(APPLICATION_ID "dev.flutter.rfw.examples.local")
+
+cmake_policy(SET CMP0063 NEW)
+
+set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
+
+# Root filesystem for cross-building.
+if(FLUTTER_TARGET_PLATFORM_SYSROOT)
+ set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
+ set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
+ set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
+ set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+ set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+endif()
+
+# Configure build options.
+if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+ set(CMAKE_BUILD_TYPE "Debug" CACHE
+ STRING "Flutter build mode" FORCE)
+ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
+ "Debug" "Profile" "Release")
+endif()
+
+# Compilation settings that should be applied to most targets.
+function(APPLY_STANDARD_SETTINGS TARGET)
+ target_compile_features(${TARGET} PUBLIC cxx_std_14)
+ target_compile_options(${TARGET} PRIVATE -Wall -Werror)
+ target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
+ target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
+endfunction()
+
+set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
+
+# Flutter library and tool build rules.
+add_subdirectory(${FLUTTER_MANAGED_DIR})
+
+# System-level dependencies.
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
+
+add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
+
+# Application build
+add_executable(${BINARY_NAME}
+ "main.cc"
+ "my_application.cc"
+ "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
+)
+apply_standard_settings(${BINARY_NAME})
+target_link_libraries(${BINARY_NAME} PRIVATE flutter)
+target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
+add_dependencies(${BINARY_NAME} flutter_assemble)
+# Only the install-generated bundle's copy of the executable will launch
+# correctly, since the resources must in the right relative locations. To avoid
+# people trying to run the unbundled copy, put it in a subdirectory instead of
+# the default top-level location.
+set_target_properties(${BINARY_NAME}
+ PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
+)
+
+# Generated plugin build rules, which manage building the plugins and adding
+# them to the application.
+include(flutter/generated_plugins.cmake)
+
+
+# === Installation ===
+# By default, "installing" just makes a relocatable bundle in the build
+# directory.
+set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
+if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+ set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
+endif()
+
+# Start with a clean build bundle directory every time.
+install(CODE "
+ file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
+ " COMPONENT Runtime)
+
+set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
+set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
+
+install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+
+if(PLUGIN_BUNDLED_LIBRARIES)
+ install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
+ DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endif()
+
+# Fully re-copy the assets directory on each build to avoid having stale files
+# from a previous install.
+set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
+install(CODE "
+ file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
+ " COMPONENT Runtime)
+install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
+ DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
+
+# Install the AOT library on non-Debug builds only.
+if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
+ install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endif()
diff --git a/packages/rfw/example/local/linux/flutter/CMakeLists.txt b/packages/rfw/example/local/linux/flutter/CMakeLists.txt
new file mode 100644
index 0000000..33fd580
--- /dev/null
+++ b/packages/rfw/example/local/linux/flutter/CMakeLists.txt
@@ -0,0 +1,87 @@
+cmake_minimum_required(VERSION 3.10)
+
+set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
+
+# Configuration provided via flutter tool.
+include(${EPHEMERAL_DIR}/generated_config.cmake)
+
+# TODO: Move the rest of this into files in ephemeral. See
+# https://github.com/flutter/flutter/issues/57146.
+
+# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
+# which isn't available in 3.10.
+function(list_prepend LIST_NAME PREFIX)
+ set(NEW_LIST "")
+ foreach(element ${${LIST_NAME}})
+ list(APPEND NEW_LIST "${PREFIX}${element}")
+ endforeach(element)
+ set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
+endfunction()
+
+# === Flutter Library ===
+# System-level dependencies.
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
+pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
+pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
+
+set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
+
+# Published to parent scope for install step.
+set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
+set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
+set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
+set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
+
+list(APPEND FLUTTER_LIBRARY_HEADERS
+ "fl_basic_message_channel.h"
+ "fl_binary_codec.h"
+ "fl_binary_messenger.h"
+ "fl_dart_project.h"
+ "fl_engine.h"
+ "fl_json_message_codec.h"
+ "fl_json_method_codec.h"
+ "fl_message_codec.h"
+ "fl_method_call.h"
+ "fl_method_channel.h"
+ "fl_method_codec.h"
+ "fl_method_response.h"
+ "fl_plugin_registrar.h"
+ "fl_plugin_registry.h"
+ "fl_standard_message_codec.h"
+ "fl_standard_method_codec.h"
+ "fl_string_codec.h"
+ "fl_value.h"
+ "fl_view.h"
+ "flutter_linux.h"
+)
+list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
+add_library(flutter INTERFACE)
+target_include_directories(flutter INTERFACE
+ "${EPHEMERAL_DIR}"
+)
+target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
+target_link_libraries(flutter INTERFACE
+ PkgConfig::GTK
+ PkgConfig::GLIB
+ PkgConfig::GIO
+)
+add_dependencies(flutter flutter_assemble)
+
+# === Flutter tool backend ===
+# _phony_ is a non-existent file to force this command to run every time,
+# since currently there's no way to get a full input/output list from the
+# flutter tool.
+add_custom_command(
+ OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
+ ${CMAKE_CURRENT_BINARY_DIR}/_phony_
+ COMMAND ${CMAKE_COMMAND} -E env
+ ${FLUTTER_TOOL_ENVIRONMENT}
+ "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
+ ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}
+ VERBATIM
+)
+add_custom_target(flutter_assemble DEPENDS
+ "${FLUTTER_LIBRARY}"
+ ${FLUTTER_LIBRARY_HEADERS}
+)
diff --git a/packages/rfw/example/local/linux/flutter/generated_plugins.cmake b/packages/rfw/example/local/linux/flutter/generated_plugins.cmake
new file mode 100644
index 0000000..51436ae
--- /dev/null
+++ b/packages/rfw/example/local/linux/flutter/generated_plugins.cmake
@@ -0,0 +1,15 @@
+#
+# Generated file, do not edit.
+#
+
+list(APPEND FLUTTER_PLUGIN_LIST
+)
+
+set(PLUGIN_BUNDLED_LIBRARIES)
+
+foreach(plugin ${FLUTTER_PLUGIN_LIST})
+ add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
+ target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
+endforeach(plugin)
diff --git a/packages/rfw/example/local/linux/main.cc b/packages/rfw/example/local/linux/main.cc
new file mode 100644
index 0000000..1507d02
--- /dev/null
+++ b/packages/rfw/example/local/linux/main.cc
@@ -0,0 +1,10 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "my_application.h"
+
+int main(int argc, char** argv) {
+ g_autoptr(MyApplication) app = my_application_new();
+ return g_application_run(G_APPLICATION(app), argc, argv);
+}
diff --git a/packages/rfw/example/local/linux/my_application.cc b/packages/rfw/example/local/linux/my_application.cc
new file mode 100644
index 0000000..98a9b29
--- /dev/null
+++ b/packages/rfw/example/local/linux/my_application.cc
@@ -0,0 +1,111 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "my_application.h"
+
+#include <flutter_linux/flutter_linux.h>
+#ifdef GDK_WINDOWING_X11
+#include <gdk/gdkx.h>
+#endif
+
+#include "flutter/generated_plugin_registrant.h"
+
+struct _MyApplication {
+ GtkApplication parent_instance;
+ char** dart_entrypoint_arguments;
+};
+
+G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
+
+// Implements GApplication::activate.
+static void my_application_activate(GApplication* application) {
+ MyApplication* self = MY_APPLICATION(application);
+ GtkWindow* window =
+ GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
+
+ // Use a header bar when running in GNOME as this is the common style used
+ // by applications and is the setup most users will be using (e.g. Ubuntu
+ // desktop).
+ // If running on X and not using GNOME then just use a traditional title bar
+ // in case the window manager does more exotic layout, e.g. tiling.
+ // If running on Wayland assume the header bar will work (may need changing
+ // if future cases occur).
+ gboolean use_header_bar = TRUE;
+#ifdef GDK_WINDOWING_X11
+ GdkScreen* screen = gtk_window_get_screen(window);
+ if (GDK_IS_X11_SCREEN(screen)) {
+ const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
+ if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
+ use_header_bar = FALSE;
+ }
+ }
+#endif
+ if (use_header_bar) {
+ GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
+ gtk_widget_show(GTK_WIDGET(header_bar));
+ gtk_header_bar_set_title(header_bar, "local");
+ gtk_header_bar_set_show_close_button(header_bar, TRUE);
+ gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
+ } else {
+ gtk_window_set_title(window, "local");
+ }
+
+ gtk_window_set_default_size(window, 1280, 720);
+ gtk_widget_show(GTK_WIDGET(window));
+
+ g_autoptr(FlDartProject) project = fl_dart_project_new();
+ fl_dart_project_set_dart_entrypoint_arguments(
+ project, self->dart_entrypoint_arguments);
+
+ FlView* view = fl_view_new(project);
+ gtk_widget_show(GTK_WIDGET(view));
+ gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
+
+ fl_register_plugins(FL_PLUGIN_REGISTRY(view));
+
+ gtk_widget_grab_focus(GTK_WIDGET(view));
+}
+
+// Implements GApplication::local_command_line.
+static gboolean my_application_local_command_line(GApplication* application,
+ gchar*** arguments,
+ int* exit_status) {
+ MyApplication* self = MY_APPLICATION(application);
+ // Strip out the first argument as it is the binary name.
+ self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
+
+ g_autoptr(GError) error = nullptr;
+ if (!g_application_register(application, nullptr, &error)) {
+ g_warning("Failed to register: %s", error->message);
+ *exit_status = 1;
+ return TRUE;
+ }
+
+ g_application_activate(application);
+ *exit_status = 0;
+
+ return TRUE;
+}
+
+// Implements GObject::dispose.
+static void my_application_dispose(GObject* object) {
+ MyApplication* self = MY_APPLICATION(object);
+ g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
+ G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
+}
+
+static void my_application_class_init(MyApplicationClass* klass) {
+ G_APPLICATION_CLASS(klass)->activate = my_application_activate;
+ G_APPLICATION_CLASS(klass)->local_command_line =
+ my_application_local_command_line;
+ G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
+}
+
+static void my_application_init(MyApplication* self) {}
+
+MyApplication* my_application_new() {
+ return MY_APPLICATION(g_object_new(my_application_get_type(),
+ "application-id", APPLICATION_ID, "flags",
+ G_APPLICATION_NON_UNIQUE, nullptr));
+}
diff --git a/packages/rfw/example/local/linux/my_application.h b/packages/rfw/example/local/linux/my_application.h
new file mode 100644
index 0000000..6e9f0c3
--- /dev/null
+++ b/packages/rfw/example/local/linux/my_application.h
@@ -0,0 +1,22 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_MY_APPLICATION_H_
+#define FLUTTER_MY_APPLICATION_H_
+
+#include <gtk/gtk.h>
+
+G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
+ GtkApplication)
+
+/**
+ * my_application_new:
+ *
+ * Creates a new Flutter-based application.
+ *
+ * Returns: a new #MyApplication.
+ */
+MyApplication* my_application_new();
+
+#endif // FLUTTER_MY_APPLICATION_H_
diff --git a/packages/rfw/example/local/macos/.gitignore b/packages/rfw/example/local/macos/.gitignore
new file mode 100644
index 0000000..d2fd377
--- /dev/null
+++ b/packages/rfw/example/local/macos/.gitignore
@@ -0,0 +1,6 @@
+# Flutter-related
+**/Flutter/ephemeral/
+**/Pods/
+
+# Xcode-related
+**/xcuserdata/
diff --git a/packages/rfw/example/local/macos/Flutter/Flutter-Debug.xcconfig b/packages/rfw/example/local/macos/Flutter/Flutter-Debug.xcconfig
new file mode 100644
index 0000000..c2efd0b
--- /dev/null
+++ b/packages/rfw/example/local/macos/Flutter/Flutter-Debug.xcconfig
@@ -0,0 +1 @@
+#include "ephemeral/Flutter-Generated.xcconfig"
diff --git a/packages/rfw/example/local/macos/Flutter/Flutter-Release.xcconfig b/packages/rfw/example/local/macos/Flutter/Flutter-Release.xcconfig
new file mode 100644
index 0000000..c2efd0b
--- /dev/null
+++ b/packages/rfw/example/local/macos/Flutter/Flutter-Release.xcconfig
@@ -0,0 +1 @@
+#include "ephemeral/Flutter-Generated.xcconfig"
diff --git a/packages/rfw/example/local/macos/Runner.xcodeproj/project.pbxproj b/packages/rfw/example/local/macos/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..43e5e2e
--- /dev/null
+++ b/packages/rfw/example/local/macos/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,572 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 51;
+ objects = {
+
+/* Begin PBXAggregateTarget section */
+ 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */;
+ buildPhases = (
+ 33CC111E2044C6BF0003C045 /* ShellScript */,
+ );
+ dependencies = (
+ );
+ name = "Flutter Assemble";
+ productName = FLX;
+ };
+/* End PBXAggregateTarget section */
+
+/* Begin PBXBuildFile section */
+ 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
+ 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
+ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
+ 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
+ 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 33CC111A2044C6BA0003C045;
+ remoteInfo = FLX;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 33CC110E2044A8840003C045 /* Bundle Framework */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Bundle Framework";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
+ 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
+ 33CC10ED2044A3C60003C045 /* local.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "local.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
+ 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
+ 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
+ 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = "<group>"; };
+ 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = "<group>"; };
+ 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = "<group>"; };
+ 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = "<group>"; };
+ 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = "<group>"; };
+ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
+ 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
+ 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 33CC10EA2044A3C60003C045 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 33BA886A226E78AF003329D5 /* Configs */ = {
+ isa = PBXGroup;
+ children = (
+ 33E5194F232828860026EE4D /* AppInfo.xcconfig */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 333000ED22D3DE5D00554162 /* Warnings.xcconfig */,
+ );
+ path = Configs;
+ sourceTree = "<group>";
+ };
+ 33CC10E42044A3C60003C045 = {
+ isa = PBXGroup;
+ children = (
+ 33FAB671232836740065AC1E /* Runner */,
+ 33CEB47122A05771004F2AC0 /* Flutter */,
+ 33CC10EE2044A3C60003C045 /* Products */,
+ D73912EC22F37F3D000D13A0 /* Frameworks */,
+ );
+ sourceTree = "<group>";
+ };
+ 33CC10EE2044A3C60003C045 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10ED2044A3C60003C045 /* local.app */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 33CC11242044D66E0003C045 /* Resources */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10F22044A3C60003C045 /* Assets.xcassets */,
+ 33CC10F42044A3C60003C045 /* MainMenu.xib */,
+ 33CC10F72044A3C60003C045 /* Info.plist */,
+ );
+ name = Resources;
+ path = ..;
+ sourceTree = "<group>";
+ };
+ 33CEB47122A05771004F2AC0 /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,
+ 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,
+ 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,
+ 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,
+ );
+ path = Flutter;
+ sourceTree = "<group>";
+ };
+ 33FAB671232836740065AC1E /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10F02044A3C60003C045 /* AppDelegate.swift */,
+ 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,
+ 33E51913231747F40026EE4D /* DebugProfile.entitlements */,
+ 33E51914231749380026EE4D /* Release.entitlements */,
+ 33CC11242044D66E0003C045 /* Resources */,
+ 33BA886A226E78AF003329D5 /* Configs */,
+ );
+ path = Runner;
+ sourceTree = "<group>";
+ };
+ D73912EC22F37F3D000D13A0 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 33CC10EC2044A3C60003C045 /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ 33CC10E92044A3C60003C045 /* Sources */,
+ 33CC10EA2044A3C60003C045 /* Frameworks */,
+ 33CC10EB2044A3C60003C045 /* Resources */,
+ 33CC110E2044A8840003C045 /* Bundle Framework */,
+ 3399D490228B24CF009A79C7 /* ShellScript */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 33CC11202044C79F0003C045 /* PBXTargetDependency */,
+ );
+ name = Runner;
+ productName = Runner;
+ productReference = 33CC10ED2044A3C60003C045 /* local.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 33CC10E52044A3C60003C045 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastSwiftUpdateCheck = 0920;
+ LastUpgradeCheck = 1300;
+ ORGANIZATIONNAME = "";
+ TargetAttributes = {
+ 33CC10EC2044A3C60003C045 = {
+ CreatedOnToolsVersion = 9.2;
+ LastSwiftMigration = 1100;
+ ProvisioningStyle = Automatic;
+ SystemCapabilities = {
+ com.apple.Sandbox = {
+ enabled = 1;
+ };
+ };
+ };
+ 33CC111A2044C6BA0003C045 = {
+ CreatedOnToolsVersion = 9.2;
+ ProvisioningStyle = Manual;
+ };
+ };
+ };
+ buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 33CC10E42044A3C60003C045;
+ productRefGroup = 33CC10EE2044A3C60003C045 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 33CC10EC2044A3C60003C045 /* Runner */,
+ 33CC111A2044C6BA0003C045 /* Flutter Assemble */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 33CC10EB2044A3C60003C045 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,
+ 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 3399D490228B24CF009A79C7 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ );
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n";
+ };
+ 33CC111E2044C6BF0003C045 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ Flutter/ephemeral/FlutterInputs.xcfilelist,
+ );
+ inputPaths = (
+ Flutter/ephemeral/tripwire,
+ );
+ outputFileListPaths = (
+ Flutter/ephemeral/FlutterOutputs.xcfilelist,
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 33CC10E92044A3C60003C045 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,
+ 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,
+ 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 33CC11202044C79F0003C045 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;
+ targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+ 33CC10F42044A3C60003C045 /* MainMenu.xib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 33CC10F52044A3C60003C045 /* Base */,
+ );
+ name = MainMenu.xib;
+ path = Runner;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 338D0CE9231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = macosx;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ };
+ name = Profile;
+ };
+ 338D0CEA231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Profile;
+ };
+ 338D0CEB231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Manual;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Profile;
+ };
+ 33CC10F92044A3C60003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ 33CC10FA2044A3C60003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = macosx;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ };
+ name = Release;
+ };
+ 33CC10FC2044A3C60003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Debug;
+ };
+ 33CC10FD2044A3C60003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Release;
+ };
+ 33CC111C2044C6BA0003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Manual;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ 33CC111D2044C6BA0003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Automatic;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC10F92044A3C60003C045 /* Debug */,
+ 33CC10FA2044A3C60003C045 /* Release */,
+ 338D0CE9231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC10FC2044A3C60003C045 /* Debug */,
+ 33CC10FD2044A3C60003C045 /* Release */,
+ 338D0CEA231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC111C2044C6BA0003C045 /* Debug */,
+ 33CC111D2044C6BA0003C045 /* Release */,
+ 338D0CEB231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 33CC10E52044A3C60003C045 /* Project object */;
+}
diff --git a/packages/rfw/example/local/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/rfw/example/local/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/packages/rfw/example/local/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IDEDidComputeMac32BitWarning</key>
+ <true/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/local/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/rfw/example/local/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 0000000..2aaf7c4
--- /dev/null
+++ b/packages/rfw/example/local/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "1300"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "33CC10EC2044A3C60003C045"
+ BuildableName = "local.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "33CC10EC2044A3C60003C045"
+ BuildableName = "local.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <Testables>
+ </Testables>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "33CC10EC2044A3C60003C045"
+ BuildableName = "local.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Profile"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "33CC10EC2044A3C60003C045"
+ BuildableName = "local.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/packages/rfw/example/local/macos/Runner.xcworkspace/contents.xcworkspacedata b/packages/rfw/example/local/macos/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..1d526a1
--- /dev/null
+++ b/packages/rfw/example/local/macos/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "group:Runner.xcodeproj">
+ </FileRef>
+</Workspace>
diff --git a/packages/rfw/example/local/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/rfw/example/local/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/packages/rfw/example/local/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IDEDidComputeMac32BitWarning</key>
+ <true/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/local/macos/Runner/AppDelegate.swift b/packages/rfw/example/local/macos/Runner/AppDelegate.swift
new file mode 100644
index 0000000..5cec4c4
--- /dev/null
+++ b/packages/rfw/example/local/macos/Runner/AppDelegate.swift
@@ -0,0 +1,13 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import Cocoa
+import FlutterMacOS
+
+@NSApplicationMain
+class AppDelegate: FlutterAppDelegate {
+ override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
+ return true
+ }
+}
diff --git a/packages/rfw/example/local/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/rfw/example/local/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..a2ec33f
--- /dev/null
+++ b/packages/rfw/example/local/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,68 @@
+{
+ "images" : [
+ {
+ "size" : "16x16",
+ "idiom" : "mac",
+ "filename" : "app_icon_16.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "16x16",
+ "idiom" : "mac",
+ "filename" : "app_icon_32.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "32x32",
+ "idiom" : "mac",
+ "filename" : "app_icon_32.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "32x32",
+ "idiom" : "mac",
+ "filename" : "app_icon_64.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "128x128",
+ "idiom" : "mac",
+ "filename" : "app_icon_128.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "128x128",
+ "idiom" : "mac",
+ "filename" : "app_icon_256.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "256x256",
+ "idiom" : "mac",
+ "filename" : "app_icon_256.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "256x256",
+ "idiom" : "mac",
+ "filename" : "app_icon_512.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "app_icon_512.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "app_icon_1024.png",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/packages/rfw/example/local/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/packages/rfw/example/local/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
new file mode 100644
index 0000000..3c4935a
--- /dev/null
+++ b/packages/rfw/example/local/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
Binary files differ
diff --git a/packages/rfw/example/local/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/packages/rfw/example/local/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png
new file mode 100644
index 0000000..ed4cc16
--- /dev/null
+++ b/packages/rfw/example/local/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png
Binary files differ
diff --git a/packages/rfw/example/local/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/packages/rfw/example/local/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png
new file mode 100644
index 0000000..483be61
--- /dev/null
+++ b/packages/rfw/example/local/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png
Binary files differ
diff --git a/packages/rfw/example/local/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/packages/rfw/example/local/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png
new file mode 100644
index 0000000..bcbf36d
--- /dev/null
+++ b/packages/rfw/example/local/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png
Binary files differ
diff --git a/packages/rfw/example/local/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/packages/rfw/example/local/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png
new file mode 100644
index 0000000..9c0a652
--- /dev/null
+++ b/packages/rfw/example/local/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png
Binary files differ
diff --git a/packages/rfw/example/local/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/packages/rfw/example/local/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png
new file mode 100644
index 0000000..e71a726
--- /dev/null
+++ b/packages/rfw/example/local/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png
Binary files differ
diff --git a/packages/rfw/example/local/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/packages/rfw/example/local/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png
new file mode 100644
index 0000000..8a31fe2
--- /dev/null
+++ b/packages/rfw/example/local/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png
Binary files differ
diff --git a/packages/rfw/example/local/macos/Runner/Base.lproj/MainMenu.xib b/packages/rfw/example/local/macos/Runner/Base.lproj/MainMenu.xib
new file mode 100644
index 0000000..537341a
--- /dev/null
+++ b/packages/rfw/example/local/macos/Runner/Base.lproj/MainMenu.xib
@@ -0,0 +1,339 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+ <dependencies>
+ <deployment identifier="macosx"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+ </dependencies>
+ <objects>
+ <customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
+ <connections>
+ <outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
+ </connections>
+ </customObject>
+ <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+ <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+ <customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="Runner" customModuleProvider="target">
+ <connections>
+ <outlet property="applicationMenu" destination="uQy-DD-JDr" id="XBo-yE-nKs"/>
+ <outlet property="mainFlutterWindow" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/>
+ </connections>
+ </customObject>
+ <customObject id="YLy-65-1bz" customClass="NSFontManager"/>
+ <menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
+ <items>
+ <menuItem title="APP_NAME" id="1Xt-HY-uBw">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="APP_NAME" systemMenu="apple" id="uQy-DD-JDr">
+ <items>
+ <menuItem title="About APP_NAME" id="5kV-Vb-QxS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
+ <menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
+ <menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
+ <menuItem title="Services" id="NMo-om-nkz">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
+ <menuItem title="Hide APP_NAME" keyEquivalent="h" id="Olw-nP-bQN">
+ <connections>
+ <action selector="hide:" target="-1" id="PnN-Uc-m68"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Show All" id="Kd2-mp-pUS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
+ <menuItem title="Quit APP_NAME" keyEquivalent="q" id="4sb-4s-VLi">
+ <connections>
+ <action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Edit" id="5QF-Oa-p0T">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Edit" id="W48-6f-4Dl">
+ <items>
+ <menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
+ <connections>
+ <action selector="undo:" target="-1" id="M6e-cu-g7V"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
+ <connections>
+ <action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
+ <menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
+ <connections>
+ <action selector="cut:" target="-1" id="YJe-68-I9s"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
+ <connections>
+ <action selector="copy:" target="-1" id="G1f-GL-Joy"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
+ <connections>
+ <action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Delete" id="pa3-QI-u2k">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
+ <connections>
+ <action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
+ <menuItem title="Find" id="4EN-yA-p0u">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Find" id="1b7-l0-nxx">
+ <items>
+ <menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
+ <connections>
+ <action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
+ <connections>
+ <action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
+ <connections>
+ <action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
+ <connections>
+ <action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
+ <connections>
+ <action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
+ <items>
+ <menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
+ <connections>
+ <action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
+ <connections>
+ <action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
+ <menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Substitutions" id="9ic-FL-obx">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
+ <items>
+ <menuItem title="Show Substitutions" id="z6F-FW-3nz">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
+ <menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smart Quotes" id="hQb-2v-fYv">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smart Dashes" id="rgM-f4-ycn">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smart Links" id="cwL-P1-jid">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Data Detectors" id="tRr-pd-1PS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Text Replacement" id="HFQ-gK-NFA">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Transformations" id="2oI-Rn-ZJC">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Transformations" id="c8a-y6-VQd">
+ <items>
+ <menuItem title="Make Upper Case" id="vmV-6d-7jI">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Make Lower Case" id="d9M-CD-aMd">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Capitalize" id="UEZ-Bs-lqG">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Speech" id="xrE-MZ-jX0">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Speech" id="3rS-ZA-NoH">
+ <items>
+ <menuItem title="Start Speaking" id="Ynk-f8-cLZ">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Stop Speaking" id="Oyz-dy-DGm">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="View" id="H8h-7b-M4v">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="View" id="HyV-fh-RgO">
+ <items>
+ <menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
+ <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+ <connections>
+ <action selector="toggleFullScreen:" target="-1" id="dU3-MA-1Rq"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Window" id="aUF-d1-5bR">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
+ <items>
+ <menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
+ <connections>
+ <action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Zoom" id="R4o-n2-Eq4">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
+ <menuItem title="Bring All to Front" id="LE2-aR-0XJ">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ </items>
+ <point key="canvasLocation" x="142" y="-258"/>
+ </menu>
+ <window title="APP_NAME" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g" customClass="MainFlutterWindow" customModule="Runner" customModuleProvider="target">
+ <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
+ <rect key="contentRect" x="335" y="390" width="800" height="600"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="2560" height="1577"/>
+ <view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
+ <rect key="frame" x="0.0" y="0.0" width="800" height="600"/>
+ <autoresizingMask key="autoresizingMask"/>
+ </view>
+ </window>
+ </objects>
+</document>
diff --git a/packages/rfw/example/local/macos/Runner/Configs/AppInfo.xcconfig b/packages/rfw/example/local/macos/Runner/Configs/AppInfo.xcconfig
new file mode 100644
index 0000000..481593c
--- /dev/null
+++ b/packages/rfw/example/local/macos/Runner/Configs/AppInfo.xcconfig
@@ -0,0 +1,14 @@
+// Application-level settings for the Runner target.
+//
+// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the
+// future. If not, the values below would default to using the project name when this becomes a
+// 'flutter create' template.
+
+// The application's name. By default this is also the title of the Flutter window.
+PRODUCT_NAME = local
+
+// The application's bundle identifier
+PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.rfw.examples.local
+
+// The copyright displayed in application information
+PRODUCT_COPYRIGHT = Copyright © 2021 dev.flutter.rfw.examples. All rights reserved.
diff --git a/packages/rfw/example/local/macos/Runner/Configs/Debug.xcconfig b/packages/rfw/example/local/macos/Runner/Configs/Debug.xcconfig
new file mode 100644
index 0000000..36b0fd9
--- /dev/null
+++ b/packages/rfw/example/local/macos/Runner/Configs/Debug.xcconfig
@@ -0,0 +1,2 @@
+#include "../../Flutter/Flutter-Debug.xcconfig"
+#include "Warnings.xcconfig"
diff --git a/packages/rfw/example/local/macos/Runner/Configs/Release.xcconfig b/packages/rfw/example/local/macos/Runner/Configs/Release.xcconfig
new file mode 100644
index 0000000..dff4f49
--- /dev/null
+++ b/packages/rfw/example/local/macos/Runner/Configs/Release.xcconfig
@@ -0,0 +1,2 @@
+#include "../../Flutter/Flutter-Release.xcconfig"
+#include "Warnings.xcconfig"
diff --git a/packages/rfw/example/local/macos/Runner/Configs/Warnings.xcconfig b/packages/rfw/example/local/macos/Runner/Configs/Warnings.xcconfig
new file mode 100644
index 0000000..42bcbf4
--- /dev/null
+++ b/packages/rfw/example/local/macos/Runner/Configs/Warnings.xcconfig
@@ -0,0 +1,13 @@
+WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings
+GCC_WARN_UNDECLARED_SELECTOR = YES
+CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES
+CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
+CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
+CLANG_WARN_PRAGMA_PACK = YES
+CLANG_WARN_STRICT_PROTOTYPES = YES
+CLANG_WARN_COMMA = YES
+GCC_WARN_STRICT_SELECTOR_MATCH = YES
+CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES
+CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
+GCC_WARN_SHADOW = YES
+CLANG_WARN_UNREACHABLE_CODE = YES
diff --git a/packages/rfw/example/local/macos/Runner/DebugProfile.entitlements b/packages/rfw/example/local/macos/Runner/DebugProfile.entitlements
new file mode 100644
index 0000000..dddb8a3
--- /dev/null
+++ b/packages/rfw/example/local/macos/Runner/DebugProfile.entitlements
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.security.app-sandbox</key>
+ <true/>
+ <key>com.apple.security.cs.allow-jit</key>
+ <true/>
+ <key>com.apple.security.network.server</key>
+ <true/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/local/macos/Runner/Info.plist b/packages/rfw/example/local/macos/Runner/Info.plist
new file mode 100644
index 0000000..4789daa
--- /dev/null
+++ b/packages/rfw/example/local/macos/Runner/Info.plist
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>$(DEVELOPMENT_LANGUAGE)</string>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>$(PRODUCT_NAME)</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>$(FLUTTER_BUILD_NAME)</string>
+ <key>CFBundleVersion</key>
+ <string>$(FLUTTER_BUILD_NUMBER)</string>
+ <key>LSMinimumSystemVersion</key>
+ <string>$(MACOSX_DEPLOYMENT_TARGET)</string>
+ <key>NSHumanReadableCopyright</key>
+ <string>$(PRODUCT_COPYRIGHT)</string>
+ <key>NSMainNibFile</key>
+ <string>MainMenu</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+</dict>
+</plist>
diff --git a/packages/rfw/example/local/macos/Runner/MainFlutterWindow.swift b/packages/rfw/example/local/macos/Runner/MainFlutterWindow.swift
new file mode 100644
index 0000000..32aaeed
--- /dev/null
+++ b/packages/rfw/example/local/macos/Runner/MainFlutterWindow.swift
@@ -0,0 +1,19 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import Cocoa
+import FlutterMacOS
+
+class MainFlutterWindow: NSWindow {
+ override func awakeFromNib() {
+ let flutterViewController = FlutterViewController.init()
+ let windowFrame = self.frame
+ self.contentViewController = flutterViewController
+ self.setFrame(windowFrame, display: true)
+
+ RegisterGeneratedPlugins(registry: flutterViewController)
+
+ super.awakeFromNib()
+ }
+}
diff --git a/packages/rfw/example/local/macos/Runner/Release.entitlements b/packages/rfw/example/local/macos/Runner/Release.entitlements
new file mode 100644
index 0000000..852fa1a
--- /dev/null
+++ b/packages/rfw/example/local/macos/Runner/Release.entitlements
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.security.app-sandbox</key>
+ <true/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/local/pubspec.yaml b/packages/rfw/example/local/pubspec.yaml
new file mode 100644
index 0000000..1ee5e93
--- /dev/null
+++ b/packages/rfw/example/local/pubspec.yaml
@@ -0,0 +1,13 @@
+name: local
+description: Example of new custom local widgets for RFW
+publish_to: 'none' # Remove this line if you wish to publish to pub.dev
+version: 1.0.0+1
+
+environment:
+ sdk: ">=2.13.0 <3.0.0"
+
+dependencies:
+ flutter:
+ sdk: flutter
+ rfw:
+ path: ../../
diff --git a/packages/rfw/example/local/web/favicon.png b/packages/rfw/example/local/web/favicon.png
new file mode 100644
index 0000000..8aaa46a
--- /dev/null
+++ b/packages/rfw/example/local/web/favicon.png
Binary files differ
diff --git a/packages/rfw/example/local/web/icons/Icon-192.png b/packages/rfw/example/local/web/icons/Icon-192.png
new file mode 100644
index 0000000..b749bfe
--- /dev/null
+++ b/packages/rfw/example/local/web/icons/Icon-192.png
Binary files differ
diff --git a/packages/rfw/example/local/web/icons/Icon-512.png b/packages/rfw/example/local/web/icons/Icon-512.png
new file mode 100644
index 0000000..88cfd48
--- /dev/null
+++ b/packages/rfw/example/local/web/icons/Icon-512.png
Binary files differ
diff --git a/packages/rfw/example/local/web/icons/Icon-maskable-192.png b/packages/rfw/example/local/web/icons/Icon-maskable-192.png
new file mode 100644
index 0000000..eb9b4d7
--- /dev/null
+++ b/packages/rfw/example/local/web/icons/Icon-maskable-192.png
Binary files differ
diff --git a/packages/rfw/example/local/web/icons/Icon-maskable-512.png b/packages/rfw/example/local/web/icons/Icon-maskable-512.png
new file mode 100644
index 0000000..d69c566
--- /dev/null
+++ b/packages/rfw/example/local/web/icons/Icon-maskable-512.png
Binary files differ
diff --git a/packages/rfw/example/local/web/index.html b/packages/rfw/example/local/web/index.html
new file mode 100644
index 0000000..6872e67
--- /dev/null
+++ b/packages/rfw/example/local/web/index.html
@@ -0,0 +1,107 @@
+<!DOCTYPE html>
+<!-- Copyright 2013 The Flutter Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file. -->
+<html>
+<head>
+ <!--
+ If you are serving your web app in a path other than the root, change the
+ href value below to reflect the base path you are serving from.
+
+ The path provided below has to start and end with a slash "/" in order for
+ it to work correctly.
+
+ For more details:
+ * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
+
+ This is a placeholder for base href that will be replaced by the value of
+ the `--base-href` argument provided to `flutter build`.
+ -->
+ <base href="$FLUTTER_BASE_HREF">
+
+ <meta charset="UTF-8">
+ <meta content="IE=Edge" http-equiv="X-UA-Compatible">
+ <meta name="description" content="Example of new custom local widgets for RFW">
+
+ <!-- iOS meta tags & icons -->
+ <meta name="apple-mobile-web-app-capable" content="yes">
+ <meta name="apple-mobile-web-app-status-bar-style" content="black">
+ <meta name="apple-mobile-web-app-title" content="local">
+ <link rel="apple-touch-icon" href="icons/Icon-192.png">
+
+ <!-- Favicon -->
+ <link rel="icon" type="image/png" href="favicon.png"/>
+
+ <title>local</title>
+ <link rel="manifest" href="manifest.json">
+</head>
+<body>
+ <!-- This script installs service_worker.js to provide PWA functionality to
+ application. For more information, see:
+ https://developers.google.com/web/fundamentals/primers/service-workers -->
+ <script>
+ var serviceWorkerVersion = null;
+ var scriptLoaded = false;
+ function loadMainDartJs() {
+ if (scriptLoaded) {
+ return;
+ }
+ scriptLoaded = true;
+ var scriptTag = document.createElement('script');
+ scriptTag.src = 'main.dart.js';
+ scriptTag.type = 'application/javascript';
+ document.body.append(scriptTag);
+ }
+
+ if ('serviceWorker' in navigator) {
+ // Service workers are supported. Use them.
+ window.addEventListener('load', function () {
+ // Wait for registration to finish before dropping the <script> tag.
+ // Otherwise, the browser will load the script multiple times,
+ // potentially different versions.
+ var serviceWorkerUrl = 'flutter_service_worker.js?v=' + serviceWorkerVersion;
+ navigator.serviceWorker.register(serviceWorkerUrl)
+ .then((reg) => {
+ function waitForActivation(serviceWorker) {
+ serviceWorker.addEventListener('statechange', () => {
+ if (serviceWorker.state == 'activated') {
+ console.log('Installed new service worker.');
+ loadMainDartJs();
+ }
+ });
+ }
+ if (!reg.active && (reg.installing || reg.waiting)) {
+ // No active web worker and we have installed or are installing
+ // one for the first time. Simply wait for it to activate.
+ waitForActivation(reg.installing || reg.waiting);
+ } else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {
+ // When the app updates the serviceWorkerVersion changes, so we
+ // need to ask the service worker to update.
+ console.log('New service worker available.');
+ reg.update();
+ waitForActivation(reg.installing);
+ } else {
+ // Existing service worker is still good.
+ console.log('Loading app from service worker.');
+ loadMainDartJs();
+ }
+ });
+
+ // If service worker doesn't succeed in a reasonable amount of time,
+ // fallback to plaint <script> tag.
+ setTimeout(() => {
+ if (!scriptLoaded) {
+ console.warn(
+ 'Failed to load app from service worker. Falling back to plain <script> tag.',
+ );
+ loadMainDartJs();
+ }
+ }, 4000);
+ });
+ } else {
+ // Service workers not supported. Just drop the <script> tag.
+ loadMainDartJs();
+ }
+ </script>
+</body>
+</html>
diff --git a/packages/rfw/example/local/web/manifest.json b/packages/rfw/example/local/web/manifest.json
new file mode 100644
index 0000000..9f9a575
--- /dev/null
+++ b/packages/rfw/example/local/web/manifest.json
@@ -0,0 +1,35 @@
+{
+ "name": "local",
+ "short_name": "local",
+ "start_url": ".",
+ "display": "standalone",
+ "background_color": "#0175C2",
+ "theme_color": "#0175C2",
+ "description": "Example of new custom local widgets for RFW",
+ "orientation": "portrait-primary",
+ "prefer_related_applications": false,
+ "icons": [
+ {
+ "src": "icons/Icon-192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ },
+ {
+ "src": "icons/Icon-512.png",
+ "sizes": "512x512",
+ "type": "image/png"
+ },
+ {
+ "src": "icons/Icon-maskable-192.png",
+ "sizes": "192x192",
+ "type": "image/png",
+ "purpose": "maskable"
+ },
+ {
+ "src": "icons/Icon-maskable-512.png",
+ "sizes": "512x512",
+ "type": "image/png",
+ "purpose": "maskable"
+ }
+ ]
+}
diff --git a/packages/rfw/example/local/windows/.gitignore b/packages/rfw/example/local/windows/.gitignore
new file mode 100644
index 0000000..d492d0d
--- /dev/null
+++ b/packages/rfw/example/local/windows/.gitignore
@@ -0,0 +1,17 @@
+flutter/ephemeral/
+
+# Visual Studio user-specific files.
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# Visual Studio build-related files.
+x64/
+x86/
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
diff --git a/packages/rfw/example/local/windows/CMakeLists.txt b/packages/rfw/example/local/windows/CMakeLists.txt
new file mode 100644
index 0000000..5ab772a
--- /dev/null
+++ b/packages/rfw/example/local/windows/CMakeLists.txt
@@ -0,0 +1,95 @@
+cmake_minimum_required(VERSION 3.15)
+project(local LANGUAGES CXX)
+
+set(BINARY_NAME "local")
+
+cmake_policy(SET CMP0063 NEW)
+
+set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
+
+# Configure build options.
+get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+if(IS_MULTICONFIG)
+ set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
+ CACHE STRING "" FORCE)
+else()
+ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+ set(CMAKE_BUILD_TYPE "Debug" CACHE
+ STRING "Flutter build mode" FORCE)
+ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
+ "Debug" "Profile" "Release")
+ endif()
+endif()
+
+set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
+set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
+set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
+set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
+
+# Use Unicode for all projects.
+add_definitions(-DUNICODE -D_UNICODE)
+
+# Compilation settings that should be applied to most targets.
+function(APPLY_STANDARD_SETTINGS TARGET)
+ target_compile_features(${TARGET} PUBLIC cxx_std_17)
+ target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
+ target_compile_options(${TARGET} PRIVATE /EHsc)
+ target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
+ target_compile_definitions(${TARGET} PRIVATE "$<$<CONFIG:Debug>:_DEBUG>")
+endfunction()
+
+set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
+
+# Flutter library and tool build rules.
+add_subdirectory(${FLUTTER_MANAGED_DIR})
+
+# Application build
+add_subdirectory("runner")
+
+# Generated plugin build rules, which manage building the plugins and adding
+# them to the application.
+include(flutter/generated_plugins.cmake)
+
+
+# === Installation ===
+# Support files are copied into place next to the executable, so that it can
+# run in place. This is done instead of making a separate bundle (as on Linux)
+# so that building and running from within Visual Studio will work.
+set(BUILD_BUNDLE_DIR "$<TARGET_FILE_DIR:${BINARY_NAME}>")
+# Make the "install" step default, as it's required to run.
+set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
+if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+ set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
+endif()
+
+set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
+set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
+
+install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+
+if(PLUGIN_BUNDLED_LIBRARIES)
+ install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
+ DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endif()
+
+# Fully re-copy the assets directory on each build to avoid having stale files
+# from a previous install.
+set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
+install(CODE "
+ file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
+ " COMPONENT Runtime)
+install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
+ DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
+
+# Install the AOT library on non-Debug builds only.
+install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ CONFIGURATIONS Profile;Release
+ COMPONENT Runtime)
diff --git a/packages/rfw/example/local/windows/flutter/CMakeLists.txt b/packages/rfw/example/local/windows/flutter/CMakeLists.txt
new file mode 100644
index 0000000..b02c548
--- /dev/null
+++ b/packages/rfw/example/local/windows/flutter/CMakeLists.txt
@@ -0,0 +1,103 @@
+cmake_minimum_required(VERSION 3.15)
+
+set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
+
+# Configuration provided via flutter tool.
+include(${EPHEMERAL_DIR}/generated_config.cmake)
+
+# TODO: Move the rest of this into files in ephemeral. See
+# https://github.com/flutter/flutter/issues/57146.
+set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
+
+# === Flutter Library ===
+set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
+
+# Published to parent scope for install step.
+set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
+set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
+set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
+set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE)
+
+list(APPEND FLUTTER_LIBRARY_HEADERS
+ "flutter_export.h"
+ "flutter_windows.h"
+ "flutter_messenger.h"
+ "flutter_plugin_registrar.h"
+ "flutter_texture_registrar.h"
+)
+list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/")
+add_library(flutter INTERFACE)
+target_include_directories(flutter INTERFACE
+ "${EPHEMERAL_DIR}"
+)
+target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib")
+add_dependencies(flutter flutter_assemble)
+
+# === Wrapper ===
+list(APPEND CPP_WRAPPER_SOURCES_CORE
+ "core_implementations.cc"
+ "standard_codec.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
+list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
+ "plugin_registrar.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
+list(APPEND CPP_WRAPPER_SOURCES_APP
+ "flutter_engine.cc"
+ "flutter_view_controller.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
+
+# Wrapper sources needed for a plugin.
+add_library(flutter_wrapper_plugin STATIC
+ ${CPP_WRAPPER_SOURCES_CORE}
+ ${CPP_WRAPPER_SOURCES_PLUGIN}
+)
+apply_standard_settings(flutter_wrapper_plugin)
+set_target_properties(flutter_wrapper_plugin PROPERTIES
+ POSITION_INDEPENDENT_CODE ON)
+set_target_properties(flutter_wrapper_plugin PROPERTIES
+ CXX_VISIBILITY_PRESET hidden)
+target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
+target_include_directories(flutter_wrapper_plugin PUBLIC
+ "${WRAPPER_ROOT}/include"
+)
+add_dependencies(flutter_wrapper_plugin flutter_assemble)
+
+# Wrapper sources needed for the runner.
+add_library(flutter_wrapper_app STATIC
+ ${CPP_WRAPPER_SOURCES_CORE}
+ ${CPP_WRAPPER_SOURCES_APP}
+)
+apply_standard_settings(flutter_wrapper_app)
+target_link_libraries(flutter_wrapper_app PUBLIC flutter)
+target_include_directories(flutter_wrapper_app PUBLIC
+ "${WRAPPER_ROOT}/include"
+)
+add_dependencies(flutter_wrapper_app flutter_assemble)
+
+# === Flutter tool backend ===
+# _phony_ is a non-existent file to force this command to run every time,
+# since currently there's no way to get a full input/output list from the
+# flutter tool.
+set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_")
+set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE)
+add_custom_command(
+ OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
+ ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
+ ${CPP_WRAPPER_SOURCES_APP}
+ ${PHONY_OUTPUT}
+ COMMAND ${CMAKE_COMMAND} -E env
+ ${FLUTTER_TOOL_ENVIRONMENT}
+ "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
+ windows-x64 $<CONFIG>
+ VERBATIM
+)
+add_custom_target(flutter_assemble DEPENDS
+ "${FLUTTER_LIBRARY}"
+ ${FLUTTER_LIBRARY_HEADERS}
+ ${CPP_WRAPPER_SOURCES_CORE}
+ ${CPP_WRAPPER_SOURCES_PLUGIN}
+ ${CPP_WRAPPER_SOURCES_APP}
+)
diff --git a/packages/rfw/example/local/windows/flutter/generated_plugins.cmake b/packages/rfw/example/local/windows/flutter/generated_plugins.cmake
new file mode 100644
index 0000000..4d10c25
--- /dev/null
+++ b/packages/rfw/example/local/windows/flutter/generated_plugins.cmake
@@ -0,0 +1,15 @@
+#
+# Generated file, do not edit.
+#
+
+list(APPEND FLUTTER_PLUGIN_LIST
+)
+
+set(PLUGIN_BUNDLED_LIBRARIES)
+
+foreach(plugin ${FLUTTER_PLUGIN_LIST})
+ add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})
+ target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
+endforeach(plugin)
diff --git a/packages/rfw/example/local/windows/runner/CMakeLists.txt b/packages/rfw/example/local/windows/runner/CMakeLists.txt
new file mode 100644
index 0000000..0b899a0
--- /dev/null
+++ b/packages/rfw/example/local/windows/runner/CMakeLists.txt
@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 3.15)
+project(runner LANGUAGES CXX)
+
+add_executable(${BINARY_NAME} WIN32
+ "flutter_window.cpp"
+ "main.cpp"
+ "utils.cpp"
+ "win32_window.cpp"
+ "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
+ "Runner.rc"
+ "runner.exe.manifest"
+)
+apply_standard_settings(${BINARY_NAME})
+target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
+target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
+target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
+add_dependencies(${BINARY_NAME} flutter_assemble)
diff --git a/packages/rfw/example/local/windows/runner/Runner.rc b/packages/rfw/example/local/windows/runner/Runner.rc
new file mode 100644
index 0000000..0ca2e22
--- /dev/null
+++ b/packages/rfw/example/local/windows/runner/Runner.rc
@@ -0,0 +1,121 @@
+// Microsoft Visual C++ generated resource script.
+//
+#pragma code_page(65001)
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (United States) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""winres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_APP_ICON ICON "resources\\app_icon.ico"
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+#ifdef FLUTTER_BUILD_NUMBER
+#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER
+#else
+#define VERSION_AS_NUMBER 1,0,0
+#endif
+
+#ifdef FLUTTER_BUILD_NAME
+#define VERSION_AS_STRING #FLUTTER_BUILD_NAME
+#else
+#define VERSION_AS_STRING "1.0.0"
+#endif
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VERSION_AS_NUMBER
+ PRODUCTVERSION VERSION_AS_NUMBER
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+#ifdef _DEBUG
+ FILEFLAGS VS_FF_DEBUG
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_APP
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "CompanyName", "dev.flutter.rfw.examples" "\0"
+ VALUE "FileDescription", "Example of new custom local widgets for RFW" "\0"
+ VALUE "FileVersion", VERSION_AS_STRING "\0"
+ VALUE "InternalName", "local" "\0"
+ VALUE "LegalCopyright", "Copyright (C) 2021 dev.flutter.rfw.examples. All rights reserved." "\0"
+ VALUE "OriginalFilename", "local.exe" "\0"
+ VALUE "ProductName", "local" "\0"
+ VALUE "ProductVersion", VERSION_AS_STRING "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252
+ END
+END
+
+#endif // English (United States) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
diff --git a/packages/rfw/example/local/windows/runner/flutter_window.cpp b/packages/rfw/example/local/windows/runner/flutter_window.cpp
new file mode 100644
index 0000000..8254bd9
--- /dev/null
+++ b/packages/rfw/example/local/windows/runner/flutter_window.cpp
@@ -0,0 +1,65 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "flutter_window.h"
+
+#include <optional>
+
+#include "flutter/generated_plugin_registrant.h"
+
+FlutterWindow::FlutterWindow(const flutter::DartProject& project)
+ : project_(project) {}
+
+FlutterWindow::~FlutterWindow() {}
+
+bool FlutterWindow::OnCreate() {
+ if (!Win32Window::OnCreate()) {
+ return false;
+ }
+
+ RECT frame = GetClientArea();
+
+ // The size here must match the window dimensions to avoid unnecessary surface
+ // creation / destruction in the startup path.
+ flutter_controller_ = std::make_unique<flutter::FlutterViewController>(
+ frame.right - frame.left, frame.bottom - frame.top, project_);
+ // Ensure that basic setup of the controller was successful.
+ if (!flutter_controller_->engine() || !flutter_controller_->view()) {
+ return false;
+ }
+ RegisterPlugins(flutter_controller_->engine());
+ SetChildContent(flutter_controller_->view()->GetNativeWindow());
+ return true;
+}
+
+void FlutterWindow::OnDestroy() {
+ if (flutter_controller_) {
+ flutter_controller_ = nullptr;
+ }
+
+ Win32Window::OnDestroy();
+}
+
+LRESULT
+FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept {
+ // Give Flutter, including plugins, an opportunity to handle window messages.
+ if (flutter_controller_) {
+ std::optional<LRESULT> result =
+ flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
+ lparam);
+ if (result) {
+ return *result;
+ }
+ }
+
+ switch (message) {
+ case WM_FONTCHANGE:
+ flutter_controller_->engine()->ReloadSystemFonts();
+ break;
+ }
+
+ return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
+}
diff --git a/packages/rfw/example/local/windows/runner/flutter_window.h b/packages/rfw/example/local/windows/runner/flutter_window.h
new file mode 100644
index 0000000..f1fc669
--- /dev/null
+++ b/packages/rfw/example/local/windows/runner/flutter_window.h
@@ -0,0 +1,37 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef RUNNER_FLUTTER_WINDOW_H_
+#define RUNNER_FLUTTER_WINDOW_H_
+
+#include <flutter/dart_project.h>
+#include <flutter/flutter_view_controller.h>
+
+#include <memory>
+
+#include "win32_window.h"
+
+// A window that does nothing but host a Flutter view.
+class FlutterWindow : public Win32Window {
+ public:
+ // Creates a new FlutterWindow hosting a Flutter view running |project|.
+ explicit FlutterWindow(const flutter::DartProject& project);
+ virtual ~FlutterWindow();
+
+ protected:
+ // Win32Window:
+ bool OnCreate() override;
+ void OnDestroy() override;
+ LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
+ LPARAM const lparam) noexcept override;
+
+ private:
+ // The project to run.
+ flutter::DartProject project_;
+
+ // The Flutter instance hosted by this window.
+ std::unique_ptr<flutter::FlutterViewController> flutter_controller_;
+};
+
+#endif // RUNNER_FLUTTER_WINDOW_H_
diff --git a/packages/rfw/example/local/windows/runner/main.cpp b/packages/rfw/example/local/windows/runner/main.cpp
new file mode 100644
index 0000000..2807ba9
--- /dev/null
+++ b/packages/rfw/example/local/windows/runner/main.cpp
@@ -0,0 +1,46 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <flutter/dart_project.h>
+#include <flutter/flutter_view_controller.h>
+#include <windows.h>
+
+#include "flutter_window.h"
+#include "utils.h"
+
+int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
+ _In_ wchar_t *command_line, _In_ int show_command) {
+ // Attach to console when present (e.g., 'flutter run') or create a
+ // new console when running with a debugger.
+ if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
+ CreateAndAttachConsole();
+ }
+
+ // Initialize COM, so that it is available for use in the library and/or
+ // plugins.
+ ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
+
+ flutter::DartProject project(L"data");
+
+ std::vector<std::string> command_line_arguments = GetCommandLineArguments();
+
+ project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
+
+ FlutterWindow window(project);
+ Win32Window::Point origin(10, 10);
+ Win32Window::Size size(1280, 720);
+ if (!window.CreateAndShow(L"local", origin, size)) {
+ return EXIT_FAILURE;
+ }
+ window.SetQuitOnClose(true);
+
+ ::MSG msg;
+ while (::GetMessage(&msg, nullptr, 0, 0)) {
+ ::TranslateMessage(&msg);
+ ::DispatchMessage(&msg);
+ }
+
+ ::CoUninitialize();
+ return EXIT_SUCCESS;
+}
diff --git a/packages/rfw/example/local/windows/runner/resource.h b/packages/rfw/example/local/windows/runner/resource.h
new file mode 100644
index 0000000..d5d958d
--- /dev/null
+++ b/packages/rfw/example/local/windows/runner/resource.h
@@ -0,0 +1,16 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by Runner.rc
+//
+#define IDI_APP_ICON 101
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 102
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/packages/rfw/example/local/windows/runner/resources/app_icon.ico b/packages/rfw/example/local/windows/runner/resources/app_icon.ico
new file mode 100644
index 0000000..c04e20c
--- /dev/null
+++ b/packages/rfw/example/local/windows/runner/resources/app_icon.ico
Binary files differ
diff --git a/packages/rfw/example/local/windows/runner/runner.exe.manifest b/packages/rfw/example/local/windows/runner/runner.exe.manifest
new file mode 100644
index 0000000..c977c4a
--- /dev/null
+++ b/packages/rfw/example/local/windows/runner/runner.exe.manifest
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <application xmlns="urn:schemas-microsoft-com:asm.v3">
+ <windowsSettings>
+ <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
+ </windowsSettings>
+ </application>
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <!-- Windows 10 -->
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
+ <!-- Windows 8.1 -->
+ <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
+ <!-- Windows 8 -->
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
+ <!-- Windows 7 -->
+ <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
+ </application>
+ </compatibility>
+</assembly>
diff --git a/packages/rfw/example/local/windows/runner/utils.cpp b/packages/rfw/example/local/windows/runner/utils.cpp
new file mode 100644
index 0000000..fb7e945
--- /dev/null
+++ b/packages/rfw/example/local/windows/runner/utils.cpp
@@ -0,0 +1,67 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "utils.h"
+
+#include <flutter_windows.h>
+#include <io.h>
+#include <stdio.h>
+#include <windows.h>
+
+#include <iostream>
+
+void CreateAndAttachConsole() {
+ if (::AllocConsole()) {
+ FILE* unused;
+ if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
+ _dup2(_fileno(stdout), 1);
+ }
+ if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
+ _dup2(_fileno(stdout), 2);
+ }
+ std::ios::sync_with_stdio();
+ FlutterDesktopResyncOutputStreams();
+ }
+}
+
+std::vector<std::string> GetCommandLineArguments() {
+ // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.
+ int argc;
+ wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
+ if (argv == nullptr) {
+ return std::vector<std::string>();
+ }
+
+ std::vector<std::string> command_line_arguments;
+
+ // Skip the first argument as it's the binary name.
+ for (int i = 1; i < argc; i++) {
+ command_line_arguments.push_back(Utf8FromUtf16(argv[i]));
+ }
+
+ ::LocalFree(argv);
+
+ return command_line_arguments;
+}
+
+std::string Utf8FromUtf16(const wchar_t* utf16_string) {
+ if (utf16_string == nullptr) {
+ return std::string();
+ }
+ int target_length =
+ ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1,
+ nullptr, 0, nullptr, nullptr);
+ if (target_length == 0) {
+ return std::string();
+ }
+ std::string utf8_string;
+ utf8_string.resize(target_length);
+ int converted_length = ::WideCharToMultiByte(
+ CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, utf8_string.data(),
+ target_length, nullptr, nullptr);
+ if (converted_length == 0) {
+ return std::string();
+ }
+ return utf8_string;
+}
diff --git a/packages/rfw/example/local/windows/runner/utils.h b/packages/rfw/example/local/windows/runner/utils.h
new file mode 100644
index 0000000..bd81e1e
--- /dev/null
+++ b/packages/rfw/example/local/windows/runner/utils.h
@@ -0,0 +1,23 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef RUNNER_UTILS_H_
+#define RUNNER_UTILS_H_
+
+#include <string>
+#include <vector>
+
+// Creates a console for the process, and redirects stdout and stderr to
+// it for both the runner and the Flutter library.
+void CreateAndAttachConsole();
+
+// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string
+// encoded in UTF-8. Returns an empty std::string on failure.
+std::string Utf8FromUtf16(const wchar_t* utf16_string);
+
+// Gets the command line arguments passed in as a std::vector<std::string>,
+// encoded in UTF-8. Returns an empty std::vector<std::string> on failure.
+std::vector<std::string> GetCommandLineArguments();
+
+#endif // RUNNER_UTILS_H_
diff --git a/packages/rfw/example/local/windows/runner/win32_window.cpp b/packages/rfw/example/local/windows/runner/win32_window.cpp
new file mode 100644
index 0000000..85aa361
--- /dev/null
+++ b/packages/rfw/example/local/windows/runner/win32_window.cpp
@@ -0,0 +1,241 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "win32_window.h"
+
+#include <flutter_windows.h>
+
+#include "resource.h"
+
+namespace {
+
+constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
+
+// The number of Win32Window objects that currently exist.
+static int g_active_window_count = 0;
+
+using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
+
+// Scale helper to convert logical scaler values to physical using passed in
+// scale factor
+int Scale(int source, double scale_factor) {
+ return static_cast<int>(source * scale_factor);
+}
+
+// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
+// This API is only needed for PerMonitor V1 awareness mode.
+void EnableFullDpiSupportIfAvailable(HWND hwnd) {
+ HMODULE user32_module = LoadLibraryA("User32.dll");
+ if (!user32_module) {
+ return;
+ }
+ auto enable_non_client_dpi_scaling =
+ reinterpret_cast<EnableNonClientDpiScaling*>(
+ GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
+ if (enable_non_client_dpi_scaling != nullptr) {
+ enable_non_client_dpi_scaling(hwnd);
+ FreeLibrary(user32_module);
+ }
+}
+
+} // namespace
+
+// Manages the Win32Window's window class registration.
+class WindowClassRegistrar {
+ public:
+ ~WindowClassRegistrar() = default;
+
+ // Returns the singleton registar instance.
+ static WindowClassRegistrar* GetInstance() {
+ if (!instance_) {
+ instance_ = new WindowClassRegistrar();
+ }
+ return instance_;
+ }
+
+ // Returns the name of the window class, registering the class if it hasn't
+ // previously been registered.
+ const wchar_t* GetWindowClass();
+
+ // Unregisters the window class. Should only be called if there are no
+ // instances of the window.
+ void UnregisterWindowClass();
+
+ private:
+ WindowClassRegistrar() = default;
+
+ static WindowClassRegistrar* instance_;
+
+ bool class_registered_ = false;
+};
+
+WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;
+
+const wchar_t* WindowClassRegistrar::GetWindowClass() {
+ if (!class_registered_) {
+ WNDCLASS window_class{};
+ window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
+ window_class.lpszClassName = kWindowClassName;
+ window_class.style = CS_HREDRAW | CS_VREDRAW;
+ window_class.cbClsExtra = 0;
+ window_class.cbWndExtra = 0;
+ window_class.hInstance = GetModuleHandle(nullptr);
+ window_class.hIcon =
+ LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
+ window_class.hbrBackground = 0;
+ window_class.lpszMenuName = nullptr;
+ window_class.lpfnWndProc = Win32Window::WndProc;
+ RegisterClass(&window_class);
+ class_registered_ = true;
+ }
+ return kWindowClassName;
+}
+
+void WindowClassRegistrar::UnregisterWindowClass() {
+ UnregisterClass(kWindowClassName, nullptr);
+ class_registered_ = false;
+}
+
+Win32Window::Win32Window() { ++g_active_window_count; }
+
+Win32Window::~Win32Window() {
+ --g_active_window_count;
+ Destroy();
+}
+
+bool Win32Window::CreateAndShow(const std::wstring& title, const Point& origin,
+ const Size& size) {
+ Destroy();
+
+ const wchar_t* window_class =
+ WindowClassRegistrar::GetInstance()->GetWindowClass();
+
+ const POINT target_point = {static_cast<LONG>(origin.x),
+ static_cast<LONG>(origin.y)};
+ HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
+ UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
+ double scale_factor = dpi / 96.0;
+
+ HWND window = CreateWindow(
+ window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
+ Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
+ Scale(size.width, scale_factor), Scale(size.height, scale_factor),
+ nullptr, nullptr, GetModuleHandle(nullptr), this);
+
+ if (!window) {
+ return false;
+ }
+
+ return OnCreate();
+}
+
+// static
+LRESULT CALLBACK Win32Window::WndProc(HWND const window, UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept {
+ if (message == WM_NCCREATE) {
+ auto window_struct = reinterpret_cast<CREATESTRUCT*>(lparam);
+ SetWindowLongPtr(window, GWLP_USERDATA,
+ reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));
+
+ auto that = static_cast<Win32Window*>(window_struct->lpCreateParams);
+ EnableFullDpiSupportIfAvailable(window);
+ that->window_handle_ = window;
+ } else if (Win32Window* that = GetThisFromHandle(window)) {
+ return that->MessageHandler(window, message, wparam, lparam);
+ }
+
+ return DefWindowProc(window, message, wparam, lparam);
+}
+
+LRESULT
+Win32Window::MessageHandler(HWND hwnd, UINT const message, WPARAM const wparam,
+ LPARAM const lparam) noexcept {
+ switch (message) {
+ case WM_DESTROY:
+ window_handle_ = nullptr;
+ Destroy();
+ if (quit_on_close_) {
+ PostQuitMessage(0);
+ }
+ return 0;
+
+ case WM_DPICHANGED: {
+ auto newRectSize = reinterpret_cast<RECT*>(lparam);
+ LONG newWidth = newRectSize->right - newRectSize->left;
+ LONG newHeight = newRectSize->bottom - newRectSize->top;
+
+ SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,
+ newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
+
+ return 0;
+ }
+ case WM_SIZE: {
+ RECT rect = GetClientArea();
+ if (child_content_ != nullptr) {
+ // Size and position the child window.
+ MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,
+ rect.bottom - rect.top, TRUE);
+ }
+ return 0;
+ }
+
+ case WM_ACTIVATE:
+ if (child_content_ != nullptr) {
+ SetFocus(child_content_);
+ }
+ return 0;
+ }
+
+ return DefWindowProc(window_handle_, message, wparam, lparam);
+}
+
+void Win32Window::Destroy() {
+ OnDestroy();
+
+ if (window_handle_) {
+ DestroyWindow(window_handle_);
+ window_handle_ = nullptr;
+ }
+ if (g_active_window_count == 0) {
+ WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
+ }
+}
+
+Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
+ return reinterpret_cast<Win32Window*>(
+ GetWindowLongPtr(window, GWLP_USERDATA));
+}
+
+void Win32Window::SetChildContent(HWND content) {
+ child_content_ = content;
+ SetParent(content, window_handle_);
+ RECT frame = GetClientArea();
+
+ MoveWindow(content, frame.left, frame.top, frame.right - frame.left,
+ frame.bottom - frame.top, true);
+
+ SetFocus(child_content_);
+}
+
+RECT Win32Window::GetClientArea() {
+ RECT frame;
+ GetClientRect(window_handle_, &frame);
+ return frame;
+}
+
+HWND Win32Window::GetHandle() { return window_handle_; }
+
+void Win32Window::SetQuitOnClose(bool quit_on_close) {
+ quit_on_close_ = quit_on_close;
+}
+
+bool Win32Window::OnCreate() {
+ // No-op; provided for subclasses.
+ return true;
+}
+
+void Win32Window::OnDestroy() {
+ // No-op; provided for subclasses.
+}
diff --git a/packages/rfw/example/local/windows/runner/win32_window.h b/packages/rfw/example/local/windows/runner/win32_window.h
new file mode 100644
index 0000000..d2a7300
--- /dev/null
+++ b/packages/rfw/example/local/windows/runner/win32_window.h
@@ -0,0 +1,99 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef RUNNER_WIN32_WINDOW_H_
+#define RUNNER_WIN32_WINDOW_H_
+
+#include <windows.h>
+
+#include <functional>
+#include <memory>
+#include <string>
+
+// A class abstraction for a high DPI-aware Win32 Window. Intended to be
+// inherited from by classes that wish to specialize with custom
+// rendering and input handling
+class Win32Window {
+ public:
+ struct Point {
+ unsigned int x;
+ unsigned int y;
+ Point(unsigned int x, unsigned int y) : x(x), y(y) {}
+ };
+
+ struct Size {
+ unsigned int width;
+ unsigned int height;
+ Size(unsigned int width, unsigned int height)
+ : width(width), height(height) {}
+ };
+
+ Win32Window();
+ virtual ~Win32Window();
+
+ // Creates and shows a win32 window with |title| and position and size using
+ // |origin| and |size|. New windows are created on the default monitor. Window
+ // sizes are specified to the OS in physical pixels, hence to ensure a
+ // consistent size to will treat the width height passed in to this function
+ // as logical pixels and scale to appropriate for the default monitor. Returns
+ // true if the window was created successfully.
+ bool CreateAndShow(const std::wstring& title, const Point& origin,
+ const Size& size);
+
+ // Release OS resources associated with window.
+ void Destroy();
+
+ // Inserts |content| into the window tree.
+ void SetChildContent(HWND content);
+
+ // Returns the backing Window handle to enable clients to set icon and other
+ // window properties. Returns nullptr if the window has been destroyed.
+ HWND GetHandle();
+
+ // If true, closing this window will quit the application.
+ void SetQuitOnClose(bool quit_on_close);
+
+ // Return a RECT representing the bounds of the current client area.
+ RECT GetClientArea();
+
+ protected:
+ // Processes and route salient window messages for mouse handling,
+ // size change and DPI. Delegates handling of these to member overloads that
+ // inheriting classes can handle.
+ virtual LRESULT MessageHandler(HWND window, UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept;
+
+ // Called when CreateAndShow is called, allowing subclass window-related
+ // setup. Subclasses should return false if setup fails.
+ virtual bool OnCreate();
+
+ // Called when Destroy is called.
+ virtual void OnDestroy();
+
+ private:
+ friend class WindowClassRegistrar;
+
+ // OS callback called by message pump. Handles the WM_NCCREATE message which
+ // is passed when the non-client area is being created and enables automatic
+ // non-client DPI scaling so that the non-client area automatically
+ // responsponds to changes in DPI. All other messages are handled by
+ // MessageHandler.
+ static LRESULT CALLBACK WndProc(HWND const window, UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept;
+
+ // Retrieves a class instance pointer for |window|
+ static Win32Window* GetThisFromHandle(HWND const window) noexcept;
+
+ bool quit_on_close_ = false;
+
+ // window handle for top level window.
+ HWND window_handle_ = nullptr;
+
+ // window handle for hosted content.
+ HWND child_content_ = nullptr;
+};
+
+#endif // RUNNER_WIN32_WINDOW_H_
diff --git a/packages/rfw/example/remote/.gitignore b/packages/rfw/example/remote/.gitignore
new file mode 100644
index 0000000..0fa6b67
--- /dev/null
+++ b/packages/rfw/example/remote/.gitignore
@@ -0,0 +1,46 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+**/ios/Flutter/.last_build_id
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+.packages
+.pub-cache/
+.pub/
+/build/
+
+# Web related
+lib/generated_plugin_registrant.dart
+
+# Symbolication related
+app.*.symbols
+
+# Obfuscation related
+app.*.map.json
+
+# Android Studio will place build artifacts here
+/android/app/debug
+/android/app/profile
+/android/app/release
diff --git a/packages/rfw/example/remote/.metadata b/packages/rfw/example/remote/.metadata
new file mode 100644
index 0000000..45c494e
--- /dev/null
+++ b/packages/rfw/example/remote/.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: a1cd3f45e02c2429e14630d6cc6de120c3d24bd0
+ channel: master
+
+project_type: app
diff --git a/packages/rfw/example/remote/.pluginToolsConfig.yaml b/packages/rfw/example/remote/.pluginToolsConfig.yaml
new file mode 100644
index 0000000..0be940a
--- /dev/null
+++ b/packages/rfw/example/remote/.pluginToolsConfig.yaml
@@ -0,0 +1,3 @@
+buildFlags:
+ global:
+ - "--no-tree-shake-icons"
diff --git a/packages/rfw/example/remote/README.md b/packages/rfw/example/remote/README.md
new file mode 100644
index 0000000..7db02e7
--- /dev/null
+++ b/packages/rfw/example/remote/README.md
@@ -0,0 +1,4 @@
+# Example of fetching remote widgets for RFW
+
+This example shows how one can fetch remote widget library files from
+a remote server.
diff --git a/packages/rfw/example/remote/android/.gitignore b/packages/rfw/example/remote/android/.gitignore
new file mode 100644
index 0000000..6f56801
--- /dev/null
+++ b/packages/rfw/example/remote/android/.gitignore
@@ -0,0 +1,13 @@
+gradle-wrapper.jar
+/.gradle
+/captures/
+/gradlew
+/gradlew.bat
+/local.properties
+GeneratedPluginRegistrant.java
+
+# Remember to never publicly share your keystore.
+# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
+key.properties
+**/*.keystore
+**/*.jks
diff --git a/packages/rfw/example/remote/android/app/build.gradle b/packages/rfw/example/remote/android/app/build.gradle
new file mode 100644
index 0000000..2797d2c
--- /dev/null
+++ b/packages/rfw/example/remote/android/app/build.gradle
@@ -0,0 +1,68 @@
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+ localPropertiesFile.withReader('UTF-8') { reader ->
+ localProperties.load(reader)
+ }
+}
+
+def flutterRoot = localProperties.getProperty('flutter.sdk')
+if (flutterRoot == null) {
+ throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
+}
+
+def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
+if (flutterVersionCode == null) {
+ flutterVersionCode = '1'
+}
+
+def flutterVersionName = localProperties.getProperty('flutter.versionName')
+if (flutterVersionName == null) {
+ flutterVersionName = '1.0'
+}
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
+
+android {
+ compileSdkVersion 30
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+
+ defaultConfig {
+ // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
+ applicationId "dev.flutter.rfw.examples.remote"
+ minSdkVersion 16
+ targetSdkVersion 30
+ versionCode flutterVersionCode.toInteger()
+ versionName flutterVersionName
+ }
+
+ buildTypes {
+ release {
+ // TODO: Add your own signing config for the release build.
+ // Signing with the debug keys for now, so `flutter run --release` works.
+ signingConfig signingConfigs.debug
+ }
+ }
+}
+
+flutter {
+ source '../..'
+}
+
+dependencies {
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+}
diff --git a/packages/rfw/example/remote/android/app/src/debug/AndroidManifest.xml b/packages/rfw/example/remote/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 0000000..3b7fd23
--- /dev/null
+++ b/packages/rfw/example/remote/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,7 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dev.flutter.rfw.examples.remote">
+ <!-- Flutter needs it to communicate with the running application
+ to allow setting breakpoints, to provide hot reload, etc.
+ -->
+ <uses-permission android:name="android.permission.INTERNET"/>
+</manifest>
diff --git a/packages/rfw/example/remote/android/app/src/main/AndroidManifest.xml b/packages/rfw/example/remote/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..cdb8d36
--- /dev/null
+++ b/packages/rfw/example/remote/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dev.flutter.rfw.examples.remote">
+ <application
+ android:label="remote"
+ android:icon="@mipmap/ic_launcher">
+ <activity
+ android:name=".MainActivity"
+ android:launchMode="singleTop"
+ android:theme="@style/LaunchTheme"
+ android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
+ android:hardwareAccelerated="true"
+ android:windowSoftInputMode="adjustResize">
+ <!-- Specifies an Android theme to apply to this Activity as soon as
+ the Android process has started. This theme is visible to the user
+ while the Flutter UI initializes. After that, this theme continues
+ to determine the Window background behind the Flutter UI. -->
+ <meta-data
+ android:name="io.flutter.embedding.android.NormalTheme"
+ android:resource="@style/NormalTheme"
+ />
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <!-- Don't delete the meta-data below.
+ This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
+ <meta-data
+ android:name="flutterEmbedding"
+ android:value="2" />
+ </application>
+</manifest>
diff --git a/packages/rfw/example/remote/android/app/src/main/kotlin/dev/flutter/rfw/examples/remote/MainActivity.kt b/packages/rfw/example/remote/android/app/src/main/kotlin/dev/flutter/rfw/examples/remote/MainActivity.kt
new file mode 100644
index 0000000..7eb9506
--- /dev/null
+++ b/packages/rfw/example/remote/android/app/src/main/kotlin/dev/flutter/rfw/examples/remote/MainActivity.kt
@@ -0,0 +1,6 @@
+package dev.flutter.rfw.examples.remote
+
+import io.flutter.embedding.android.FlutterActivity
+
+class MainActivity: FlutterActivity() {
+}
diff --git a/packages/rfw/example/remote/android/app/src/main/res/drawable-v21/launch_background.xml b/packages/rfw/example/remote/android/app/src/main/res/drawable-v21/launch_background.xml
new file mode 100644
index 0000000..f74085f
--- /dev/null
+++ b/packages/rfw/example/remote/android/app/src/main/res/drawable-v21/launch_background.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Modify this file to customize your launch splash screen -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="?android:colorBackground" />
+
+ <!-- You can insert your own image assets here -->
+ <!-- <item>
+ <bitmap
+ android:gravity="center"
+ android:src="@mipmap/launch_image" />
+ </item> -->
+</layer-list>
diff --git a/packages/rfw/example/remote/android/app/src/main/res/drawable/launch_background.xml b/packages/rfw/example/remote/android/app/src/main/res/drawable/launch_background.xml
new file mode 100644
index 0000000..304732f
--- /dev/null
+++ b/packages/rfw/example/remote/android/app/src/main/res/drawable/launch_background.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Modify this file to customize your launch splash screen -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@android:color/white" />
+
+ <!-- You can insert your own image assets here -->
+ <!-- <item>
+ <bitmap
+ android:gravity="center"
+ android:src="@mipmap/launch_image" />
+ </item> -->
+</layer-list>
diff --git a/packages/rfw/example/remote/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/rfw/example/remote/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..db77bb4
--- /dev/null
+++ b/packages/rfw/example/remote/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/packages/rfw/example/remote/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/rfw/example/remote/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..17987b7
--- /dev/null
+++ b/packages/rfw/example/remote/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/packages/rfw/example/remote/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/packages/rfw/example/remote/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..09d4391
--- /dev/null
+++ b/packages/rfw/example/remote/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/packages/rfw/example/remote/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/rfw/example/remote/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..d5f1c8d
--- /dev/null
+++ b/packages/rfw/example/remote/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/packages/rfw/example/remote/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/rfw/example/remote/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4d6372e
--- /dev/null
+++ b/packages/rfw/example/remote/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/packages/rfw/example/remote/android/app/src/main/res/values-night/styles.xml b/packages/rfw/example/remote/android/app/src/main/res/values-night/styles.xml
new file mode 100644
index 0000000..449a9f9
--- /dev/null
+++ b/packages/rfw/example/remote/android/app/src/main/res/values-night/styles.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
+ <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
+ <!-- Show a splash screen on the activity. Automatically removed when
+ Flutter draws its first frame -->
+ <item name="android:windowBackground">@drawable/launch_background</item>
+ </style>
+ <!-- Theme applied to the Android Window as soon as the process has started.
+ This theme determines the color of the Android Window while your
+ Flutter UI initializes, as well as behind your Flutter UI while its
+ running.
+
+ This Theme is only used starting with V2 of Flutter's Android embedding. -->
+ <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
+ <item name="android:windowBackground">?android:colorBackground</item>
+ </style>
+</resources>
diff --git a/packages/rfw/example/remote/android/app/src/main/res/values/styles.xml b/packages/rfw/example/remote/android/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..d74aa35
--- /dev/null
+++ b/packages/rfw/example/remote/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
+ <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
+ <!-- Show a splash screen on the activity. Automatically removed when
+ Flutter draws its first frame -->
+ <item name="android:windowBackground">@drawable/launch_background</item>
+ </style>
+ <!-- Theme applied to the Android Window as soon as the process has started.
+ This theme determines the color of the Android Window while your
+ Flutter UI initializes, as well as behind your Flutter UI while its
+ running.
+
+ This Theme is only used starting with V2 of Flutter's Android embedding. -->
+ <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
+ <item name="android:windowBackground">?android:colorBackground</item>
+ </style>
+</resources>
diff --git a/packages/rfw/example/remote/android/app/src/profile/AndroidManifest.xml b/packages/rfw/example/remote/android/app/src/profile/AndroidManifest.xml
new file mode 100644
index 0000000..3b7fd23
--- /dev/null
+++ b/packages/rfw/example/remote/android/app/src/profile/AndroidManifest.xml
@@ -0,0 +1,7 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dev.flutter.rfw.examples.remote">
+ <!-- Flutter needs it to communicate with the running application
+ to allow setting breakpoints, to provide hot reload, etc.
+ -->
+ <uses-permission android:name="android.permission.INTERNET"/>
+</manifest>
diff --git a/packages/rfw/example/remote/android/build.gradle b/packages/rfw/example/remote/android/build.gradle
new file mode 100644
index 0000000..ed45c65
--- /dev/null
+++ b/packages/rfw/example/remote/android/build.gradle
@@ -0,0 +1,29 @@
+buildscript {
+ ext.kotlin_version = '1.3.50'
+ repositories {
+ google()
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:4.1.0'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.buildDir = '../build'
+subprojects {
+ project.buildDir = "${rootProject.buildDir}/${project.name}"
+ project.evaluationDependsOn(':app')
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/packages/rfw/example/remote/android/gradle.properties b/packages/rfw/example/remote/android/gradle.properties
new file mode 100644
index 0000000..94adc3a
--- /dev/null
+++ b/packages/rfw/example/remote/android/gradle.properties
@@ -0,0 +1,3 @@
+org.gradle.jvmargs=-Xmx1536M
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/packages/rfw/example/remote/android/gradle/wrapper/gradle-wrapper.properties b/packages/rfw/example/remote/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..bc6a58a
--- /dev/null
+++ b/packages/rfw/example/remote/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-6.7-all.zip
diff --git a/packages/rfw/example/remote/android/settings.gradle b/packages/rfw/example/remote/android/settings.gradle
new file mode 100644
index 0000000..44e62bc
--- /dev/null
+++ b/packages/rfw/example/remote/android/settings.gradle
@@ -0,0 +1,11 @@
+include ':app'
+
+def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
+def properties = new Properties()
+
+assert localPropertiesFile.exists()
+localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
+
+def flutterSdkPath = properties.getProperty("flutter.sdk")
+assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
diff --git a/packages/rfw/example/remote/ios/.gitignore b/packages/rfw/example/remote/ios/.gitignore
new file mode 100644
index 0000000..151026b
--- /dev/null
+++ b/packages/rfw/example/remote/ios/.gitignore
@@ -0,0 +1,33 @@
+*.mode1v3
+*.mode2v3
+*.moved-aside
+*.pbxuser
+*.perspectivev3
+**/*sync/
+.sconsign.dblite
+.tags*
+**/.vagrant/
+**/DerivedData/
+Icon?
+**/Pods/
+**/.symlinks/
+profile
+xcuserdata
+**/.generated/
+Flutter/App.framework
+Flutter/Flutter.framework
+Flutter/Flutter.podspec
+Flutter/Generated.xcconfig
+Flutter/ephemeral/
+Flutter/app.flx
+Flutter/app.zip
+Flutter/flutter_assets/
+Flutter/flutter_export_environment.sh
+ServiceDefinitions.json
+Runner/GeneratedPluginRegistrant.*
+
+# Exceptions to above rules.
+!default.mode1v3
+!default.mode2v3
+!default.pbxuser
+!default.perspectivev3
diff --git a/packages/rfw/example/remote/ios/Flutter/AppFrameworkInfo.plist b/packages/rfw/example/remote/ios/Flutter/AppFrameworkInfo.plist
new file mode 100644
index 0000000..8d4492f
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Flutter/AppFrameworkInfo.plist
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleExecutable</key>
+ <string>App</string>
+ <key>CFBundleIdentifier</key>
+ <string>io.flutter.flutter.app</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>App</string>
+ <key>CFBundlePackageType</key>
+ <string>FMWK</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>MinimumOSVersion</key>
+ <string>9.0</string>
+</dict>
+</plist>
diff --git a/packages/rfw/example/remote/ios/Flutter/Debug.xcconfig b/packages/rfw/example/remote/ios/Flutter/Debug.xcconfig
new file mode 100644
index 0000000..592ceee
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Flutter/Debug.xcconfig
@@ -0,0 +1 @@
+#include "Generated.xcconfig"
diff --git a/packages/rfw/example/remote/ios/Flutter/Release.xcconfig b/packages/rfw/example/remote/ios/Flutter/Release.xcconfig
new file mode 100644
index 0000000..592ceee
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Flutter/Release.xcconfig
@@ -0,0 +1 @@
+#include "Generated.xcconfig"
diff --git a/packages/rfw/example/remote/ios/Runner.xcodeproj/project.pbxproj b/packages/rfw/example/remote/ios/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..758c2d3
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,481 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 50;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
+ 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
+ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 97C146EB1CF9000F007C117D /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 9740EEB11CF90186004384FC /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */,
+ );
+ name = Flutter;
+ sourceTree = "<group>";
+ };
+ 97C146E51CF9000F007C117D = {
+ isa = PBXGroup;
+ children = (
+ 9740EEB11CF90186004384FC /* Flutter */,
+ 97C146F01CF9000F007C117D /* Runner */,
+ 97C146EF1CF9000F007C117D /* Products */,
+ );
+ sourceTree = "<group>";
+ };
+ 97C146EF1CF9000F007C117D /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146EE1CF9000F007C117D /* Runner.app */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 97C146F01CF9000F007C117D /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146FA1CF9000F007C117D /* Main.storyboard */,
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */,
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
+ 97C147021CF9000F007C117D /* Info.plist */,
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
+ );
+ path = Runner;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 97C146ED1CF9000F007C117D /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ 9740EEB61CF901F6004384FC /* Run Script */,
+ 97C146EA1CF9000F007C117D /* Sources */,
+ 97C146EB1CF9000F007C117D /* Frameworks */,
+ 97C146EC1CF9000F007C117D /* Resources */,
+ 9705A1C41CF9048500538489 /* Embed Frameworks */,
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = Runner;
+ productName = Runner;
+ productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 97C146E61CF9000F007C117D /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 1300;
+ ORGANIZATIONNAME = "";
+ TargetAttributes = {
+ 97C146ED1CF9000F007C117D = {
+ CreatedOnToolsVersion = 7.3.1;
+ LastSwiftMigration = 1100;
+ };
+ };
+ };
+ buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 97C146E51CF9000F007C117D;
+ productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 97C146ED1CF9000F007C117D /* Runner */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 97C146EC1CF9000F007C117D /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Thin Binary";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
+ };
+ 9740EEB61CF901F6004384FC /* Run Script */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Run Script";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 97C146EA1CF9000F007C117D /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C146FB1CF9000F007C117D /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "<group>";
+ };
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C147001CF9000F007C117D /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 249021D3217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Profile;
+ };
+ 249021D4217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.rfw.examples.remote;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Profile;
+ };
+ 97C147031CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 97C147041CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 97C147061CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.rfw.examples.remote;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Debug;
+ };
+ 97C147071CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.rfw.examples.remote;
+ 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/packages/rfw/example/remote/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/rfw/example/remote/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..919434a
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "self:">
+ </FileRef>
+</Workspace>
diff --git a/packages/rfw/example/remote/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/rfw/example/remote/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IDEDidComputeMac32BitWarning</key>
+ <true/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/remote/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/rfw/example/remote/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 0000000..f9b0d7c
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>PreviewsEnabled</key>
+ <false/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/remote/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/rfw/example/remote/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 0000000..c87d15a
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "1300"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+ BuildableName = "Runner.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+ BuildableName = "Runner.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <Testables>
+ </Testables>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+ BuildableName = "Runner.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Profile"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+ BuildableName = "Runner.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/packages/rfw/example/remote/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/rfw/example/remote/ios/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..1d526a1
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "group:Runner.xcodeproj">
+ </FileRef>
+</Workspace>
diff --git a/packages/rfw/example/remote/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/rfw/example/remote/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IDEDidComputeMac32BitWarning</key>
+ <true/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/remote/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/rfw/example/remote/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 0000000..f9b0d7c
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>PreviewsEnabled</key>
+ <false/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/remote/ios/Runner/AppDelegate.swift b/packages/rfw/example/remote/ios/Runner/AppDelegate.swift
new file mode 100644
index 0000000..caf9983
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Runner/AppDelegate.swift
@@ -0,0 +1,17 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+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/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..d36b1fa
--- /dev/null
+++ b/packages/rfw/example/remote/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/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
new file mode 100644
index 0000000..dc9ada4
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
Binary files differ
diff --git a/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
new file mode 100644
index 0000000..28c6bf0
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
Binary files differ
diff --git a/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
new file mode 100644
index 0000000..2ccbfd9
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
Binary files differ
diff --git a/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
new file mode 100644
index 0000000..f091b6b
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
Binary files differ
diff --git a/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
new file mode 100644
index 0000000..4cde121
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
Binary files differ
diff --git a/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
new file mode 100644
index 0000000..d0ef06e
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
Binary files differ
diff --git a/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
new file mode 100644
index 0000000..dcdc230
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
Binary files differ
diff --git a/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
new file mode 100644
index 0000000..2ccbfd9
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
Binary files differ
diff --git a/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
new file mode 100644
index 0000000..c8f9ed8
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
Binary files differ
diff --git a/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
new file mode 100644
index 0000000..a6d6b86
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
Binary files differ
diff --git a/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
new file mode 100644
index 0000000..a6d6b86
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
Binary files differ
diff --git a/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
new file mode 100644
index 0000000..75b2d16
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
Binary files differ
diff --git a/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
new file mode 100644
index 0000000..c4df70d
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
Binary files differ
diff --git a/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
new file mode 100644
index 0000000..6a84f41
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
Binary files differ
diff --git a/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
new file mode 100644
index 0000000..d0e1f58
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
Binary files differ
diff --git a/packages/rfw/example/remote/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
new file mode 100644
index 0000000..0bedcf2
--- /dev/null
+++ b/packages/rfw/example/remote/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/packages/rfw/example/remote/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
new file mode 100644
index 0000000..9da19ea
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
Binary files differ
diff --git a/packages/rfw/example/remote/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
new file mode 100644
index 0000000..9da19ea
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
Binary files differ
diff --git a/packages/rfw/example/remote/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
new file mode 100644
index 0000000..9da19ea
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
Binary files differ
diff --git a/packages/rfw/example/remote/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/packages/rfw/example/remote/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
new file mode 100644
index 0000000..89c2725
--- /dev/null
+++ b/packages/rfw/example/remote/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/packages/rfw/example/remote/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/rfw/example/remote/ios/Runner/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000..f2e259c
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
+ <dependencies>
+ <deployment identifier="iOS"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
+ </dependencies>
+ <scenes>
+ <!--View Controller-->
+ <scene sceneID="EHf-IW-A2E">
+ <objects>
+ <viewController id="01J-lp-oVM" sceneMemberID="viewController">
+ <layoutGuides>
+ <viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
+ <viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
+ </layoutGuides>
+ <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <subviews>
+ <imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
+ </imageView>
+ </subviews>
+ <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+ <constraints>
+ <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
+ <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
+ </constraints>
+ </view>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="53" y="375"/>
+ </scene>
+ </scenes>
+ <resources>
+ <image name="LaunchImage" width="168" height="185"/>
+ </resources>
+</document>
diff --git a/packages/rfw/example/remote/ios/Runner/Base.lproj/Main.storyboard b/packages/rfw/example/remote/ios/Runner/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..f3c2851
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Runner/Base.lproj/Main.storyboard
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
+ <dependencies>
+ <deployment identifier="iOS"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
+ </dependencies>
+ <scenes>
+ <!--Flutter View Controller-->
+ <scene sceneID="tne-QT-ifu">
+ <objects>
+ <viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
+ <layoutGuides>
+ <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
+ <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
+ </layoutGuides>
+ <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
+ <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+ </view>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
+ </objects>
+ </scene>
+ </scenes>
+</document>
diff --git a/packages/rfw/example/remote/ios/Runner/Info.plist b/packages/rfw/example/remote/ios/Runner/Info.plist
new file mode 100644
index 0000000..dcfd841
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Runner/Info.plist
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>$(DEVELOPMENT_LANGUAGE)</string>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>remote</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>$(FLUTTER_BUILD_NAME)</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>$(FLUTTER_BUILD_NUMBER)</string>
+ <key>LSRequiresIPhoneOS</key>
+ <true/>
+ <key>UILaunchStoryboardName</key>
+ <string>LaunchScreen</string>
+ <key>UIMainStoryboardFile</key>
+ <string>Main</string>
+ <key>UISupportedInterfaceOrientations</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+ <key>UISupportedInterfaceOrientations~ipad</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationPortraitUpsideDown</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+ <key>UIViewControllerBasedStatusBarAppearance</key>
+ <false/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/remote/ios/Runner/Runner-Bridging-Header.h b/packages/rfw/example/remote/ios/Runner/Runner-Bridging-Header.h
new file mode 100644
index 0000000..eb7e8ba
--- /dev/null
+++ b/packages/rfw/example/remote/ios/Runner/Runner-Bridging-Header.h
@@ -0,0 +1,5 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "GeneratedPluginRegistrant.h"
diff --git a/packages/rfw/example/remote/lib/main.dart b/packages/rfw/example/remote/lib/main.dart
new file mode 100644
index 0000000..7255d42
--- /dev/null
+++ b/packages/rfw/example/remote/lib/main.dart
@@ -0,0 +1,112 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is hand-formatted.
+
+import 'dart:io';
+
+import 'package:flutter/material.dart';
+import 'package:path/path.dart' as path;
+import 'package:path_provider/path_provider.dart';
+import 'package:rfw/rfw.dart';
+
+const String urlPrefix = 'https://raw.githubusercontent.com/flutter/packages/master/packages/rfw/example/remote/remote_widget_libraries';
+
+void main() {
+ runApp(const MaterialApp(home: Example()));
+}
+
+class Example extends StatefulWidget {
+ const Example({Key? key}) : super(key: key);
+
+ @override
+ State<Example> createState() => _ExampleState();
+}
+
+class _ExampleState extends State<Example> {
+ final Runtime _runtime = Runtime();
+ final DynamicContent _data = DynamicContent();
+
+ bool _ready = false;
+ int _counter = 0;
+
+ @override
+ void initState() {
+ super.initState();
+ _runtime.update(const LibraryName(<String>['core', 'widgets']), createCoreWidgets());
+ _runtime.update(const LibraryName(<String>['core', 'material']), createMaterialWidgets());
+ _updateData();
+ _updateWidgets();
+ }
+
+ void _updateData() {
+ _data.update('counter', _counter.toString());
+ }
+
+ Future<void> _updateWidgets() async {
+ final Directory home = await getApplicationSupportDirectory();
+ final File settingsFile = File(path.join(home.path, 'settings.txt'));
+ String nextFile = 'counter_app1.rfw';
+ if (settingsFile.existsSync()) {
+ final String settings = await settingsFile.readAsString();
+ if (settings == nextFile) {
+ nextFile = 'counter_app2.rfw';
+ }
+ }
+ final File currentFile = File(path.join(home.path, 'current.rfw'));
+ if (currentFile.existsSync()) {
+ try {
+ _runtime.update(const LibraryName(<String>['main']), decodeLibraryBlob(await currentFile.readAsBytes()));
+ setState(() {
+ _ready = true;
+ });
+ } catch (e, stack) {
+ FlutterError.reportError(FlutterErrorDetails(exception: e, stack: stack));
+ }
+ }
+ print('Fetching: $urlPrefix/$nextFile');
+ final HttpClientResponse client = await (await HttpClient().getUrl(Uri.parse('$urlPrefix/$nextFile'))).close();
+ await currentFile.writeAsBytes(await client.expand((List<int> chunk) => chunk).toList());
+ await settingsFile.writeAsString(nextFile);
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final Widget result;
+ if (_ready) {
+ result = RemoteWidget(
+ runtime: _runtime,
+ data: _data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['main']), 'Counter'),
+ onEvent: (String name, DynamicMap arguments) {
+ if (name == 'increment') {
+ _counter += 1;
+ _updateData();
+ }
+ },
+ );
+ } else {
+ result = Material(
+ child: SafeArea(
+ child: Padding(
+ padding: const EdgeInsets.all(20.0),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: const <Widget>[
+ Padding(padding: EdgeInsets.only(right: 100.0), child: Text('REMOTE', textAlign: TextAlign.center, style: TextStyle(letterSpacing: 12.0))),
+ Expanded(child: DecoratedBox(decoration: FlutterLogoDecoration(style: FlutterLogoStyle.horizontal))),
+ Padding(padding: EdgeInsets.only(left: 100.0), child: Text('WIDGETS', textAlign: TextAlign.center, style: TextStyle(letterSpacing: 12.0))),
+ Spacer(),
+ Expanded(child: Text('Every time this program is run, it fetches a new remote widgets library.', textAlign: TextAlign.center)),
+ Expanded(child: Text('The interface that it shows is whatever library was last fetched.', textAlign: TextAlign.center)),
+ Expanded(child: Text('Restart this application to see the new interface!', textAlign: TextAlign.center)),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+ return AnimatedSwitcher(duration: const Duration(milliseconds: 1250), switchOutCurve: Curves.easeOut, switchInCurve: Curves.easeOut, child: result);
+ }
+}
diff --git a/packages/rfw/example/remote/linux/.gitignore b/packages/rfw/example/remote/linux/.gitignore
new file mode 100644
index 0000000..d3896c9
--- /dev/null
+++ b/packages/rfw/example/remote/linux/.gitignore
@@ -0,0 +1 @@
+flutter/ephemeral
diff --git a/packages/rfw/example/remote/linux/CMakeLists.txt b/packages/rfw/example/remote/linux/CMakeLists.txt
new file mode 100644
index 0000000..2f6f76f
--- /dev/null
+++ b/packages/rfw/example/remote/linux/CMakeLists.txt
@@ -0,0 +1,116 @@
+cmake_minimum_required(VERSION 3.10)
+project(runner LANGUAGES CXX)
+
+set(BINARY_NAME "remote")
+set(APPLICATION_ID "dev.flutter.rfw.examples.remote")
+
+cmake_policy(SET CMP0063 NEW)
+
+set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
+
+# Root filesystem for cross-building.
+if(FLUTTER_TARGET_PLATFORM_SYSROOT)
+ set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
+ set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
+ set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
+ set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+ set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+endif()
+
+# Configure build options.
+if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+ set(CMAKE_BUILD_TYPE "Debug" CACHE
+ STRING "Flutter build mode" FORCE)
+ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
+ "Debug" "Profile" "Release")
+endif()
+
+# Compilation settings that should be applied to most targets.
+function(APPLY_STANDARD_SETTINGS TARGET)
+ target_compile_features(${TARGET} PUBLIC cxx_std_14)
+ target_compile_options(${TARGET} PRIVATE -Wall -Werror)
+ target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
+ target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
+endfunction()
+
+set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
+
+# Flutter library and tool build rules.
+add_subdirectory(${FLUTTER_MANAGED_DIR})
+
+# System-level dependencies.
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
+
+add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
+
+# Application build
+add_executable(${BINARY_NAME}
+ "main.cc"
+ "my_application.cc"
+ "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
+)
+apply_standard_settings(${BINARY_NAME})
+target_link_libraries(${BINARY_NAME} PRIVATE flutter)
+target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
+add_dependencies(${BINARY_NAME} flutter_assemble)
+# Only the install-generated bundle's copy of the executable will launch
+# correctly, since the resources must in the right relative locations. To avoid
+# people trying to run the unbundled copy, put it in a subdirectory instead of
+# the default top-level location.
+set_target_properties(${BINARY_NAME}
+ PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
+)
+
+# Generated plugin build rules, which manage building the plugins and adding
+# them to the application.
+include(flutter/generated_plugins.cmake)
+
+
+# === Installation ===
+# By default, "installing" just makes a relocatable bundle in the build
+# directory.
+set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
+if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+ set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
+endif()
+
+# Start with a clean build bundle directory every time.
+install(CODE "
+ file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
+ " COMPONENT Runtime)
+
+set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
+set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
+
+install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+
+if(PLUGIN_BUNDLED_LIBRARIES)
+ install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
+ DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endif()
+
+# Fully re-copy the assets directory on each build to avoid having stale files
+# from a previous install.
+set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
+install(CODE "
+ file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
+ " COMPONENT Runtime)
+install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
+ DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
+
+# Install the AOT library on non-Debug builds only.
+if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
+ install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endif()
diff --git a/packages/rfw/example/remote/linux/flutter/CMakeLists.txt b/packages/rfw/example/remote/linux/flutter/CMakeLists.txt
new file mode 100644
index 0000000..33fd580
--- /dev/null
+++ b/packages/rfw/example/remote/linux/flutter/CMakeLists.txt
@@ -0,0 +1,87 @@
+cmake_minimum_required(VERSION 3.10)
+
+set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
+
+# Configuration provided via flutter tool.
+include(${EPHEMERAL_DIR}/generated_config.cmake)
+
+# TODO: Move the rest of this into files in ephemeral. See
+# https://github.com/flutter/flutter/issues/57146.
+
+# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
+# which isn't available in 3.10.
+function(list_prepend LIST_NAME PREFIX)
+ set(NEW_LIST "")
+ foreach(element ${${LIST_NAME}})
+ list(APPEND NEW_LIST "${PREFIX}${element}")
+ endforeach(element)
+ set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
+endfunction()
+
+# === Flutter Library ===
+# System-level dependencies.
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
+pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
+pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
+
+set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
+
+# Published to parent scope for install step.
+set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
+set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
+set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
+set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
+
+list(APPEND FLUTTER_LIBRARY_HEADERS
+ "fl_basic_message_channel.h"
+ "fl_binary_codec.h"
+ "fl_binary_messenger.h"
+ "fl_dart_project.h"
+ "fl_engine.h"
+ "fl_json_message_codec.h"
+ "fl_json_method_codec.h"
+ "fl_message_codec.h"
+ "fl_method_call.h"
+ "fl_method_channel.h"
+ "fl_method_codec.h"
+ "fl_method_response.h"
+ "fl_plugin_registrar.h"
+ "fl_plugin_registry.h"
+ "fl_standard_message_codec.h"
+ "fl_standard_method_codec.h"
+ "fl_string_codec.h"
+ "fl_value.h"
+ "fl_view.h"
+ "flutter_linux.h"
+)
+list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
+add_library(flutter INTERFACE)
+target_include_directories(flutter INTERFACE
+ "${EPHEMERAL_DIR}"
+)
+target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
+target_link_libraries(flutter INTERFACE
+ PkgConfig::GTK
+ PkgConfig::GLIB
+ PkgConfig::GIO
+)
+add_dependencies(flutter flutter_assemble)
+
+# === Flutter tool backend ===
+# _phony_ is a non-existent file to force this command to run every time,
+# since currently there's no way to get a full input/output list from the
+# flutter tool.
+add_custom_command(
+ OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
+ ${CMAKE_CURRENT_BINARY_DIR}/_phony_
+ COMMAND ${CMAKE_COMMAND} -E env
+ ${FLUTTER_TOOL_ENVIRONMENT}
+ "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
+ ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}
+ VERBATIM
+)
+add_custom_target(flutter_assemble DEPENDS
+ "${FLUTTER_LIBRARY}"
+ ${FLUTTER_LIBRARY_HEADERS}
+)
diff --git a/packages/rfw/example/remote/linux/flutter/generated_plugins.cmake b/packages/rfw/example/remote/linux/flutter/generated_plugins.cmake
new file mode 100644
index 0000000..51436ae
--- /dev/null
+++ b/packages/rfw/example/remote/linux/flutter/generated_plugins.cmake
@@ -0,0 +1,15 @@
+#
+# Generated file, do not edit.
+#
+
+list(APPEND FLUTTER_PLUGIN_LIST
+)
+
+set(PLUGIN_BUNDLED_LIBRARIES)
+
+foreach(plugin ${FLUTTER_PLUGIN_LIST})
+ add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
+ target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
+endforeach(plugin)
diff --git a/packages/rfw/example/remote/linux/main.cc b/packages/rfw/example/remote/linux/main.cc
new file mode 100644
index 0000000..1507d02
--- /dev/null
+++ b/packages/rfw/example/remote/linux/main.cc
@@ -0,0 +1,10 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "my_application.h"
+
+int main(int argc, char** argv) {
+ g_autoptr(MyApplication) app = my_application_new();
+ return g_application_run(G_APPLICATION(app), argc, argv);
+}
diff --git a/packages/rfw/example/remote/linux/my_application.cc b/packages/rfw/example/remote/linux/my_application.cc
new file mode 100644
index 0000000..168ef0d
--- /dev/null
+++ b/packages/rfw/example/remote/linux/my_application.cc
@@ -0,0 +1,111 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "my_application.h"
+
+#include <flutter_linux/flutter_linux.h>
+#ifdef GDK_WINDOWING_X11
+#include <gdk/gdkx.h>
+#endif
+
+#include "flutter/generated_plugin_registrant.h"
+
+struct _MyApplication {
+ GtkApplication parent_instance;
+ char** dart_entrypoint_arguments;
+};
+
+G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
+
+// Implements GApplication::activate.
+static void my_application_activate(GApplication* application) {
+ MyApplication* self = MY_APPLICATION(application);
+ GtkWindow* window =
+ GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
+
+ // Use a header bar when running in GNOME as this is the common style used
+ // by applications and is the setup most users will be using (e.g. Ubuntu
+ // desktop).
+ // If running on X and not using GNOME then just use a traditional title bar
+ // in case the window manager does more exotic layout, e.g. tiling.
+ // If running on Wayland assume the header bar will work (may need changing
+ // if future cases occur).
+ gboolean use_header_bar = TRUE;
+#ifdef GDK_WINDOWING_X11
+ GdkScreen* screen = gtk_window_get_screen(window);
+ if (GDK_IS_X11_SCREEN(screen)) {
+ const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
+ if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
+ use_header_bar = FALSE;
+ }
+ }
+#endif
+ if (use_header_bar) {
+ GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
+ gtk_widget_show(GTK_WIDGET(header_bar));
+ gtk_header_bar_set_title(header_bar, "remote");
+ gtk_header_bar_set_show_close_button(header_bar, TRUE);
+ gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
+ } else {
+ gtk_window_set_title(window, "remote");
+ }
+
+ gtk_window_set_default_size(window, 1280, 720);
+ gtk_widget_show(GTK_WIDGET(window));
+
+ g_autoptr(FlDartProject) project = fl_dart_project_new();
+ fl_dart_project_set_dart_entrypoint_arguments(
+ project, self->dart_entrypoint_arguments);
+
+ FlView* view = fl_view_new(project);
+ gtk_widget_show(GTK_WIDGET(view));
+ gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
+
+ fl_register_plugins(FL_PLUGIN_REGISTRY(view));
+
+ gtk_widget_grab_focus(GTK_WIDGET(view));
+}
+
+// Implements GApplication::local_command_line.
+static gboolean my_application_local_command_line(GApplication* application,
+ gchar*** arguments,
+ int* exit_status) {
+ MyApplication* self = MY_APPLICATION(application);
+ // Strip out the first argument as it is the binary name.
+ self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
+
+ g_autoptr(GError) error = nullptr;
+ if (!g_application_register(application, nullptr, &error)) {
+ g_warning("Failed to register: %s", error->message);
+ *exit_status = 1;
+ return TRUE;
+ }
+
+ g_application_activate(application);
+ *exit_status = 0;
+
+ return TRUE;
+}
+
+// Implements GObject::dispose.
+static void my_application_dispose(GObject* object) {
+ MyApplication* self = MY_APPLICATION(object);
+ g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
+ G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
+}
+
+static void my_application_class_init(MyApplicationClass* klass) {
+ G_APPLICATION_CLASS(klass)->activate = my_application_activate;
+ G_APPLICATION_CLASS(klass)->local_command_line =
+ my_application_local_command_line;
+ G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
+}
+
+static void my_application_init(MyApplication* self) {}
+
+MyApplication* my_application_new() {
+ return MY_APPLICATION(g_object_new(my_application_get_type(),
+ "application-id", APPLICATION_ID, "flags",
+ G_APPLICATION_NON_UNIQUE, nullptr));
+}
diff --git a/packages/rfw/example/remote/linux/my_application.h b/packages/rfw/example/remote/linux/my_application.h
new file mode 100644
index 0000000..6e9f0c3
--- /dev/null
+++ b/packages/rfw/example/remote/linux/my_application.h
@@ -0,0 +1,22 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_MY_APPLICATION_H_
+#define FLUTTER_MY_APPLICATION_H_
+
+#include <gtk/gtk.h>
+
+G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
+ GtkApplication)
+
+/**
+ * my_application_new:
+ *
+ * Creates a new Flutter-based application.
+ *
+ * Returns: a new #MyApplication.
+ */
+MyApplication* my_application_new();
+
+#endif // FLUTTER_MY_APPLICATION_H_
diff --git a/packages/rfw/example/remote/macos/.gitignore b/packages/rfw/example/remote/macos/.gitignore
new file mode 100644
index 0000000..d2fd377
--- /dev/null
+++ b/packages/rfw/example/remote/macos/.gitignore
@@ -0,0 +1,6 @@
+# Flutter-related
+**/Flutter/ephemeral/
+**/Pods/
+
+# Xcode-related
+**/xcuserdata/
diff --git a/packages/rfw/example/remote/macos/Flutter/Flutter-Debug.xcconfig b/packages/rfw/example/remote/macos/Flutter/Flutter-Debug.xcconfig
new file mode 100644
index 0000000..c2efd0b
--- /dev/null
+++ b/packages/rfw/example/remote/macos/Flutter/Flutter-Debug.xcconfig
@@ -0,0 +1 @@
+#include "ephemeral/Flutter-Generated.xcconfig"
diff --git a/packages/rfw/example/remote/macos/Flutter/Flutter-Release.xcconfig b/packages/rfw/example/remote/macos/Flutter/Flutter-Release.xcconfig
new file mode 100644
index 0000000..c2efd0b
--- /dev/null
+++ b/packages/rfw/example/remote/macos/Flutter/Flutter-Release.xcconfig
@@ -0,0 +1 @@
+#include "ephemeral/Flutter-Generated.xcconfig"
diff --git a/packages/rfw/example/remote/macos/Runner.xcodeproj/project.pbxproj b/packages/rfw/example/remote/macos/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..2f5eaa9
--- /dev/null
+++ b/packages/rfw/example/remote/macos/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,572 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 51;
+ objects = {
+
+/* Begin PBXAggregateTarget section */
+ 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */;
+ buildPhases = (
+ 33CC111E2044C6BF0003C045 /* ShellScript */,
+ );
+ dependencies = (
+ );
+ name = "Flutter Assemble";
+ productName = FLX;
+ };
+/* End PBXAggregateTarget section */
+
+/* Begin PBXBuildFile section */
+ 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
+ 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
+ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
+ 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
+ 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 33CC111A2044C6BA0003C045;
+ remoteInfo = FLX;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 33CC110E2044A8840003C045 /* Bundle Framework */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Bundle Framework";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
+ 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
+ 33CC10ED2044A3C60003C045 /* remote.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "remote.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
+ 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
+ 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
+ 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = "<group>"; };
+ 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = "<group>"; };
+ 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = "<group>"; };
+ 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = "<group>"; };
+ 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = "<group>"; };
+ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
+ 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
+ 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 33CC10EA2044A3C60003C045 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 33BA886A226E78AF003329D5 /* Configs */ = {
+ isa = PBXGroup;
+ children = (
+ 33E5194F232828860026EE4D /* AppInfo.xcconfig */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 333000ED22D3DE5D00554162 /* Warnings.xcconfig */,
+ );
+ path = Configs;
+ sourceTree = "<group>";
+ };
+ 33CC10E42044A3C60003C045 = {
+ isa = PBXGroup;
+ children = (
+ 33FAB671232836740065AC1E /* Runner */,
+ 33CEB47122A05771004F2AC0 /* Flutter */,
+ 33CC10EE2044A3C60003C045 /* Products */,
+ D73912EC22F37F3D000D13A0 /* Frameworks */,
+ );
+ sourceTree = "<group>";
+ };
+ 33CC10EE2044A3C60003C045 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10ED2044A3C60003C045 /* remote.app */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 33CC11242044D66E0003C045 /* Resources */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10F22044A3C60003C045 /* Assets.xcassets */,
+ 33CC10F42044A3C60003C045 /* MainMenu.xib */,
+ 33CC10F72044A3C60003C045 /* Info.plist */,
+ );
+ name = Resources;
+ path = ..;
+ sourceTree = "<group>";
+ };
+ 33CEB47122A05771004F2AC0 /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,
+ 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,
+ 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,
+ 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,
+ );
+ path = Flutter;
+ sourceTree = "<group>";
+ };
+ 33FAB671232836740065AC1E /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10F02044A3C60003C045 /* AppDelegate.swift */,
+ 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,
+ 33E51913231747F40026EE4D /* DebugProfile.entitlements */,
+ 33E51914231749380026EE4D /* Release.entitlements */,
+ 33CC11242044D66E0003C045 /* Resources */,
+ 33BA886A226E78AF003329D5 /* Configs */,
+ );
+ path = Runner;
+ sourceTree = "<group>";
+ };
+ D73912EC22F37F3D000D13A0 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 33CC10EC2044A3C60003C045 /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ 33CC10E92044A3C60003C045 /* Sources */,
+ 33CC10EA2044A3C60003C045 /* Frameworks */,
+ 33CC10EB2044A3C60003C045 /* Resources */,
+ 33CC110E2044A8840003C045 /* Bundle Framework */,
+ 3399D490228B24CF009A79C7 /* ShellScript */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 33CC11202044C79F0003C045 /* PBXTargetDependency */,
+ );
+ name = Runner;
+ productName = Runner;
+ productReference = 33CC10ED2044A3C60003C045 /* remote.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 33CC10E52044A3C60003C045 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastSwiftUpdateCheck = 0920;
+ LastUpgradeCheck = 1300;
+ ORGANIZATIONNAME = "";
+ TargetAttributes = {
+ 33CC10EC2044A3C60003C045 = {
+ CreatedOnToolsVersion = 9.2;
+ LastSwiftMigration = 1100;
+ ProvisioningStyle = Automatic;
+ SystemCapabilities = {
+ com.apple.Sandbox = {
+ enabled = 1;
+ };
+ };
+ };
+ 33CC111A2044C6BA0003C045 = {
+ CreatedOnToolsVersion = 9.2;
+ ProvisioningStyle = Manual;
+ };
+ };
+ };
+ buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 33CC10E42044A3C60003C045;
+ productRefGroup = 33CC10EE2044A3C60003C045 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 33CC10EC2044A3C60003C045 /* Runner */,
+ 33CC111A2044C6BA0003C045 /* Flutter Assemble */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 33CC10EB2044A3C60003C045 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,
+ 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 3399D490228B24CF009A79C7 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ );
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n";
+ };
+ 33CC111E2044C6BF0003C045 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ Flutter/ephemeral/FlutterInputs.xcfilelist,
+ );
+ inputPaths = (
+ Flutter/ephemeral/tripwire,
+ );
+ outputFileListPaths = (
+ Flutter/ephemeral/FlutterOutputs.xcfilelist,
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 33CC10E92044A3C60003C045 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,
+ 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,
+ 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 33CC11202044C79F0003C045 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;
+ targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+ 33CC10F42044A3C60003C045 /* MainMenu.xib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 33CC10F52044A3C60003C045 /* Base */,
+ );
+ name = MainMenu.xib;
+ path = Runner;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 338D0CE9231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = macosx;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ };
+ name = Profile;
+ };
+ 338D0CEA231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Profile;
+ };
+ 338D0CEB231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Manual;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Profile;
+ };
+ 33CC10F92044A3C60003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ 33CC10FA2044A3C60003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = macosx;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ };
+ name = Release;
+ };
+ 33CC10FC2044A3C60003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Debug;
+ };
+ 33CC10FD2044A3C60003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Release;
+ };
+ 33CC111C2044C6BA0003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Manual;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ 33CC111D2044C6BA0003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Automatic;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC10F92044A3C60003C045 /* Debug */,
+ 33CC10FA2044A3C60003C045 /* Release */,
+ 338D0CE9231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC10FC2044A3C60003C045 /* Debug */,
+ 33CC10FD2044A3C60003C045 /* Release */,
+ 338D0CEA231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC111C2044C6BA0003C045 /* Debug */,
+ 33CC111D2044C6BA0003C045 /* Release */,
+ 338D0CEB231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 33CC10E52044A3C60003C045 /* Project object */;
+}
diff --git a/packages/rfw/example/remote/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/rfw/example/remote/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/packages/rfw/example/remote/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IDEDidComputeMac32BitWarning</key>
+ <true/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/remote/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/rfw/example/remote/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 0000000..cccb242
--- /dev/null
+++ b/packages/rfw/example/remote/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "1300"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "33CC10EC2044A3C60003C045"
+ BuildableName = "remote.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "33CC10EC2044A3C60003C045"
+ BuildableName = "remote.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <Testables>
+ </Testables>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "33CC10EC2044A3C60003C045"
+ BuildableName = "remote.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Profile"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "33CC10EC2044A3C60003C045"
+ BuildableName = "remote.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/packages/rfw/example/remote/macos/Runner.xcworkspace/contents.xcworkspacedata b/packages/rfw/example/remote/macos/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..1d526a1
--- /dev/null
+++ b/packages/rfw/example/remote/macos/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "group:Runner.xcodeproj">
+ </FileRef>
+</Workspace>
diff --git a/packages/rfw/example/remote/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/rfw/example/remote/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/packages/rfw/example/remote/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IDEDidComputeMac32BitWarning</key>
+ <true/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/remote/macos/Runner/AppDelegate.swift b/packages/rfw/example/remote/macos/Runner/AppDelegate.swift
new file mode 100644
index 0000000..5cec4c4
--- /dev/null
+++ b/packages/rfw/example/remote/macos/Runner/AppDelegate.swift
@@ -0,0 +1,13 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import Cocoa
+import FlutterMacOS
+
+@NSApplicationMain
+class AppDelegate: FlutterAppDelegate {
+ override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
+ return true
+ }
+}
diff --git a/packages/rfw/example/remote/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/rfw/example/remote/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..a2ec33f
--- /dev/null
+++ b/packages/rfw/example/remote/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,68 @@
+{
+ "images" : [
+ {
+ "size" : "16x16",
+ "idiom" : "mac",
+ "filename" : "app_icon_16.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "16x16",
+ "idiom" : "mac",
+ "filename" : "app_icon_32.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "32x32",
+ "idiom" : "mac",
+ "filename" : "app_icon_32.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "32x32",
+ "idiom" : "mac",
+ "filename" : "app_icon_64.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "128x128",
+ "idiom" : "mac",
+ "filename" : "app_icon_128.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "128x128",
+ "idiom" : "mac",
+ "filename" : "app_icon_256.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "256x256",
+ "idiom" : "mac",
+ "filename" : "app_icon_256.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "256x256",
+ "idiom" : "mac",
+ "filename" : "app_icon_512.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "app_icon_512.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "app_icon_1024.png",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/packages/rfw/example/remote/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/packages/rfw/example/remote/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
new file mode 100644
index 0000000..3c4935a
--- /dev/null
+++ b/packages/rfw/example/remote/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
Binary files differ
diff --git a/packages/rfw/example/remote/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/packages/rfw/example/remote/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png
new file mode 100644
index 0000000..ed4cc16
--- /dev/null
+++ b/packages/rfw/example/remote/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png
Binary files differ
diff --git a/packages/rfw/example/remote/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/packages/rfw/example/remote/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png
new file mode 100644
index 0000000..483be61
--- /dev/null
+++ b/packages/rfw/example/remote/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png
Binary files differ
diff --git a/packages/rfw/example/remote/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/packages/rfw/example/remote/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png
new file mode 100644
index 0000000..bcbf36d
--- /dev/null
+++ b/packages/rfw/example/remote/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png
Binary files differ
diff --git a/packages/rfw/example/remote/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/packages/rfw/example/remote/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png
new file mode 100644
index 0000000..9c0a652
--- /dev/null
+++ b/packages/rfw/example/remote/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png
Binary files differ
diff --git a/packages/rfw/example/remote/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/packages/rfw/example/remote/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png
new file mode 100644
index 0000000..e71a726
--- /dev/null
+++ b/packages/rfw/example/remote/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png
Binary files differ
diff --git a/packages/rfw/example/remote/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/packages/rfw/example/remote/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png
new file mode 100644
index 0000000..8a31fe2
--- /dev/null
+++ b/packages/rfw/example/remote/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png
Binary files differ
diff --git a/packages/rfw/example/remote/macos/Runner/Base.lproj/MainMenu.xib b/packages/rfw/example/remote/macos/Runner/Base.lproj/MainMenu.xib
new file mode 100644
index 0000000..537341a
--- /dev/null
+++ b/packages/rfw/example/remote/macos/Runner/Base.lproj/MainMenu.xib
@@ -0,0 +1,339 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+ <dependencies>
+ <deployment identifier="macosx"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+ </dependencies>
+ <objects>
+ <customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
+ <connections>
+ <outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
+ </connections>
+ </customObject>
+ <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+ <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+ <customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="Runner" customModuleProvider="target">
+ <connections>
+ <outlet property="applicationMenu" destination="uQy-DD-JDr" id="XBo-yE-nKs"/>
+ <outlet property="mainFlutterWindow" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/>
+ </connections>
+ </customObject>
+ <customObject id="YLy-65-1bz" customClass="NSFontManager"/>
+ <menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
+ <items>
+ <menuItem title="APP_NAME" id="1Xt-HY-uBw">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="APP_NAME" systemMenu="apple" id="uQy-DD-JDr">
+ <items>
+ <menuItem title="About APP_NAME" id="5kV-Vb-QxS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
+ <menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
+ <menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
+ <menuItem title="Services" id="NMo-om-nkz">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
+ <menuItem title="Hide APP_NAME" keyEquivalent="h" id="Olw-nP-bQN">
+ <connections>
+ <action selector="hide:" target="-1" id="PnN-Uc-m68"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Show All" id="Kd2-mp-pUS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
+ <menuItem title="Quit APP_NAME" keyEquivalent="q" id="4sb-4s-VLi">
+ <connections>
+ <action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Edit" id="5QF-Oa-p0T">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Edit" id="W48-6f-4Dl">
+ <items>
+ <menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
+ <connections>
+ <action selector="undo:" target="-1" id="M6e-cu-g7V"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
+ <connections>
+ <action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
+ <menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
+ <connections>
+ <action selector="cut:" target="-1" id="YJe-68-I9s"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
+ <connections>
+ <action selector="copy:" target="-1" id="G1f-GL-Joy"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
+ <connections>
+ <action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Delete" id="pa3-QI-u2k">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
+ <connections>
+ <action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
+ <menuItem title="Find" id="4EN-yA-p0u">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Find" id="1b7-l0-nxx">
+ <items>
+ <menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
+ <connections>
+ <action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
+ <connections>
+ <action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
+ <connections>
+ <action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
+ <connections>
+ <action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
+ <connections>
+ <action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
+ <items>
+ <menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
+ <connections>
+ <action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
+ <connections>
+ <action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
+ <menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Substitutions" id="9ic-FL-obx">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
+ <items>
+ <menuItem title="Show Substitutions" id="z6F-FW-3nz">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
+ <menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smart Quotes" id="hQb-2v-fYv">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smart Dashes" id="rgM-f4-ycn">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smart Links" id="cwL-P1-jid">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Data Detectors" id="tRr-pd-1PS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Text Replacement" id="HFQ-gK-NFA">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Transformations" id="2oI-Rn-ZJC">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Transformations" id="c8a-y6-VQd">
+ <items>
+ <menuItem title="Make Upper Case" id="vmV-6d-7jI">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Make Lower Case" id="d9M-CD-aMd">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Capitalize" id="UEZ-Bs-lqG">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Speech" id="xrE-MZ-jX0">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Speech" id="3rS-ZA-NoH">
+ <items>
+ <menuItem title="Start Speaking" id="Ynk-f8-cLZ">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Stop Speaking" id="Oyz-dy-DGm">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="View" id="H8h-7b-M4v">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="View" id="HyV-fh-RgO">
+ <items>
+ <menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
+ <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+ <connections>
+ <action selector="toggleFullScreen:" target="-1" id="dU3-MA-1Rq"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Window" id="aUF-d1-5bR">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
+ <items>
+ <menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
+ <connections>
+ <action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Zoom" id="R4o-n2-Eq4">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
+ <menuItem title="Bring All to Front" id="LE2-aR-0XJ">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ </items>
+ <point key="canvasLocation" x="142" y="-258"/>
+ </menu>
+ <window title="APP_NAME" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g" customClass="MainFlutterWindow" customModule="Runner" customModuleProvider="target">
+ <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
+ <rect key="contentRect" x="335" y="390" width="800" height="600"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="2560" height="1577"/>
+ <view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
+ <rect key="frame" x="0.0" y="0.0" width="800" height="600"/>
+ <autoresizingMask key="autoresizingMask"/>
+ </view>
+ </window>
+ </objects>
+</document>
diff --git a/packages/rfw/example/remote/macos/Runner/Configs/AppInfo.xcconfig b/packages/rfw/example/remote/macos/Runner/Configs/AppInfo.xcconfig
new file mode 100644
index 0000000..6d859b0
--- /dev/null
+++ b/packages/rfw/example/remote/macos/Runner/Configs/AppInfo.xcconfig
@@ -0,0 +1,14 @@
+// Application-level settings for the Runner target.
+//
+// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the
+// future. If not, the values below would default to using the project name when this becomes a
+// 'flutter create' template.
+
+// The application's name. By default this is also the title of the Flutter window.
+PRODUCT_NAME = remote
+
+// The application's bundle identifier
+PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.rfw.examples.remote
+
+// The copyright displayed in application information
+PRODUCT_COPYRIGHT = Copyright © 2021 dev.flutter.rfw.examples. All rights reserved.
diff --git a/packages/rfw/example/remote/macos/Runner/Configs/Debug.xcconfig b/packages/rfw/example/remote/macos/Runner/Configs/Debug.xcconfig
new file mode 100644
index 0000000..36b0fd9
--- /dev/null
+++ b/packages/rfw/example/remote/macos/Runner/Configs/Debug.xcconfig
@@ -0,0 +1,2 @@
+#include "../../Flutter/Flutter-Debug.xcconfig"
+#include "Warnings.xcconfig"
diff --git a/packages/rfw/example/remote/macos/Runner/Configs/Release.xcconfig b/packages/rfw/example/remote/macos/Runner/Configs/Release.xcconfig
new file mode 100644
index 0000000..dff4f49
--- /dev/null
+++ b/packages/rfw/example/remote/macos/Runner/Configs/Release.xcconfig
@@ -0,0 +1,2 @@
+#include "../../Flutter/Flutter-Release.xcconfig"
+#include "Warnings.xcconfig"
diff --git a/packages/rfw/example/remote/macos/Runner/Configs/Warnings.xcconfig b/packages/rfw/example/remote/macos/Runner/Configs/Warnings.xcconfig
new file mode 100644
index 0000000..42bcbf4
--- /dev/null
+++ b/packages/rfw/example/remote/macos/Runner/Configs/Warnings.xcconfig
@@ -0,0 +1,13 @@
+WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings
+GCC_WARN_UNDECLARED_SELECTOR = YES
+CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES
+CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
+CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
+CLANG_WARN_PRAGMA_PACK = YES
+CLANG_WARN_STRICT_PROTOTYPES = YES
+CLANG_WARN_COMMA = YES
+GCC_WARN_STRICT_SELECTOR_MATCH = YES
+CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES
+CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
+GCC_WARN_SHADOW = YES
+CLANG_WARN_UNREACHABLE_CODE = YES
diff --git a/packages/rfw/example/remote/macos/Runner/DebugProfile.entitlements b/packages/rfw/example/remote/macos/Runner/DebugProfile.entitlements
new file mode 100644
index 0000000..dddb8a3
--- /dev/null
+++ b/packages/rfw/example/remote/macos/Runner/DebugProfile.entitlements
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.security.app-sandbox</key>
+ <true/>
+ <key>com.apple.security.cs.allow-jit</key>
+ <true/>
+ <key>com.apple.security.network.server</key>
+ <true/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/remote/macos/Runner/Info.plist b/packages/rfw/example/remote/macos/Runner/Info.plist
new file mode 100644
index 0000000..4789daa
--- /dev/null
+++ b/packages/rfw/example/remote/macos/Runner/Info.plist
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>$(DEVELOPMENT_LANGUAGE)</string>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>$(PRODUCT_NAME)</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>$(FLUTTER_BUILD_NAME)</string>
+ <key>CFBundleVersion</key>
+ <string>$(FLUTTER_BUILD_NUMBER)</string>
+ <key>LSMinimumSystemVersion</key>
+ <string>$(MACOSX_DEPLOYMENT_TARGET)</string>
+ <key>NSHumanReadableCopyright</key>
+ <string>$(PRODUCT_COPYRIGHT)</string>
+ <key>NSMainNibFile</key>
+ <string>MainMenu</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+</dict>
+</plist>
diff --git a/packages/rfw/example/remote/macos/Runner/MainFlutterWindow.swift b/packages/rfw/example/remote/macos/Runner/MainFlutterWindow.swift
new file mode 100644
index 0000000..32aaeed
--- /dev/null
+++ b/packages/rfw/example/remote/macos/Runner/MainFlutterWindow.swift
@@ -0,0 +1,19 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import Cocoa
+import FlutterMacOS
+
+class MainFlutterWindow: NSWindow {
+ override func awakeFromNib() {
+ let flutterViewController = FlutterViewController.init()
+ let windowFrame = self.frame
+ self.contentViewController = flutterViewController
+ self.setFrame(windowFrame, display: true)
+
+ RegisterGeneratedPlugins(registry: flutterViewController)
+
+ super.awakeFromNib()
+ }
+}
diff --git a/packages/rfw/example/remote/macos/Runner/Release.entitlements b/packages/rfw/example/remote/macos/Runner/Release.entitlements
new file mode 100644
index 0000000..852fa1a
--- /dev/null
+++ b/packages/rfw/example/remote/macos/Runner/Release.entitlements
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.security.app-sandbox</key>
+ <true/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/remote/pubspec.yaml b/packages/rfw/example/remote/pubspec.yaml
new file mode 100644
index 0000000..82f5890
--- /dev/null
+++ b/packages/rfw/example/remote/pubspec.yaml
@@ -0,0 +1,18 @@
+name: remote
+description: Example of fetching remote widgets for RFW
+publish_to: 'none' # Remove this line if you wish to publish to pub.dev
+version: 1.0.0+1
+
+environment:
+ sdk: ">=2.13.0 <3.0.0"
+
+dependencies:
+ flutter:
+ sdk: flutter
+ path: ^1.8.0
+ path_provider: ^2.0.2
+ rfw:
+ path: ../../
+
+flutter:
+ uses-material-design: true
diff --git a/packages/rfw/example/remote/remote_widget_libraries/README.md b/packages/rfw/example/remote/remote_widget_libraries/README.md
new file mode 100644
index 0000000..3febefa
--- /dev/null
+++ b/packages/rfw/example/remote/remote_widget_libraries/README.md
@@ -0,0 +1,5 @@
+This directory contains the source files (.rfwtxt) for the binary UI
+descriptions (.rfw) that are used by the demo.
+
+To convert the source files to binary files run `encode.dart` in this
+directory.
\ No newline at end of file
diff --git a/packages/rfw/example/remote/remote_widget_libraries/counter_app1.rfw b/packages/rfw/example/remote/remote_widget_libraries/counter_app1.rfw
new file mode 100644
index 0000000..b39a0a1
--- /dev/null
+++ b/packages/rfw/example/remote/remote_widget_libraries/counter_app1.rfw
Binary files differ
diff --git a/packages/rfw/example/remote/remote_widget_libraries/counter_app1.rfwtxt b/packages/rfw/example/remote/remote_widget_libraries/counter_app1.rfwtxt
new file mode 100644
index 0000000..b6ffd07
--- /dev/null
+++ b/packages/rfw/example/remote/remote_widget_libraries/counter_app1.rfwtxt
@@ -0,0 +1,22 @@
+import core.widgets;
+import core.material;
+
+widget Counter = Scaffold(
+ appBar: AppBar(title: Text(text: "Counter Demo")),
+ body: Center(
+ child: Column(
+ mainAxisAlignment: "center",
+ children: [
+ Text(text: 'You have pushed the button this many times:', textAlign: "center"),
+ Text(text: data.counter, style: {
+ fontSize: 36.0,
+ }),
+ ],
+ ),
+ ),
+ floatingActionButton: FloatingActionButton(
+ onPressed: event "increment" { },
+ tooltip: "Increment",
+ child: Icon(icon: 0xE047, fontFamily: "MaterialIcons"),
+ ),
+);
diff --git a/packages/rfw/example/remote/remote_widget_libraries/counter_app2.rfw b/packages/rfw/example/remote/remote_widget_libraries/counter_app2.rfw
new file mode 100644
index 0000000..b7d112b
--- /dev/null
+++ b/packages/rfw/example/remote/remote_widget_libraries/counter_app2.rfw
Binary files differ
diff --git a/packages/rfw/example/remote/remote_widget_libraries/counter_app2.rfwtxt b/packages/rfw/example/remote/remote_widget_libraries/counter_app2.rfwtxt
new file mode 100644
index 0000000..a4fbcee
--- /dev/null
+++ b/packages/rfw/example/remote/remote_widget_libraries/counter_app2.rfwtxt
@@ -0,0 +1,59 @@
+import core.widgets;
+import core.material;
+
+widget Counter = Container(
+ color: 0xFF66AACC,
+ child: Center(
+ child: Button(
+ child: Padding(
+ padding: [ 20.0 ],
+ child: Text(text: data.counter, style: {
+ fontSize: 56.0,
+ color: 0xFF000000,
+ }),
+ ),
+ onPressed: event 'increment' { },
+ ),
+ ),
+);
+
+widget Button { down: false } = GestureDetector(
+ onTap: args.onPressed,
+ onTapDown: set state.down = true,
+ onTapUp: set state.down = false,
+ onTapCancel: set state.down = false,
+ child: Container(
+ duration: 50,
+ margin: switch state.down {
+ false: [ 0.0, 0.0, 2.0, 2.0 ],
+ true: [ 2.0, 2.0, 0.0, 0.0 ],
+ },
+ padding: [ 12.0, 8.0 ],
+ decoration: {
+ type: "shape",
+ shape: {
+ type: "stadium",
+ side: { width: 1.0 },
+ },
+ gradient: {
+ type: "linear",
+ begin: { x: -0.5, y: -0.25 },
+ end: { x: 0.0, y: 0.5 },
+ colors: [ 0xFFFFFF99, 0xFFEEDD00 ],
+ stops: [ 0.0, 1.0 ],
+ tileMode: "mirror",
+ },
+ shadows: switch state.down {
+ false: [ { blurRadius: 4.0, spreadRadius: 0.5, offset: { x: 1.0, y: 1.0, } } ],
+ default: [],
+ },
+ },
+ child: DefaultTextStyle(
+ style: {
+ color: 0xFF000000,
+ fontSize: 32.0,
+ },
+ child: args.child,
+ ),
+ ),
+);
diff --git a/packages/rfw/example/remote/remote_widget_libraries/encode.dart b/packages/rfw/example/remote/remote_widget_libraries/encode.dart
new file mode 100644
index 0000000..a2be602
--- /dev/null
+++ b/packages/rfw/example/remote/remote_widget_libraries/encode.dart
@@ -0,0 +1,16 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is hand-formatted.
+
+import 'dart:io';
+
+import 'package:rfw/formats.dart';
+
+void main() {
+ final String counterApp1 = File('counter_app1.rfwtxt').readAsStringSync();
+ File('counter_app1.rfw').writeAsBytesSync(encodeLibraryBlob(parseLibraryFile(counterApp1)));
+ final String counterApp2 = File('counter_app2.rfwtxt').readAsStringSync();
+ File('counter_app2.rfw').writeAsBytesSync(encodeLibraryBlob(parseLibraryFile(counterApp2)));
+}
diff --git a/packages/rfw/example/remote/web/favicon.png b/packages/rfw/example/remote/web/favicon.png
new file mode 100644
index 0000000..8aaa46a
--- /dev/null
+++ b/packages/rfw/example/remote/web/favicon.png
Binary files differ
diff --git a/packages/rfw/example/remote/web/icons/Icon-192.png b/packages/rfw/example/remote/web/icons/Icon-192.png
new file mode 100644
index 0000000..b749bfe
--- /dev/null
+++ b/packages/rfw/example/remote/web/icons/Icon-192.png
Binary files differ
diff --git a/packages/rfw/example/remote/web/icons/Icon-512.png b/packages/rfw/example/remote/web/icons/Icon-512.png
new file mode 100644
index 0000000..88cfd48
--- /dev/null
+++ b/packages/rfw/example/remote/web/icons/Icon-512.png
Binary files differ
diff --git a/packages/rfw/example/remote/web/icons/Icon-maskable-192.png b/packages/rfw/example/remote/web/icons/Icon-maskable-192.png
new file mode 100644
index 0000000..eb9b4d7
--- /dev/null
+++ b/packages/rfw/example/remote/web/icons/Icon-maskable-192.png
Binary files differ
diff --git a/packages/rfw/example/remote/web/icons/Icon-maskable-512.png b/packages/rfw/example/remote/web/icons/Icon-maskable-512.png
new file mode 100644
index 0000000..d69c566
--- /dev/null
+++ b/packages/rfw/example/remote/web/icons/Icon-maskable-512.png
Binary files differ
diff --git a/packages/rfw/example/remote/web/index.html b/packages/rfw/example/remote/web/index.html
new file mode 100644
index 0000000..2bcc056
--- /dev/null
+++ b/packages/rfw/example/remote/web/index.html
@@ -0,0 +1,107 @@
+<!DOCTYPE html>
+<!-- Copyright 2013 The Flutter Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file. -->
+<html>
+<head>
+ <!--
+ If you are serving your web app in a path other than the root, change the
+ href value below to reflect the base path you are serving from.
+
+ The path provided below has to start and end with a slash "/" in order for
+ it to work correctly.
+
+ For more details:
+ * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
+
+ This is a placeholder for base href that will be replaced by the value of
+ the `--base-href` argument provided to `flutter build`.
+ -->
+ <base href="$FLUTTER_BASE_HREF">
+
+ <meta charset="UTF-8">
+ <meta content="IE=Edge" http-equiv="X-UA-Compatible">
+ <meta name="description" content="Example of fetching remote widgets for RFW">
+
+ <!-- iOS meta tags & icons -->
+ <meta name="apple-mobile-web-app-capable" content="yes">
+ <meta name="apple-mobile-web-app-status-bar-style" content="black">
+ <meta name="apple-mobile-web-app-title" content="remote">
+ <link rel="apple-touch-icon" href="icons/Icon-192.png">
+
+ <!-- Favicon -->
+ <link rel="icon" type="image/png" href="favicon.png"/>
+
+ <title>remote</title>
+ <link rel="manifest" href="manifest.json">
+</head>
+<body>
+ <!-- This script installs service_worker.js to provide PWA functionality to
+ application. For more information, see:
+ https://developers.google.com/web/fundamentals/primers/service-workers -->
+ <script>
+ var serviceWorkerVersion = null;
+ var scriptLoaded = false;
+ function loadMainDartJs() {
+ if (scriptLoaded) {
+ return;
+ }
+ scriptLoaded = true;
+ var scriptTag = document.createElement('script');
+ scriptTag.src = 'main.dart.js';
+ scriptTag.type = 'application/javascript';
+ document.body.append(scriptTag);
+ }
+
+ if ('serviceWorker' in navigator) {
+ // Service workers are supported. Use them.
+ window.addEventListener('load', function () {
+ // Wait for registration to finish before dropping the <script> tag.
+ // Otherwise, the browser will load the script multiple times,
+ // potentially different versions.
+ var serviceWorkerUrl = 'flutter_service_worker.js?v=' + serviceWorkerVersion;
+ navigator.serviceWorker.register(serviceWorkerUrl)
+ .then((reg) => {
+ function waitForActivation(serviceWorker) {
+ serviceWorker.addEventListener('statechange', () => {
+ if (serviceWorker.state == 'activated') {
+ console.log('Installed new service worker.');
+ loadMainDartJs();
+ }
+ });
+ }
+ if (!reg.active && (reg.installing || reg.waiting)) {
+ // No active web worker and we have installed or are installing
+ // one for the first time. Simply wait for it to activate.
+ waitForActivation(reg.installing || reg.waiting);
+ } else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {
+ // When the app updates the serviceWorkerVersion changes, so we
+ // need to ask the service worker to update.
+ console.log('New service worker available.');
+ reg.update();
+ waitForActivation(reg.installing);
+ } else {
+ // Existing service worker is still good.
+ console.log('Loading app from service worker.');
+ loadMainDartJs();
+ }
+ });
+
+ // If service worker doesn't succeed in a reasonable amount of time,
+ // fallback to plaint <script> tag.
+ setTimeout(() => {
+ if (!scriptLoaded) {
+ console.warn(
+ 'Failed to load app from service worker. Falling back to plain <script> tag.',
+ );
+ loadMainDartJs();
+ }
+ }, 4000);
+ });
+ } else {
+ // Service workers not supported. Just drop the <script> tag.
+ loadMainDartJs();
+ }
+ </script>
+</body>
+</html>
diff --git a/packages/rfw/example/remote/web/manifest.json b/packages/rfw/example/remote/web/manifest.json
new file mode 100644
index 0000000..1ef5a87
--- /dev/null
+++ b/packages/rfw/example/remote/web/manifest.json
@@ -0,0 +1,35 @@
+{
+ "name": "remote",
+ "short_name": "remote",
+ "start_url": ".",
+ "display": "standalone",
+ "background_color": "#0175C2",
+ "theme_color": "#0175C2",
+ "description": "Example of fetching remote widgets for RFW",
+ "orientation": "portrait-primary",
+ "prefer_related_applications": false,
+ "icons": [
+ {
+ "src": "icons/Icon-192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ },
+ {
+ "src": "icons/Icon-512.png",
+ "sizes": "512x512",
+ "type": "image/png"
+ },
+ {
+ "src": "icons/Icon-maskable-192.png",
+ "sizes": "192x192",
+ "type": "image/png",
+ "purpose": "maskable"
+ },
+ {
+ "src": "icons/Icon-maskable-512.png",
+ "sizes": "512x512",
+ "type": "image/png",
+ "purpose": "maskable"
+ }
+ ]
+}
diff --git a/packages/rfw/example/remote/windows/.gitignore b/packages/rfw/example/remote/windows/.gitignore
new file mode 100644
index 0000000..d492d0d
--- /dev/null
+++ b/packages/rfw/example/remote/windows/.gitignore
@@ -0,0 +1,17 @@
+flutter/ephemeral/
+
+# Visual Studio user-specific files.
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# Visual Studio build-related files.
+x64/
+x86/
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
diff --git a/packages/rfw/example/remote/windows/CMakeLists.txt b/packages/rfw/example/remote/windows/CMakeLists.txt
new file mode 100644
index 0000000..cb7ce77
--- /dev/null
+++ b/packages/rfw/example/remote/windows/CMakeLists.txt
@@ -0,0 +1,95 @@
+cmake_minimum_required(VERSION 3.15)
+project(remote LANGUAGES CXX)
+
+set(BINARY_NAME "remote")
+
+cmake_policy(SET CMP0063 NEW)
+
+set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
+
+# Configure build options.
+get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+if(IS_MULTICONFIG)
+ set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
+ CACHE STRING "" FORCE)
+else()
+ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+ set(CMAKE_BUILD_TYPE "Debug" CACHE
+ STRING "Flutter build mode" FORCE)
+ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
+ "Debug" "Profile" "Release")
+ endif()
+endif()
+
+set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
+set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
+set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
+set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
+
+# Use Unicode for all projects.
+add_definitions(-DUNICODE -D_UNICODE)
+
+# Compilation settings that should be applied to most targets.
+function(APPLY_STANDARD_SETTINGS TARGET)
+ target_compile_features(${TARGET} PUBLIC cxx_std_17)
+ target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
+ target_compile_options(${TARGET} PRIVATE /EHsc)
+ target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
+ target_compile_definitions(${TARGET} PRIVATE "$<$<CONFIG:Debug>:_DEBUG>")
+endfunction()
+
+set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
+
+# Flutter library and tool build rules.
+add_subdirectory(${FLUTTER_MANAGED_DIR})
+
+# Application build
+add_subdirectory("runner")
+
+# Generated plugin build rules, which manage building the plugins and adding
+# them to the application.
+include(flutter/generated_plugins.cmake)
+
+
+# === Installation ===
+# Support files are copied into place next to the executable, so that it can
+# run in place. This is done instead of making a separate bundle (as on Linux)
+# so that building and running from within Visual Studio will work.
+set(BUILD_BUNDLE_DIR "$<TARGET_FILE_DIR:${BINARY_NAME}>")
+# Make the "install" step default, as it's required to run.
+set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
+if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+ set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
+endif()
+
+set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
+set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
+
+install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+
+if(PLUGIN_BUNDLED_LIBRARIES)
+ install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
+ DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endif()
+
+# Fully re-copy the assets directory on each build to avoid having stale files
+# from a previous install.
+set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
+install(CODE "
+ file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
+ " COMPONENT Runtime)
+install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
+ DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
+
+# Install the AOT library on non-Debug builds only.
+install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ CONFIGURATIONS Profile;Release
+ COMPONENT Runtime)
diff --git a/packages/rfw/example/remote/windows/flutter/CMakeLists.txt b/packages/rfw/example/remote/windows/flutter/CMakeLists.txt
new file mode 100644
index 0000000..b02c548
--- /dev/null
+++ b/packages/rfw/example/remote/windows/flutter/CMakeLists.txt
@@ -0,0 +1,103 @@
+cmake_minimum_required(VERSION 3.15)
+
+set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
+
+# Configuration provided via flutter tool.
+include(${EPHEMERAL_DIR}/generated_config.cmake)
+
+# TODO: Move the rest of this into files in ephemeral. See
+# https://github.com/flutter/flutter/issues/57146.
+set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
+
+# === Flutter Library ===
+set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
+
+# Published to parent scope for install step.
+set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
+set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
+set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
+set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE)
+
+list(APPEND FLUTTER_LIBRARY_HEADERS
+ "flutter_export.h"
+ "flutter_windows.h"
+ "flutter_messenger.h"
+ "flutter_plugin_registrar.h"
+ "flutter_texture_registrar.h"
+)
+list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/")
+add_library(flutter INTERFACE)
+target_include_directories(flutter INTERFACE
+ "${EPHEMERAL_DIR}"
+)
+target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib")
+add_dependencies(flutter flutter_assemble)
+
+# === Wrapper ===
+list(APPEND CPP_WRAPPER_SOURCES_CORE
+ "core_implementations.cc"
+ "standard_codec.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
+list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
+ "plugin_registrar.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
+list(APPEND CPP_WRAPPER_SOURCES_APP
+ "flutter_engine.cc"
+ "flutter_view_controller.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
+
+# Wrapper sources needed for a plugin.
+add_library(flutter_wrapper_plugin STATIC
+ ${CPP_WRAPPER_SOURCES_CORE}
+ ${CPP_WRAPPER_SOURCES_PLUGIN}
+)
+apply_standard_settings(flutter_wrapper_plugin)
+set_target_properties(flutter_wrapper_plugin PROPERTIES
+ POSITION_INDEPENDENT_CODE ON)
+set_target_properties(flutter_wrapper_plugin PROPERTIES
+ CXX_VISIBILITY_PRESET hidden)
+target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
+target_include_directories(flutter_wrapper_plugin PUBLIC
+ "${WRAPPER_ROOT}/include"
+)
+add_dependencies(flutter_wrapper_plugin flutter_assemble)
+
+# Wrapper sources needed for the runner.
+add_library(flutter_wrapper_app STATIC
+ ${CPP_WRAPPER_SOURCES_CORE}
+ ${CPP_WRAPPER_SOURCES_APP}
+)
+apply_standard_settings(flutter_wrapper_app)
+target_link_libraries(flutter_wrapper_app PUBLIC flutter)
+target_include_directories(flutter_wrapper_app PUBLIC
+ "${WRAPPER_ROOT}/include"
+)
+add_dependencies(flutter_wrapper_app flutter_assemble)
+
+# === Flutter tool backend ===
+# _phony_ is a non-existent file to force this command to run every time,
+# since currently there's no way to get a full input/output list from the
+# flutter tool.
+set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_")
+set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE)
+add_custom_command(
+ OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
+ ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
+ ${CPP_WRAPPER_SOURCES_APP}
+ ${PHONY_OUTPUT}
+ COMMAND ${CMAKE_COMMAND} -E env
+ ${FLUTTER_TOOL_ENVIRONMENT}
+ "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
+ windows-x64 $<CONFIG>
+ VERBATIM
+)
+add_custom_target(flutter_assemble DEPENDS
+ "${FLUTTER_LIBRARY}"
+ ${FLUTTER_LIBRARY_HEADERS}
+ ${CPP_WRAPPER_SOURCES_CORE}
+ ${CPP_WRAPPER_SOURCES_PLUGIN}
+ ${CPP_WRAPPER_SOURCES_APP}
+)
diff --git a/packages/rfw/example/remote/windows/flutter/generated_plugins.cmake b/packages/rfw/example/remote/windows/flutter/generated_plugins.cmake
new file mode 100644
index 0000000..4d10c25
--- /dev/null
+++ b/packages/rfw/example/remote/windows/flutter/generated_plugins.cmake
@@ -0,0 +1,15 @@
+#
+# Generated file, do not edit.
+#
+
+list(APPEND FLUTTER_PLUGIN_LIST
+)
+
+set(PLUGIN_BUNDLED_LIBRARIES)
+
+foreach(plugin ${FLUTTER_PLUGIN_LIST})
+ add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})
+ target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
+endforeach(plugin)
diff --git a/packages/rfw/example/remote/windows/runner/CMakeLists.txt b/packages/rfw/example/remote/windows/runner/CMakeLists.txt
new file mode 100644
index 0000000..0b899a0
--- /dev/null
+++ b/packages/rfw/example/remote/windows/runner/CMakeLists.txt
@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 3.15)
+project(runner LANGUAGES CXX)
+
+add_executable(${BINARY_NAME} WIN32
+ "flutter_window.cpp"
+ "main.cpp"
+ "utils.cpp"
+ "win32_window.cpp"
+ "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
+ "Runner.rc"
+ "runner.exe.manifest"
+)
+apply_standard_settings(${BINARY_NAME})
+target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
+target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
+target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
+add_dependencies(${BINARY_NAME} flutter_assemble)
diff --git a/packages/rfw/example/remote/windows/runner/Runner.rc b/packages/rfw/example/remote/windows/runner/Runner.rc
new file mode 100644
index 0000000..991f8f5
--- /dev/null
+++ b/packages/rfw/example/remote/windows/runner/Runner.rc
@@ -0,0 +1,121 @@
+// Microsoft Visual C++ generated resource script.
+//
+#pragma code_page(65001)
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (United States) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""winres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_APP_ICON ICON "resources\\app_icon.ico"
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+#ifdef FLUTTER_BUILD_NUMBER
+#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER
+#else
+#define VERSION_AS_NUMBER 1,0,0
+#endif
+
+#ifdef FLUTTER_BUILD_NAME
+#define VERSION_AS_STRING #FLUTTER_BUILD_NAME
+#else
+#define VERSION_AS_STRING "1.0.0"
+#endif
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VERSION_AS_NUMBER
+ PRODUCTVERSION VERSION_AS_NUMBER
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+#ifdef _DEBUG
+ FILEFLAGS VS_FF_DEBUG
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_APP
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "CompanyName", "dev.flutter.rfw.examples" "\0"
+ VALUE "FileDescription", "Example of fetching remote widgets for RFW" "\0"
+ VALUE "FileVersion", VERSION_AS_STRING "\0"
+ VALUE "InternalName", "remote" "\0"
+ VALUE "LegalCopyright", "Copyright (C) 2021 dev.flutter.rfw.examples. All rights reserved." "\0"
+ VALUE "OriginalFilename", "remote.exe" "\0"
+ VALUE "ProductName", "remote" "\0"
+ VALUE "ProductVersion", VERSION_AS_STRING "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252
+ END
+END
+
+#endif // English (United States) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
diff --git a/packages/rfw/example/remote/windows/runner/flutter_window.cpp b/packages/rfw/example/remote/windows/runner/flutter_window.cpp
new file mode 100644
index 0000000..8254bd9
--- /dev/null
+++ b/packages/rfw/example/remote/windows/runner/flutter_window.cpp
@@ -0,0 +1,65 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "flutter_window.h"
+
+#include <optional>
+
+#include "flutter/generated_plugin_registrant.h"
+
+FlutterWindow::FlutterWindow(const flutter::DartProject& project)
+ : project_(project) {}
+
+FlutterWindow::~FlutterWindow() {}
+
+bool FlutterWindow::OnCreate() {
+ if (!Win32Window::OnCreate()) {
+ return false;
+ }
+
+ RECT frame = GetClientArea();
+
+ // The size here must match the window dimensions to avoid unnecessary surface
+ // creation / destruction in the startup path.
+ flutter_controller_ = std::make_unique<flutter::FlutterViewController>(
+ frame.right - frame.left, frame.bottom - frame.top, project_);
+ // Ensure that basic setup of the controller was successful.
+ if (!flutter_controller_->engine() || !flutter_controller_->view()) {
+ return false;
+ }
+ RegisterPlugins(flutter_controller_->engine());
+ SetChildContent(flutter_controller_->view()->GetNativeWindow());
+ return true;
+}
+
+void FlutterWindow::OnDestroy() {
+ if (flutter_controller_) {
+ flutter_controller_ = nullptr;
+ }
+
+ Win32Window::OnDestroy();
+}
+
+LRESULT
+FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept {
+ // Give Flutter, including plugins, an opportunity to handle window messages.
+ if (flutter_controller_) {
+ std::optional<LRESULT> result =
+ flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
+ lparam);
+ if (result) {
+ return *result;
+ }
+ }
+
+ switch (message) {
+ case WM_FONTCHANGE:
+ flutter_controller_->engine()->ReloadSystemFonts();
+ break;
+ }
+
+ return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
+}
diff --git a/packages/rfw/example/remote/windows/runner/flutter_window.h b/packages/rfw/example/remote/windows/runner/flutter_window.h
new file mode 100644
index 0000000..f1fc669
--- /dev/null
+++ b/packages/rfw/example/remote/windows/runner/flutter_window.h
@@ -0,0 +1,37 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef RUNNER_FLUTTER_WINDOW_H_
+#define RUNNER_FLUTTER_WINDOW_H_
+
+#include <flutter/dart_project.h>
+#include <flutter/flutter_view_controller.h>
+
+#include <memory>
+
+#include "win32_window.h"
+
+// A window that does nothing but host a Flutter view.
+class FlutterWindow : public Win32Window {
+ public:
+ // Creates a new FlutterWindow hosting a Flutter view running |project|.
+ explicit FlutterWindow(const flutter::DartProject& project);
+ virtual ~FlutterWindow();
+
+ protected:
+ // Win32Window:
+ bool OnCreate() override;
+ void OnDestroy() override;
+ LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
+ LPARAM const lparam) noexcept override;
+
+ private:
+ // The project to run.
+ flutter::DartProject project_;
+
+ // The Flutter instance hosted by this window.
+ std::unique_ptr<flutter::FlutterViewController> flutter_controller_;
+};
+
+#endif // RUNNER_FLUTTER_WINDOW_H_
diff --git a/packages/rfw/example/remote/windows/runner/main.cpp b/packages/rfw/example/remote/windows/runner/main.cpp
new file mode 100644
index 0000000..cc84fb7
--- /dev/null
+++ b/packages/rfw/example/remote/windows/runner/main.cpp
@@ -0,0 +1,46 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <flutter/dart_project.h>
+#include <flutter/flutter_view_controller.h>
+#include <windows.h>
+
+#include "flutter_window.h"
+#include "utils.h"
+
+int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
+ _In_ wchar_t *command_line, _In_ int show_command) {
+ // Attach to console when present (e.g., 'flutter run') or create a
+ // new console when running with a debugger.
+ if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
+ CreateAndAttachConsole();
+ }
+
+ // Initialize COM, so that it is available for use in the library and/or
+ // plugins.
+ ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
+
+ flutter::DartProject project(L"data");
+
+ std::vector<std::string> command_line_arguments = GetCommandLineArguments();
+
+ project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
+
+ FlutterWindow window(project);
+ Win32Window::Point origin(10, 10);
+ Win32Window::Size size(1280, 720);
+ if (!window.CreateAndShow(L"remote", origin, size)) {
+ return EXIT_FAILURE;
+ }
+ window.SetQuitOnClose(true);
+
+ ::MSG msg;
+ while (::GetMessage(&msg, nullptr, 0, 0)) {
+ ::TranslateMessage(&msg);
+ ::DispatchMessage(&msg);
+ }
+
+ ::CoUninitialize();
+ return EXIT_SUCCESS;
+}
diff --git a/packages/rfw/example/remote/windows/runner/resource.h b/packages/rfw/example/remote/windows/runner/resource.h
new file mode 100644
index 0000000..d5d958d
--- /dev/null
+++ b/packages/rfw/example/remote/windows/runner/resource.h
@@ -0,0 +1,16 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by Runner.rc
+//
+#define IDI_APP_ICON 101
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 102
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/packages/rfw/example/remote/windows/runner/resources/app_icon.ico b/packages/rfw/example/remote/windows/runner/resources/app_icon.ico
new file mode 100644
index 0000000..c04e20c
--- /dev/null
+++ b/packages/rfw/example/remote/windows/runner/resources/app_icon.ico
Binary files differ
diff --git a/packages/rfw/example/remote/windows/runner/runner.exe.manifest b/packages/rfw/example/remote/windows/runner/runner.exe.manifest
new file mode 100644
index 0000000..c977c4a
--- /dev/null
+++ b/packages/rfw/example/remote/windows/runner/runner.exe.manifest
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <application xmlns="urn:schemas-microsoft-com:asm.v3">
+ <windowsSettings>
+ <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
+ </windowsSettings>
+ </application>
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <!-- Windows 10 -->
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
+ <!-- Windows 8.1 -->
+ <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
+ <!-- Windows 8 -->
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
+ <!-- Windows 7 -->
+ <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
+ </application>
+ </compatibility>
+</assembly>
diff --git a/packages/rfw/example/remote/windows/runner/utils.cpp b/packages/rfw/example/remote/windows/runner/utils.cpp
new file mode 100644
index 0000000..fb7e945
--- /dev/null
+++ b/packages/rfw/example/remote/windows/runner/utils.cpp
@@ -0,0 +1,67 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "utils.h"
+
+#include <flutter_windows.h>
+#include <io.h>
+#include <stdio.h>
+#include <windows.h>
+
+#include <iostream>
+
+void CreateAndAttachConsole() {
+ if (::AllocConsole()) {
+ FILE* unused;
+ if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
+ _dup2(_fileno(stdout), 1);
+ }
+ if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
+ _dup2(_fileno(stdout), 2);
+ }
+ std::ios::sync_with_stdio();
+ FlutterDesktopResyncOutputStreams();
+ }
+}
+
+std::vector<std::string> GetCommandLineArguments() {
+ // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.
+ int argc;
+ wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
+ if (argv == nullptr) {
+ return std::vector<std::string>();
+ }
+
+ std::vector<std::string> command_line_arguments;
+
+ // Skip the first argument as it's the binary name.
+ for (int i = 1; i < argc; i++) {
+ command_line_arguments.push_back(Utf8FromUtf16(argv[i]));
+ }
+
+ ::LocalFree(argv);
+
+ return command_line_arguments;
+}
+
+std::string Utf8FromUtf16(const wchar_t* utf16_string) {
+ if (utf16_string == nullptr) {
+ return std::string();
+ }
+ int target_length =
+ ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1,
+ nullptr, 0, nullptr, nullptr);
+ if (target_length == 0) {
+ return std::string();
+ }
+ std::string utf8_string;
+ utf8_string.resize(target_length);
+ int converted_length = ::WideCharToMultiByte(
+ CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, utf8_string.data(),
+ target_length, nullptr, nullptr);
+ if (converted_length == 0) {
+ return std::string();
+ }
+ return utf8_string;
+}
diff --git a/packages/rfw/example/remote/windows/runner/utils.h b/packages/rfw/example/remote/windows/runner/utils.h
new file mode 100644
index 0000000..bd81e1e
--- /dev/null
+++ b/packages/rfw/example/remote/windows/runner/utils.h
@@ -0,0 +1,23 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef RUNNER_UTILS_H_
+#define RUNNER_UTILS_H_
+
+#include <string>
+#include <vector>
+
+// Creates a console for the process, and redirects stdout and stderr to
+// it for both the runner and the Flutter library.
+void CreateAndAttachConsole();
+
+// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string
+// encoded in UTF-8. Returns an empty std::string on failure.
+std::string Utf8FromUtf16(const wchar_t* utf16_string);
+
+// Gets the command line arguments passed in as a std::vector<std::string>,
+// encoded in UTF-8. Returns an empty std::vector<std::string> on failure.
+std::vector<std::string> GetCommandLineArguments();
+
+#endif // RUNNER_UTILS_H_
diff --git a/packages/rfw/example/remote/windows/runner/win32_window.cpp b/packages/rfw/example/remote/windows/runner/win32_window.cpp
new file mode 100644
index 0000000..85aa361
--- /dev/null
+++ b/packages/rfw/example/remote/windows/runner/win32_window.cpp
@@ -0,0 +1,241 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "win32_window.h"
+
+#include <flutter_windows.h>
+
+#include "resource.h"
+
+namespace {
+
+constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
+
+// The number of Win32Window objects that currently exist.
+static int g_active_window_count = 0;
+
+using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
+
+// Scale helper to convert logical scaler values to physical using passed in
+// scale factor
+int Scale(int source, double scale_factor) {
+ return static_cast<int>(source * scale_factor);
+}
+
+// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
+// This API is only needed for PerMonitor V1 awareness mode.
+void EnableFullDpiSupportIfAvailable(HWND hwnd) {
+ HMODULE user32_module = LoadLibraryA("User32.dll");
+ if (!user32_module) {
+ return;
+ }
+ auto enable_non_client_dpi_scaling =
+ reinterpret_cast<EnableNonClientDpiScaling*>(
+ GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
+ if (enable_non_client_dpi_scaling != nullptr) {
+ enable_non_client_dpi_scaling(hwnd);
+ FreeLibrary(user32_module);
+ }
+}
+
+} // namespace
+
+// Manages the Win32Window's window class registration.
+class WindowClassRegistrar {
+ public:
+ ~WindowClassRegistrar() = default;
+
+ // Returns the singleton registar instance.
+ static WindowClassRegistrar* GetInstance() {
+ if (!instance_) {
+ instance_ = new WindowClassRegistrar();
+ }
+ return instance_;
+ }
+
+ // Returns the name of the window class, registering the class if it hasn't
+ // previously been registered.
+ const wchar_t* GetWindowClass();
+
+ // Unregisters the window class. Should only be called if there are no
+ // instances of the window.
+ void UnregisterWindowClass();
+
+ private:
+ WindowClassRegistrar() = default;
+
+ static WindowClassRegistrar* instance_;
+
+ bool class_registered_ = false;
+};
+
+WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;
+
+const wchar_t* WindowClassRegistrar::GetWindowClass() {
+ if (!class_registered_) {
+ WNDCLASS window_class{};
+ window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
+ window_class.lpszClassName = kWindowClassName;
+ window_class.style = CS_HREDRAW | CS_VREDRAW;
+ window_class.cbClsExtra = 0;
+ window_class.cbWndExtra = 0;
+ window_class.hInstance = GetModuleHandle(nullptr);
+ window_class.hIcon =
+ LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
+ window_class.hbrBackground = 0;
+ window_class.lpszMenuName = nullptr;
+ window_class.lpfnWndProc = Win32Window::WndProc;
+ RegisterClass(&window_class);
+ class_registered_ = true;
+ }
+ return kWindowClassName;
+}
+
+void WindowClassRegistrar::UnregisterWindowClass() {
+ UnregisterClass(kWindowClassName, nullptr);
+ class_registered_ = false;
+}
+
+Win32Window::Win32Window() { ++g_active_window_count; }
+
+Win32Window::~Win32Window() {
+ --g_active_window_count;
+ Destroy();
+}
+
+bool Win32Window::CreateAndShow(const std::wstring& title, const Point& origin,
+ const Size& size) {
+ Destroy();
+
+ const wchar_t* window_class =
+ WindowClassRegistrar::GetInstance()->GetWindowClass();
+
+ const POINT target_point = {static_cast<LONG>(origin.x),
+ static_cast<LONG>(origin.y)};
+ HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
+ UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
+ double scale_factor = dpi / 96.0;
+
+ HWND window = CreateWindow(
+ window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
+ Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
+ Scale(size.width, scale_factor), Scale(size.height, scale_factor),
+ nullptr, nullptr, GetModuleHandle(nullptr), this);
+
+ if (!window) {
+ return false;
+ }
+
+ return OnCreate();
+}
+
+// static
+LRESULT CALLBACK Win32Window::WndProc(HWND const window, UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept {
+ if (message == WM_NCCREATE) {
+ auto window_struct = reinterpret_cast<CREATESTRUCT*>(lparam);
+ SetWindowLongPtr(window, GWLP_USERDATA,
+ reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));
+
+ auto that = static_cast<Win32Window*>(window_struct->lpCreateParams);
+ EnableFullDpiSupportIfAvailable(window);
+ that->window_handle_ = window;
+ } else if (Win32Window* that = GetThisFromHandle(window)) {
+ return that->MessageHandler(window, message, wparam, lparam);
+ }
+
+ return DefWindowProc(window, message, wparam, lparam);
+}
+
+LRESULT
+Win32Window::MessageHandler(HWND hwnd, UINT const message, WPARAM const wparam,
+ LPARAM const lparam) noexcept {
+ switch (message) {
+ case WM_DESTROY:
+ window_handle_ = nullptr;
+ Destroy();
+ if (quit_on_close_) {
+ PostQuitMessage(0);
+ }
+ return 0;
+
+ case WM_DPICHANGED: {
+ auto newRectSize = reinterpret_cast<RECT*>(lparam);
+ LONG newWidth = newRectSize->right - newRectSize->left;
+ LONG newHeight = newRectSize->bottom - newRectSize->top;
+
+ SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,
+ newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
+
+ return 0;
+ }
+ case WM_SIZE: {
+ RECT rect = GetClientArea();
+ if (child_content_ != nullptr) {
+ // Size and position the child window.
+ MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,
+ rect.bottom - rect.top, TRUE);
+ }
+ return 0;
+ }
+
+ case WM_ACTIVATE:
+ if (child_content_ != nullptr) {
+ SetFocus(child_content_);
+ }
+ return 0;
+ }
+
+ return DefWindowProc(window_handle_, message, wparam, lparam);
+}
+
+void Win32Window::Destroy() {
+ OnDestroy();
+
+ if (window_handle_) {
+ DestroyWindow(window_handle_);
+ window_handle_ = nullptr;
+ }
+ if (g_active_window_count == 0) {
+ WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
+ }
+}
+
+Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
+ return reinterpret_cast<Win32Window*>(
+ GetWindowLongPtr(window, GWLP_USERDATA));
+}
+
+void Win32Window::SetChildContent(HWND content) {
+ child_content_ = content;
+ SetParent(content, window_handle_);
+ RECT frame = GetClientArea();
+
+ MoveWindow(content, frame.left, frame.top, frame.right - frame.left,
+ frame.bottom - frame.top, true);
+
+ SetFocus(child_content_);
+}
+
+RECT Win32Window::GetClientArea() {
+ RECT frame;
+ GetClientRect(window_handle_, &frame);
+ return frame;
+}
+
+HWND Win32Window::GetHandle() { return window_handle_; }
+
+void Win32Window::SetQuitOnClose(bool quit_on_close) {
+ quit_on_close_ = quit_on_close;
+}
+
+bool Win32Window::OnCreate() {
+ // No-op; provided for subclasses.
+ return true;
+}
+
+void Win32Window::OnDestroy() {
+ // No-op; provided for subclasses.
+}
diff --git a/packages/rfw/example/remote/windows/runner/win32_window.h b/packages/rfw/example/remote/windows/runner/win32_window.h
new file mode 100644
index 0000000..d2a7300
--- /dev/null
+++ b/packages/rfw/example/remote/windows/runner/win32_window.h
@@ -0,0 +1,99 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef RUNNER_WIN32_WINDOW_H_
+#define RUNNER_WIN32_WINDOW_H_
+
+#include <windows.h>
+
+#include <functional>
+#include <memory>
+#include <string>
+
+// A class abstraction for a high DPI-aware Win32 Window. Intended to be
+// inherited from by classes that wish to specialize with custom
+// rendering and input handling
+class Win32Window {
+ public:
+ struct Point {
+ unsigned int x;
+ unsigned int y;
+ Point(unsigned int x, unsigned int y) : x(x), y(y) {}
+ };
+
+ struct Size {
+ unsigned int width;
+ unsigned int height;
+ Size(unsigned int width, unsigned int height)
+ : width(width), height(height) {}
+ };
+
+ Win32Window();
+ virtual ~Win32Window();
+
+ // Creates and shows a win32 window with |title| and position and size using
+ // |origin| and |size|. New windows are created on the default monitor. Window
+ // sizes are specified to the OS in physical pixels, hence to ensure a
+ // consistent size to will treat the width height passed in to this function
+ // as logical pixels and scale to appropriate for the default monitor. Returns
+ // true if the window was created successfully.
+ bool CreateAndShow(const std::wstring& title, const Point& origin,
+ const Size& size);
+
+ // Release OS resources associated with window.
+ void Destroy();
+
+ // Inserts |content| into the window tree.
+ void SetChildContent(HWND content);
+
+ // Returns the backing Window handle to enable clients to set icon and other
+ // window properties. Returns nullptr if the window has been destroyed.
+ HWND GetHandle();
+
+ // If true, closing this window will quit the application.
+ void SetQuitOnClose(bool quit_on_close);
+
+ // Return a RECT representing the bounds of the current client area.
+ RECT GetClientArea();
+
+ protected:
+ // Processes and route salient window messages for mouse handling,
+ // size change and DPI. Delegates handling of these to member overloads that
+ // inheriting classes can handle.
+ virtual LRESULT MessageHandler(HWND window, UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept;
+
+ // Called when CreateAndShow is called, allowing subclass window-related
+ // setup. Subclasses should return false if setup fails.
+ virtual bool OnCreate();
+
+ // Called when Destroy is called.
+ virtual void OnDestroy();
+
+ private:
+ friend class WindowClassRegistrar;
+
+ // OS callback called by message pump. Handles the WM_NCCREATE message which
+ // is passed when the non-client area is being created and enables automatic
+ // non-client DPI scaling so that the non-client area automatically
+ // responsponds to changes in DPI. All other messages are handled by
+ // MessageHandler.
+ static LRESULT CALLBACK WndProc(HWND const window, UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept;
+
+ // Retrieves a class instance pointer for |window|
+ static Win32Window* GetThisFromHandle(HWND const window) noexcept;
+
+ bool quit_on_close_ = false;
+
+ // window handle for top level window.
+ HWND window_handle_ = nullptr;
+
+ // window handle for hosted content.
+ HWND child_content_ = nullptr;
+};
+
+#endif // RUNNER_WIN32_WINDOW_H_
diff --git a/packages/rfw/example/wasm/.gitignore b/packages/rfw/example/wasm/.gitignore
new file mode 100644
index 0000000..0fa6b67
--- /dev/null
+++ b/packages/rfw/example/wasm/.gitignore
@@ -0,0 +1,46 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+**/ios/Flutter/.last_build_id
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+.packages
+.pub-cache/
+.pub/
+/build/
+
+# Web related
+lib/generated_plugin_registrant.dart
+
+# Symbolication related
+app.*.symbols
+
+# Obfuscation related
+app.*.map.json
+
+# Android Studio will place build artifacts here
+/android/app/debug
+/android/app/profile
+/android/app/release
diff --git a/packages/rfw/example/wasm/.metadata b/packages/rfw/example/wasm/.metadata
new file mode 100644
index 0000000..45c494e
--- /dev/null
+++ b/packages/rfw/example/wasm/.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: a1cd3f45e02c2429e14630d6cc6de120c3d24bd0
+ channel: master
+
+project_type: app
diff --git a/packages/rfw/example/wasm/.pluginToolsConfig.yaml b/packages/rfw/example/wasm/.pluginToolsConfig.yaml
new file mode 100644
index 0000000..0be940a
--- /dev/null
+++ b/packages/rfw/example/wasm/.pluginToolsConfig.yaml
@@ -0,0 +1,3 @@
+buildFlags:
+ global:
+ - "--no-tree-shake-icons"
diff --git a/packages/rfw/example/wasm/README.md b/packages/rfw/example/wasm/README.md
new file mode 100644
index 0000000..af1fe39
--- /dev/null
+++ b/packages/rfw/example/wasm/README.md
@@ -0,0 +1,45 @@
+# Example of using Wasm with RFW
+
+In this example, the application downloads both RFW descriptions of UI
+and Wasm logic to drive it. The Flutter application itself does not
+contain any of the calculator UI or logic.
+
+Currently this only runs on macOS, Windows, and Linux, since
+`package:wasm` does not yet support Android, iOS, or web.
+
+## Building
+
+Before running this package, you must run `flutter pub run wasm:setup`
+in this directory. Before doing this, you will need to have installed
+Rust and clang.
+
+To rebuild the files in the logic/ directory (which are the files that
+the application downloads at runtime), you will additionally need to
+have installed clang and lld, and dart must be on your path.
+
+## Conventions
+
+The application renders the remote widget named `root` defined in the
+`logic/calculator.rfw` file, and loads the Wasm module defined in the
+`logic/calculator.wasm` file.
+
+The Wasm module must implement a function `value` that takes no
+arguments and returns an integer, which is the data to pass to the
+interface. This will be the first function called.
+
+That data is stored in the `value` key of the data exposed to the
+remote widgets, as a map with two values, `numeric` (which stores the
+integer as-is) and `string` (which stores its string representation,
+e.g. for display using a `Text` widget).
+
+The remote widgets can signal events. The names of such events are
+looked up as functions in the Wasm module. The `arguments` key, if
+present, must be a list of integers to pass to the function.
+
+Only the `core.widgets` local library is loaded into the runtime.
+
+## Application behavior
+
+The demo application fetches the RFW and Wasm files on startup, if it
+has not downloaded them before or if they were downloaded more than
+six hours earlier.
diff --git a/packages/rfw/example/wasm/android/.gitignore b/packages/rfw/example/wasm/android/.gitignore
new file mode 100644
index 0000000..6f56801
--- /dev/null
+++ b/packages/rfw/example/wasm/android/.gitignore
@@ -0,0 +1,13 @@
+gradle-wrapper.jar
+/.gradle
+/captures/
+/gradlew
+/gradlew.bat
+/local.properties
+GeneratedPluginRegistrant.java
+
+# Remember to never publicly share your keystore.
+# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
+key.properties
+**/*.keystore
+**/*.jks
diff --git a/packages/rfw/example/wasm/android/app/build.gradle b/packages/rfw/example/wasm/android/app/build.gradle
new file mode 100644
index 0000000..2b9cd29
--- /dev/null
+++ b/packages/rfw/example/wasm/android/app/build.gradle
@@ -0,0 +1,68 @@
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+ localPropertiesFile.withReader('UTF-8') { reader ->
+ localProperties.load(reader)
+ }
+}
+
+def flutterRoot = localProperties.getProperty('flutter.sdk')
+if (flutterRoot == null) {
+ throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
+}
+
+def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
+if (flutterVersionCode == null) {
+ flutterVersionCode = '1'
+}
+
+def flutterVersionName = localProperties.getProperty('flutter.versionName')
+if (flutterVersionName == null) {
+ flutterVersionName = '1.0'
+}
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
+
+android {
+ compileSdkVersion 30
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+
+ defaultConfig {
+ // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
+ applicationId "dev.flutter.rfw.examples.wasm"
+ minSdkVersion 16
+ targetSdkVersion 30
+ versionCode flutterVersionCode.toInteger()
+ versionName flutterVersionName
+ }
+
+ buildTypes {
+ release {
+ // TODO: Add your own signing config for the release build.
+ // Signing with the debug keys for now, so `flutter run --release` works.
+ signingConfig signingConfigs.debug
+ }
+ }
+}
+
+flutter {
+ source '../..'
+}
+
+dependencies {
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+}
diff --git a/packages/rfw/example/wasm/android/app/src/debug/AndroidManifest.xml b/packages/rfw/example/wasm/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 0000000..bb039dc
--- /dev/null
+++ b/packages/rfw/example/wasm/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,7 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dev.flutter.rfw.examples.wasm">
+ <!-- Flutter needs it to communicate with the running application
+ to allow setting breakpoints, to provide hot reload, etc.
+ -->
+ <uses-permission android:name="android.permission.INTERNET"/>
+</manifest>
diff --git a/packages/rfw/example/wasm/android/app/src/main/AndroidManifest.xml b/packages/rfw/example/wasm/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..2867e4b
--- /dev/null
+++ b/packages/rfw/example/wasm/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dev.flutter.rfw.examples.wasm">
+ <application
+ android:label="wasm"
+ android:icon="@mipmap/ic_launcher">
+ <activity
+ android:name=".MainActivity"
+ android:launchMode="singleTop"
+ android:theme="@style/LaunchTheme"
+ android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
+ android:hardwareAccelerated="true"
+ android:windowSoftInputMode="adjustResize">
+ <!-- Specifies an Android theme to apply to this Activity as soon as
+ the Android process has started. This theme is visible to the user
+ while the Flutter UI initializes. After that, this theme continues
+ to determine the Window background behind the Flutter UI. -->
+ <meta-data
+ android:name="io.flutter.embedding.android.NormalTheme"
+ android:resource="@style/NormalTheme"
+ />
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <!-- Don't delete the meta-data below.
+ This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
+ <meta-data
+ android:name="flutterEmbedding"
+ android:value="2" />
+ </application>
+</manifest>
diff --git a/packages/rfw/example/wasm/android/app/src/main/kotlin/dev/flutter/rfw/examples/wasm/MainActivity.kt b/packages/rfw/example/wasm/android/app/src/main/kotlin/dev/flutter/rfw/examples/wasm/MainActivity.kt
new file mode 100644
index 0000000..1cd18a6
--- /dev/null
+++ b/packages/rfw/example/wasm/android/app/src/main/kotlin/dev/flutter/rfw/examples/wasm/MainActivity.kt
@@ -0,0 +1,6 @@
+package dev.flutter.rfw.examples.wasm
+
+import io.flutter.embedding.android.FlutterActivity
+
+class MainActivity: FlutterActivity() {
+}
diff --git a/packages/rfw/example/wasm/android/app/src/main/res/drawable-v21/launch_background.xml b/packages/rfw/example/wasm/android/app/src/main/res/drawable-v21/launch_background.xml
new file mode 100644
index 0000000..f74085f
--- /dev/null
+++ b/packages/rfw/example/wasm/android/app/src/main/res/drawable-v21/launch_background.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Modify this file to customize your launch splash screen -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="?android:colorBackground" />
+
+ <!-- You can insert your own image assets here -->
+ <!-- <item>
+ <bitmap
+ android:gravity="center"
+ android:src="@mipmap/launch_image" />
+ </item> -->
+</layer-list>
diff --git a/packages/rfw/example/wasm/android/app/src/main/res/drawable/launch_background.xml b/packages/rfw/example/wasm/android/app/src/main/res/drawable/launch_background.xml
new file mode 100644
index 0000000..304732f
--- /dev/null
+++ b/packages/rfw/example/wasm/android/app/src/main/res/drawable/launch_background.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Modify this file to customize your launch splash screen -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@android:color/white" />
+
+ <!-- You can insert your own image assets here -->
+ <!-- <item>
+ <bitmap
+ android:gravity="center"
+ android:src="@mipmap/launch_image" />
+ </item> -->
+</layer-list>
diff --git a/packages/rfw/example/wasm/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/rfw/example/wasm/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..db77bb4
--- /dev/null
+++ b/packages/rfw/example/wasm/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/packages/rfw/example/wasm/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/rfw/example/wasm/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..17987b7
--- /dev/null
+++ b/packages/rfw/example/wasm/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/packages/rfw/example/wasm/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/packages/rfw/example/wasm/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..09d4391
--- /dev/null
+++ b/packages/rfw/example/wasm/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/packages/rfw/example/wasm/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/rfw/example/wasm/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..d5f1c8d
--- /dev/null
+++ b/packages/rfw/example/wasm/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/packages/rfw/example/wasm/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/rfw/example/wasm/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4d6372e
--- /dev/null
+++ b/packages/rfw/example/wasm/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/packages/rfw/example/wasm/android/app/src/main/res/values-night/styles.xml b/packages/rfw/example/wasm/android/app/src/main/res/values-night/styles.xml
new file mode 100644
index 0000000..449a9f9
--- /dev/null
+++ b/packages/rfw/example/wasm/android/app/src/main/res/values-night/styles.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
+ <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
+ <!-- Show a splash screen on the activity. Automatically removed when
+ Flutter draws its first frame -->
+ <item name="android:windowBackground">@drawable/launch_background</item>
+ </style>
+ <!-- Theme applied to the Android Window as soon as the process has started.
+ This theme determines the color of the Android Window while your
+ Flutter UI initializes, as well as behind your Flutter UI while its
+ running.
+
+ This Theme is only used starting with V2 of Flutter's Android embedding. -->
+ <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
+ <item name="android:windowBackground">?android:colorBackground</item>
+ </style>
+</resources>
diff --git a/packages/rfw/example/wasm/android/app/src/main/res/values/styles.xml b/packages/rfw/example/wasm/android/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..d74aa35
--- /dev/null
+++ b/packages/rfw/example/wasm/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
+ <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
+ <!-- Show a splash screen on the activity. Automatically removed when
+ Flutter draws its first frame -->
+ <item name="android:windowBackground">@drawable/launch_background</item>
+ </style>
+ <!-- Theme applied to the Android Window as soon as the process has started.
+ This theme determines the color of the Android Window while your
+ Flutter UI initializes, as well as behind your Flutter UI while its
+ running.
+
+ This Theme is only used starting with V2 of Flutter's Android embedding. -->
+ <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
+ <item name="android:windowBackground">?android:colorBackground</item>
+ </style>
+</resources>
diff --git a/packages/rfw/example/wasm/android/app/src/profile/AndroidManifest.xml b/packages/rfw/example/wasm/android/app/src/profile/AndroidManifest.xml
new file mode 100644
index 0000000..bb039dc
--- /dev/null
+++ b/packages/rfw/example/wasm/android/app/src/profile/AndroidManifest.xml
@@ -0,0 +1,7 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dev.flutter.rfw.examples.wasm">
+ <!-- Flutter needs it to communicate with the running application
+ to allow setting breakpoints, to provide hot reload, etc.
+ -->
+ <uses-permission android:name="android.permission.INTERNET"/>
+</manifest>
diff --git a/packages/rfw/example/wasm/android/build.gradle b/packages/rfw/example/wasm/android/build.gradle
new file mode 100644
index 0000000..ed45c65
--- /dev/null
+++ b/packages/rfw/example/wasm/android/build.gradle
@@ -0,0 +1,29 @@
+buildscript {
+ ext.kotlin_version = '1.3.50'
+ repositories {
+ google()
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:4.1.0'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.buildDir = '../build'
+subprojects {
+ project.buildDir = "${rootProject.buildDir}/${project.name}"
+ project.evaluationDependsOn(':app')
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/packages/rfw/example/wasm/android/gradle.properties b/packages/rfw/example/wasm/android/gradle.properties
new file mode 100644
index 0000000..94adc3a
--- /dev/null
+++ b/packages/rfw/example/wasm/android/gradle.properties
@@ -0,0 +1,3 @@
+org.gradle.jvmargs=-Xmx1536M
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/packages/rfw/example/wasm/android/gradle/wrapper/gradle-wrapper.properties b/packages/rfw/example/wasm/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..bc6a58a
--- /dev/null
+++ b/packages/rfw/example/wasm/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-6.7-all.zip
diff --git a/packages/rfw/example/wasm/android/settings.gradle b/packages/rfw/example/wasm/android/settings.gradle
new file mode 100644
index 0000000..44e62bc
--- /dev/null
+++ b/packages/rfw/example/wasm/android/settings.gradle
@@ -0,0 +1,11 @@
+include ':app'
+
+def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
+def properties = new Properties()
+
+assert localPropertiesFile.exists()
+localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
+
+def flutterSdkPath = properties.getProperty("flutter.sdk")
+assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
diff --git a/packages/rfw/example/wasm/ios/.gitignore b/packages/rfw/example/wasm/ios/.gitignore
new file mode 100644
index 0000000..151026b
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/.gitignore
@@ -0,0 +1,33 @@
+*.mode1v3
+*.mode2v3
+*.moved-aside
+*.pbxuser
+*.perspectivev3
+**/*sync/
+.sconsign.dblite
+.tags*
+**/.vagrant/
+**/DerivedData/
+Icon?
+**/Pods/
+**/.symlinks/
+profile
+xcuserdata
+**/.generated/
+Flutter/App.framework
+Flutter/Flutter.framework
+Flutter/Flutter.podspec
+Flutter/Generated.xcconfig
+Flutter/ephemeral/
+Flutter/app.flx
+Flutter/app.zip
+Flutter/flutter_assets/
+Flutter/flutter_export_environment.sh
+ServiceDefinitions.json
+Runner/GeneratedPluginRegistrant.*
+
+# Exceptions to above rules.
+!default.mode1v3
+!default.mode2v3
+!default.pbxuser
+!default.perspectivev3
diff --git a/packages/rfw/example/wasm/ios/Flutter/AppFrameworkInfo.plist b/packages/rfw/example/wasm/ios/Flutter/AppFrameworkInfo.plist
new file mode 100644
index 0000000..8d4492f
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Flutter/AppFrameworkInfo.plist
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleExecutable</key>
+ <string>App</string>
+ <key>CFBundleIdentifier</key>
+ <string>io.flutter.flutter.app</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>App</string>
+ <key>CFBundlePackageType</key>
+ <string>FMWK</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>MinimumOSVersion</key>
+ <string>9.0</string>
+</dict>
+</plist>
diff --git a/packages/rfw/example/wasm/ios/Flutter/Debug.xcconfig b/packages/rfw/example/wasm/ios/Flutter/Debug.xcconfig
new file mode 100644
index 0000000..592ceee
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Flutter/Debug.xcconfig
@@ -0,0 +1 @@
+#include "Generated.xcconfig"
diff --git a/packages/rfw/example/wasm/ios/Flutter/Release.xcconfig b/packages/rfw/example/wasm/ios/Flutter/Release.xcconfig
new file mode 100644
index 0000000..592ceee
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Flutter/Release.xcconfig
@@ -0,0 +1 @@
+#include "Generated.xcconfig"
diff --git a/packages/rfw/example/wasm/ios/Runner.xcodeproj/project.pbxproj b/packages/rfw/example/wasm/ios/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..9558958
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,481 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 50;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
+ 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
+ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 97C146EB1CF9000F007C117D /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 9740EEB11CF90186004384FC /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */,
+ );
+ name = Flutter;
+ sourceTree = "<group>";
+ };
+ 97C146E51CF9000F007C117D = {
+ isa = PBXGroup;
+ children = (
+ 9740EEB11CF90186004384FC /* Flutter */,
+ 97C146F01CF9000F007C117D /* Runner */,
+ 97C146EF1CF9000F007C117D /* Products */,
+ );
+ sourceTree = "<group>";
+ };
+ 97C146EF1CF9000F007C117D /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146EE1CF9000F007C117D /* Runner.app */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 97C146F01CF9000F007C117D /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146FA1CF9000F007C117D /* Main.storyboard */,
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */,
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
+ 97C147021CF9000F007C117D /* Info.plist */,
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
+ );
+ path = Runner;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 97C146ED1CF9000F007C117D /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ 9740EEB61CF901F6004384FC /* Run Script */,
+ 97C146EA1CF9000F007C117D /* Sources */,
+ 97C146EB1CF9000F007C117D /* Frameworks */,
+ 97C146EC1CF9000F007C117D /* Resources */,
+ 9705A1C41CF9048500538489 /* Embed Frameworks */,
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = Runner;
+ productName = Runner;
+ productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 97C146E61CF9000F007C117D /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 1300;
+ ORGANIZATIONNAME = "";
+ TargetAttributes = {
+ 97C146ED1CF9000F007C117D = {
+ CreatedOnToolsVersion = 7.3.1;
+ LastSwiftMigration = 1100;
+ };
+ };
+ };
+ buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 97C146E51CF9000F007C117D;
+ productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 97C146ED1CF9000F007C117D /* Runner */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 97C146EC1CF9000F007C117D /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Thin Binary";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
+ };
+ 9740EEB61CF901F6004384FC /* Run Script */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Run Script";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 97C146EA1CF9000F007C117D /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C146FB1CF9000F007C117D /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "<group>";
+ };
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C147001CF9000F007C117D /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 249021D3217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Profile;
+ };
+ 249021D4217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.rfw.examples.wasm;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Profile;
+ };
+ 97C147031CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 97C147041CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 97C147061CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.rfw.examples.wasm;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Debug;
+ };
+ 97C147071CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.rfw.examples.wasm;
+ 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/packages/rfw/example/wasm/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/rfw/example/wasm/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..919434a
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "self:">
+ </FileRef>
+</Workspace>
diff --git a/packages/rfw/example/wasm/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/rfw/example/wasm/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IDEDidComputeMac32BitWarning</key>
+ <true/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/wasm/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/rfw/example/wasm/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 0000000..f9b0d7c
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>PreviewsEnabled</key>
+ <false/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/wasm/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/rfw/example/wasm/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 0000000..c87d15a
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "1300"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+ BuildableName = "Runner.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+ BuildableName = "Runner.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <Testables>
+ </Testables>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+ BuildableName = "Runner.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Profile"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+ BuildableName = "Runner.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/packages/rfw/example/wasm/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/rfw/example/wasm/ios/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..1d526a1
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "group:Runner.xcodeproj">
+ </FileRef>
+</Workspace>
diff --git a/packages/rfw/example/wasm/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/rfw/example/wasm/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IDEDidComputeMac32BitWarning</key>
+ <true/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/wasm/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/rfw/example/wasm/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 0000000..f9b0d7c
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>PreviewsEnabled</key>
+ <false/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/wasm/ios/Runner/AppDelegate.swift b/packages/rfw/example/wasm/ios/Runner/AppDelegate.swift
new file mode 100644
index 0000000..caf9983
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Runner/AppDelegate.swift
@@ -0,0 +1,17 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+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/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..d36b1fa
--- /dev/null
+++ b/packages/rfw/example/wasm/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/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
new file mode 100644
index 0000000..dc9ada4
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
Binary files differ
diff --git a/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
new file mode 100644
index 0000000..28c6bf0
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
Binary files differ
diff --git a/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
new file mode 100644
index 0000000..2ccbfd9
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
Binary files differ
diff --git a/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
new file mode 100644
index 0000000..f091b6b
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
Binary files differ
diff --git a/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
new file mode 100644
index 0000000..4cde121
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
Binary files differ
diff --git a/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
new file mode 100644
index 0000000..d0ef06e
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
Binary files differ
diff --git a/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
new file mode 100644
index 0000000..dcdc230
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
Binary files differ
diff --git a/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
new file mode 100644
index 0000000..2ccbfd9
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
Binary files differ
diff --git a/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
new file mode 100644
index 0000000..c8f9ed8
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
Binary files differ
diff --git a/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
new file mode 100644
index 0000000..a6d6b86
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
Binary files differ
diff --git a/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
new file mode 100644
index 0000000..a6d6b86
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
Binary files differ
diff --git a/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
new file mode 100644
index 0000000..75b2d16
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
Binary files differ
diff --git a/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
new file mode 100644
index 0000000..c4df70d
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
Binary files differ
diff --git a/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
new file mode 100644
index 0000000..6a84f41
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
Binary files differ
diff --git a/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
new file mode 100644
index 0000000..d0e1f58
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
Binary files differ
diff --git a/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
new file mode 100644
index 0000000..0bedcf2
--- /dev/null
+++ b/packages/rfw/example/wasm/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/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
new file mode 100644
index 0000000..9da19ea
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
Binary files differ
diff --git a/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
new file mode 100644
index 0000000..9da19ea
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
Binary files differ
diff --git a/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
new file mode 100644
index 0000000..9da19ea
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
Binary files differ
diff --git a/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/packages/rfw/example/wasm/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
new file mode 100644
index 0000000..89c2725
--- /dev/null
+++ b/packages/rfw/example/wasm/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/packages/rfw/example/wasm/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/rfw/example/wasm/ios/Runner/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000..f2e259c
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
+ <dependencies>
+ <deployment identifier="iOS"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
+ </dependencies>
+ <scenes>
+ <!--View Controller-->
+ <scene sceneID="EHf-IW-A2E">
+ <objects>
+ <viewController id="01J-lp-oVM" sceneMemberID="viewController">
+ <layoutGuides>
+ <viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
+ <viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
+ </layoutGuides>
+ <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <subviews>
+ <imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
+ </imageView>
+ </subviews>
+ <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+ <constraints>
+ <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
+ <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
+ </constraints>
+ </view>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="53" y="375"/>
+ </scene>
+ </scenes>
+ <resources>
+ <image name="LaunchImage" width="168" height="185"/>
+ </resources>
+</document>
diff --git a/packages/rfw/example/wasm/ios/Runner/Base.lproj/Main.storyboard b/packages/rfw/example/wasm/ios/Runner/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..f3c2851
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Runner/Base.lproj/Main.storyboard
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
+ <dependencies>
+ <deployment identifier="iOS"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
+ </dependencies>
+ <scenes>
+ <!--Flutter View Controller-->
+ <scene sceneID="tne-QT-ifu">
+ <objects>
+ <viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
+ <layoutGuides>
+ <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
+ <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
+ </layoutGuides>
+ <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
+ <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+ </view>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
+ </objects>
+ </scene>
+ </scenes>
+</document>
diff --git a/packages/rfw/example/wasm/ios/Runner/Info.plist b/packages/rfw/example/wasm/ios/Runner/Info.plist
new file mode 100644
index 0000000..5579c54
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Runner/Info.plist
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>$(DEVELOPMENT_LANGUAGE)</string>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>wasm</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>$(FLUTTER_BUILD_NAME)</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>$(FLUTTER_BUILD_NUMBER)</string>
+ <key>LSRequiresIPhoneOS</key>
+ <true/>
+ <key>UILaunchStoryboardName</key>
+ <string>LaunchScreen</string>
+ <key>UIMainStoryboardFile</key>
+ <string>Main</string>
+ <key>UISupportedInterfaceOrientations</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+ <key>UISupportedInterfaceOrientations~ipad</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationPortraitUpsideDown</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+ <key>UIViewControllerBasedStatusBarAppearance</key>
+ <false/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/wasm/ios/Runner/Runner-Bridging-Header.h b/packages/rfw/example/wasm/ios/Runner/Runner-Bridging-Header.h
new file mode 100644
index 0000000..eb7e8ba
--- /dev/null
+++ b/packages/rfw/example/wasm/ios/Runner/Runner-Bridging-Header.h
@@ -0,0 +1,5 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "GeneratedPluginRegistrant.h"
diff --git a/packages/rfw/example/wasm/lib/main.dart b/packages/rfw/example/wasm/lib/main.dart
new file mode 100644
index 0000000..c91b4d0
--- /dev/null
+++ b/packages/rfw/example/wasm/lib/main.dart
@@ -0,0 +1,94 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is hand-formatted.
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:flutter/rendering.dart';
+import 'package:flutter/widgets.dart';
+import 'package:path/path.dart' as path;
+import 'package:path_provider/path_provider.dart';
+import 'package:rfw/rfw.dart';
+import 'package:wasm/wasm.dart';
+
+const String urlPrefix = 'https://raw.githubusercontent.com/flutter/packages/master/packages/rfw/example/wasm/logic';
+
+const String interfaceUrl = '$urlPrefix/calculator.rfw';
+const String logicUrl = '$urlPrefix/calculator.wasm';
+
+void main() {
+ runApp(WidgetsApp(color: const Color(0xFF000000), builder: (BuildContext context, Widget? navigator) => const Example()));
+}
+
+class Example extends StatefulWidget {
+ const Example({Key? key}) : super(key: key);
+
+ @override
+ State<Example> createState() => _ExampleState();
+}
+
+class _ExampleState extends State<Example> {
+ final Runtime _runtime = Runtime();
+ final DynamicContent _data = DynamicContent();
+ late final WasmInstance _logic;
+
+ @override
+ void initState() {
+ super.initState();
+ RendererBinding.instance!.deferFirstFrame();
+ _runtime.update(const LibraryName(<String>['core', 'widgets']), createCoreWidgets());
+ _loadLogic();
+ }
+
+ late final WasmFunction _dataFetcher;
+
+ Future<void> _loadLogic() async {
+ final DateTime expiryDate = DateTime.now().subtract(const Duration(hours: 6));
+ final Directory home = await getApplicationSupportDirectory();
+ final File interfaceFile = File(path.join(home.path, 'cache.rfw'));
+ if (!interfaceFile.existsSync() || interfaceFile.lastModifiedSync().isBefore(expiryDate)) {
+ final HttpClientResponse client = await (await HttpClient().getUrl(Uri.parse(interfaceUrl))).close();
+ await interfaceFile.writeAsBytes(await client.expand((List<int> chunk) => chunk).toList());
+ }
+ final File logicFile = File(path.join(home.path, 'cache.wasm'));
+ if (!logicFile.existsSync() || logicFile.lastModifiedSync().isBefore(expiryDate)) {
+ final HttpClientResponse client = await (await HttpClient().getUrl(Uri.parse(logicUrl))).close();
+ await logicFile.writeAsBytes(await client.expand((List<int> chunk) => chunk).toList());
+ }
+ _runtime.update(const LibraryName(<String>['main']), decodeLibraryBlob(await interfaceFile.readAsBytes()));
+ _logic = WasmModule(await logicFile.readAsBytes()).builder().build();
+ _dataFetcher = _logic.lookupFunction('value');
+ _updateData();
+ setState(() { RendererBinding.instance!.allowFirstFrame(); });
+ }
+
+ void _updateData() {
+ final dynamic value = _dataFetcher.apply(const <Object?>[]);
+ _data.update('value', <String, Object?>{ 'numeric': value, 'string': value.toString() });
+ }
+
+ List<Object?> _asList(Object? value) {
+ if (value is List<Object?>)
+ return value;
+ return const <Object?>[];
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ if (!RendererBinding.instance!.sendFramesToEngine)
+ return const SizedBox.shrink();
+ return RemoteWidget(
+ runtime: _runtime,
+ data: _data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['main']), 'root'),
+ onEvent: (String name, DynamicMap arguments) {
+ final WasmFunction function = _logic.lookupFunction(name);
+ function.apply(_asList(arguments['arguments']));
+ _updateData();
+ },
+ );
+ }
+}
diff --git a/packages/rfw/example/wasm/linux/.gitignore b/packages/rfw/example/wasm/linux/.gitignore
new file mode 100644
index 0000000..d3896c9
--- /dev/null
+++ b/packages/rfw/example/wasm/linux/.gitignore
@@ -0,0 +1 @@
+flutter/ephemeral
diff --git a/packages/rfw/example/wasm/linux/CMakeLists.txt b/packages/rfw/example/wasm/linux/CMakeLists.txt
new file mode 100644
index 0000000..cd53444
--- /dev/null
+++ b/packages/rfw/example/wasm/linux/CMakeLists.txt
@@ -0,0 +1,116 @@
+cmake_minimum_required(VERSION 3.10)
+project(runner LANGUAGES CXX)
+
+set(BINARY_NAME "wasm")
+set(APPLICATION_ID "dev.flutter.rfw.examples.wasm")
+
+cmake_policy(SET CMP0063 NEW)
+
+set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
+
+# Root filesystem for cross-building.
+if(FLUTTER_TARGET_PLATFORM_SYSROOT)
+ set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
+ set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
+ set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
+ set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+ set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+endif()
+
+# Configure build options.
+if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+ set(CMAKE_BUILD_TYPE "Debug" CACHE
+ STRING "Flutter build mode" FORCE)
+ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
+ "Debug" "Profile" "Release")
+endif()
+
+# Compilation settings that should be applied to most targets.
+function(APPLY_STANDARD_SETTINGS TARGET)
+ target_compile_features(${TARGET} PUBLIC cxx_std_14)
+ target_compile_options(${TARGET} PRIVATE -Wall -Werror)
+ target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
+ target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
+endfunction()
+
+set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
+
+# Flutter library and tool build rules.
+add_subdirectory(${FLUTTER_MANAGED_DIR})
+
+# System-level dependencies.
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
+
+add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
+
+# Application build
+add_executable(${BINARY_NAME}
+ "main.cc"
+ "my_application.cc"
+ "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
+)
+apply_standard_settings(${BINARY_NAME})
+target_link_libraries(${BINARY_NAME} PRIVATE flutter)
+target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
+add_dependencies(${BINARY_NAME} flutter_assemble)
+# Only the install-generated bundle's copy of the executable will launch
+# correctly, since the resources must in the right relative locations. To avoid
+# people trying to run the unbundled copy, put it in a subdirectory instead of
+# the default top-level location.
+set_target_properties(${BINARY_NAME}
+ PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
+)
+
+# Generated plugin build rules, which manage building the plugins and adding
+# them to the application.
+include(flutter/generated_plugins.cmake)
+
+
+# === Installation ===
+# By default, "installing" just makes a relocatable bundle in the build
+# directory.
+set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
+if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+ set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
+endif()
+
+# Start with a clean build bundle directory every time.
+install(CODE "
+ file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
+ " COMPONENT Runtime)
+
+set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
+set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
+
+install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+
+if(PLUGIN_BUNDLED_LIBRARIES)
+ install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
+ DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endif()
+
+# Fully re-copy the assets directory on each build to avoid having stale files
+# from a previous install.
+set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
+install(CODE "
+ file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
+ " COMPONENT Runtime)
+install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
+ DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
+
+# Install the AOT library on non-Debug builds only.
+if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
+ install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endif()
diff --git a/packages/rfw/example/wasm/linux/flutter/CMakeLists.txt b/packages/rfw/example/wasm/linux/flutter/CMakeLists.txt
new file mode 100644
index 0000000..33fd580
--- /dev/null
+++ b/packages/rfw/example/wasm/linux/flutter/CMakeLists.txt
@@ -0,0 +1,87 @@
+cmake_minimum_required(VERSION 3.10)
+
+set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
+
+# Configuration provided via flutter tool.
+include(${EPHEMERAL_DIR}/generated_config.cmake)
+
+# TODO: Move the rest of this into files in ephemeral. See
+# https://github.com/flutter/flutter/issues/57146.
+
+# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
+# which isn't available in 3.10.
+function(list_prepend LIST_NAME PREFIX)
+ set(NEW_LIST "")
+ foreach(element ${${LIST_NAME}})
+ list(APPEND NEW_LIST "${PREFIX}${element}")
+ endforeach(element)
+ set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
+endfunction()
+
+# === Flutter Library ===
+# System-level dependencies.
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
+pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
+pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
+
+set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
+
+# Published to parent scope for install step.
+set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
+set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
+set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
+set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
+
+list(APPEND FLUTTER_LIBRARY_HEADERS
+ "fl_basic_message_channel.h"
+ "fl_binary_codec.h"
+ "fl_binary_messenger.h"
+ "fl_dart_project.h"
+ "fl_engine.h"
+ "fl_json_message_codec.h"
+ "fl_json_method_codec.h"
+ "fl_message_codec.h"
+ "fl_method_call.h"
+ "fl_method_channel.h"
+ "fl_method_codec.h"
+ "fl_method_response.h"
+ "fl_plugin_registrar.h"
+ "fl_plugin_registry.h"
+ "fl_standard_message_codec.h"
+ "fl_standard_method_codec.h"
+ "fl_string_codec.h"
+ "fl_value.h"
+ "fl_view.h"
+ "flutter_linux.h"
+)
+list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
+add_library(flutter INTERFACE)
+target_include_directories(flutter INTERFACE
+ "${EPHEMERAL_DIR}"
+)
+target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
+target_link_libraries(flutter INTERFACE
+ PkgConfig::GTK
+ PkgConfig::GLIB
+ PkgConfig::GIO
+)
+add_dependencies(flutter flutter_assemble)
+
+# === Flutter tool backend ===
+# _phony_ is a non-existent file to force this command to run every time,
+# since currently there's no way to get a full input/output list from the
+# flutter tool.
+add_custom_command(
+ OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
+ ${CMAKE_CURRENT_BINARY_DIR}/_phony_
+ COMMAND ${CMAKE_COMMAND} -E env
+ ${FLUTTER_TOOL_ENVIRONMENT}
+ "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
+ ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}
+ VERBATIM
+)
+add_custom_target(flutter_assemble DEPENDS
+ "${FLUTTER_LIBRARY}"
+ ${FLUTTER_LIBRARY_HEADERS}
+)
diff --git a/packages/rfw/example/wasm/linux/flutter/generated_plugins.cmake b/packages/rfw/example/wasm/linux/flutter/generated_plugins.cmake
new file mode 100644
index 0000000..51436ae
--- /dev/null
+++ b/packages/rfw/example/wasm/linux/flutter/generated_plugins.cmake
@@ -0,0 +1,15 @@
+#
+# Generated file, do not edit.
+#
+
+list(APPEND FLUTTER_PLUGIN_LIST
+)
+
+set(PLUGIN_BUNDLED_LIBRARIES)
+
+foreach(plugin ${FLUTTER_PLUGIN_LIST})
+ add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
+ target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
+endforeach(plugin)
diff --git a/packages/rfw/example/wasm/linux/main.cc b/packages/rfw/example/wasm/linux/main.cc
new file mode 100644
index 0000000..1507d02
--- /dev/null
+++ b/packages/rfw/example/wasm/linux/main.cc
@@ -0,0 +1,10 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "my_application.h"
+
+int main(int argc, char** argv) {
+ g_autoptr(MyApplication) app = my_application_new();
+ return g_application_run(G_APPLICATION(app), argc, argv);
+}
diff --git a/packages/rfw/example/wasm/linux/my_application.cc b/packages/rfw/example/wasm/linux/my_application.cc
new file mode 100644
index 0000000..ae90ddf
--- /dev/null
+++ b/packages/rfw/example/wasm/linux/my_application.cc
@@ -0,0 +1,111 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "my_application.h"
+
+#include <flutter_linux/flutter_linux.h>
+#ifdef GDK_WINDOWING_X11
+#include <gdk/gdkx.h>
+#endif
+
+#include "flutter/generated_plugin_registrant.h"
+
+struct _MyApplication {
+ GtkApplication parent_instance;
+ char** dart_entrypoint_arguments;
+};
+
+G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
+
+// Implements GApplication::activate.
+static void my_application_activate(GApplication* application) {
+ MyApplication* self = MY_APPLICATION(application);
+ GtkWindow* window =
+ GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
+
+ // Use a header bar when running in GNOME as this is the common style used
+ // by applications and is the setup most users will be using (e.g. Ubuntu
+ // desktop).
+ // If running on X and not using GNOME then just use a traditional title bar
+ // in case the window manager does more exotic layout, e.g. tiling.
+ // If running on Wayland assume the header bar will work (may need changing
+ // if future cases occur).
+ gboolean use_header_bar = TRUE;
+#ifdef GDK_WINDOWING_X11
+ GdkScreen* screen = gtk_window_get_screen(window);
+ if (GDK_IS_X11_SCREEN(screen)) {
+ const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
+ if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
+ use_header_bar = FALSE;
+ }
+ }
+#endif
+ if (use_header_bar) {
+ GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
+ gtk_widget_show(GTK_WIDGET(header_bar));
+ gtk_header_bar_set_title(header_bar, "wasm");
+ gtk_header_bar_set_show_close_button(header_bar, TRUE);
+ gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
+ } else {
+ gtk_window_set_title(window, "wasm");
+ }
+
+ gtk_window_set_default_size(window, 1280, 720);
+ gtk_widget_show(GTK_WIDGET(window));
+
+ g_autoptr(FlDartProject) project = fl_dart_project_new();
+ fl_dart_project_set_dart_entrypoint_arguments(
+ project, self->dart_entrypoint_arguments);
+
+ FlView* view = fl_view_new(project);
+ gtk_widget_show(GTK_WIDGET(view));
+ gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
+
+ fl_register_plugins(FL_PLUGIN_REGISTRY(view));
+
+ gtk_widget_grab_focus(GTK_WIDGET(view));
+}
+
+// Implements GApplication::local_command_line.
+static gboolean my_application_local_command_line(GApplication* application,
+ gchar*** arguments,
+ int* exit_status) {
+ MyApplication* self = MY_APPLICATION(application);
+ // Strip out the first argument as it is the binary name.
+ self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
+
+ g_autoptr(GError) error = nullptr;
+ if (!g_application_register(application, nullptr, &error)) {
+ g_warning("Failed to register: %s", error->message);
+ *exit_status = 1;
+ return TRUE;
+ }
+
+ g_application_activate(application);
+ *exit_status = 0;
+
+ return TRUE;
+}
+
+// Implements GObject::dispose.
+static void my_application_dispose(GObject* object) {
+ MyApplication* self = MY_APPLICATION(object);
+ g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
+ G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
+}
+
+static void my_application_class_init(MyApplicationClass* klass) {
+ G_APPLICATION_CLASS(klass)->activate = my_application_activate;
+ G_APPLICATION_CLASS(klass)->local_command_line =
+ my_application_local_command_line;
+ G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
+}
+
+static void my_application_init(MyApplication* self) {}
+
+MyApplication* my_application_new() {
+ return MY_APPLICATION(g_object_new(my_application_get_type(),
+ "application-id", APPLICATION_ID, "flags",
+ G_APPLICATION_NON_UNIQUE, nullptr));
+}
diff --git a/packages/rfw/example/wasm/linux/my_application.h b/packages/rfw/example/wasm/linux/my_application.h
new file mode 100644
index 0000000..6e9f0c3
--- /dev/null
+++ b/packages/rfw/example/wasm/linux/my_application.h
@@ -0,0 +1,22 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_MY_APPLICATION_H_
+#define FLUTTER_MY_APPLICATION_H_
+
+#include <gtk/gtk.h>
+
+G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
+ GtkApplication)
+
+/**
+ * my_application_new:
+ *
+ * Creates a new Flutter-based application.
+ *
+ * Returns: a new #MyApplication.
+ */
+MyApplication* my_application_new();
+
+#endif // FLUTTER_MY_APPLICATION_H_
diff --git a/packages/rfw/example/wasm/logic/build.sh b/packages/rfw/example/wasm/logic/build.sh
new file mode 100755
index 0000000..6434381
--- /dev/null
+++ b/packages/rfw/example/wasm/logic/build.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+# Copyright 2013 The Flutter Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+set -ex
+
+clang++ --target=wasm32 -nostdlib "-Wl,--export-all" "-Wl,--no-entry" -o calculator.wasm calculator.cc
+dart encode.dart calculator.rfwtxt calculator.rfw
diff --git a/packages/rfw/example/wasm/logic/calculator.cc b/packages/rfw/example/wasm/logic/calculator.cc
new file mode 100644
index 0000000..b744a0f
--- /dev/null
+++ b/packages/rfw/example/wasm/logic/calculator.cc
@@ -0,0 +1,50 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// internals
+
+enum Ops { opNone, opAdd };
+
+int _pending = 0;
+Ops _pendingOp = opNone;
+int _display = 0;
+bool _displayLocked = false;
+
+void _resolve() {
+ switch (_pendingOp) {
+ case opNone:
+ break;
+ case opAdd:
+ _display += _pending;
+ break;
+ }
+}
+
+// public API
+
+extern "C" int value() { return _display; }
+
+extern "C" void digit(int n) {
+ if (_displayLocked) {
+ _display = 0;
+ }
+ _display *= 10;
+ _display += n;
+ _displayLocked = false;
+}
+
+extern "C" void add() {
+ _resolve();
+ _pending = _display;
+ _pendingOp = opAdd;
+ _display = 0;
+ _displayLocked = false;
+}
+
+extern "C" void equals() {
+ int current = _displayLocked ? _pending : _display;
+ _resolve();
+ _pending = current;
+ _displayLocked = true;
+}
diff --git a/packages/rfw/example/wasm/logic/calculator.rfw b/packages/rfw/example/wasm/logic/calculator.rfw
new file mode 100644
index 0000000..85166b3
--- /dev/null
+++ b/packages/rfw/example/wasm/logic/calculator.rfw
Binary files differ
diff --git a/packages/rfw/example/wasm/logic/calculator.rfwtxt b/packages/rfw/example/wasm/logic/calculator.rfwtxt
new file mode 100644
index 0000000..900d984
--- /dev/null
+++ b/packages/rfw/example/wasm/logic/calculator.rfwtxt
@@ -0,0 +1,140 @@
+import core.widgets;
+
+widget root = Calculator();
+
+widget Calculator = SafeArea(
+ child: Center(
+ child: IntrinsicWidth(
+ child: Column(
+ crossAxisAlignment: "stretch",
+ children: [
+ Display(text: data.value.string),
+ Expanded(
+ child: FittedBox(
+ alignment: { x: 0.0, y: 1.0 },
+ child: CalculatorPad(),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+);
+
+widget CalculatorPad = Column(
+ children: [
+ Row(
+ children: [
+ CalculatorButton(label: "7", onPressed: event "digit" { arguments: [7] }),
+ CalculatorButton(label: "8", onPressed: event "digit" { arguments: [8] }),
+ CalculatorButton(label: "9", onPressed: event "digit" { arguments: [9] }),
+ SizedBox(width: 116.0, height: 116.0),
+ ],
+ ),
+ Row(
+ children: [
+ CalculatorButton(label: "4", onPressed: event "digit" { arguments: [4] }),
+ CalculatorButton(label: "5", onPressed: event "digit" { arguments: [5] }),
+ CalculatorButton(label: "6", onPressed: event "digit" { arguments: [6] }),
+ CalculatorButton(label: "+", onPressed: event "add" { }),
+ ],
+ ),
+ Row(
+ children: [
+ CalculatorButton(label: "1", onPressed: event "digit" { arguments: [1] }),
+ CalculatorButton(label: "2", onPressed: event "digit" { arguments: [2] }),
+ CalculatorButton(label: "3", onPressed: event "digit" { arguments: [3] }),
+ CalculatorButton(label: "=", onPressed: event "equals" { }),
+ ],
+ ),
+ Row(
+ children: [
+ SizedBox(width: 116.0, height: 116.0),
+ CalculatorButton(label: "0", onPressed: event "digit" { arguments: [0] }),
+ SizedBox(width: 116.0, height: 116.0),
+ SizedBox(width: 116.0, height: 116.0),
+ ],
+ ),
+ ],
+);
+
+widget CalculatorButton = Padding(
+ padding: [8.0],
+ child: SizedBox(
+ width: 100.0,
+ height: 100.0,
+ child: Button(
+ child: FittedBox(child: Text(text: args.label)),
+ onPressed: args.onPressed,
+ ),
+ ),
+);
+
+widget Button { down: false } = GestureDetector(
+ onTap: args.onPressed,
+ onTapDown: set state.down = true,
+ onTapUp: set state.down = false,
+ onTapCancel: set state.down = false,
+ child: Container(
+ duration: 50,
+ margin: switch state.down {
+ false: [ 0.0, 0.0, 2.0, 2.0 ],
+ true: [ 2.0, 2.0, 0.0, 0.0 ],
+ },
+ padding: [ 12.0, 8.0 ],
+ decoration: {
+ type: "shape",
+ shape: {
+ type: "stadium",
+ side: { width: 1.0 },
+ },
+ gradient: {
+ type: "linear",
+ begin: { x: -0.5, y: -0.25 },
+ end: { x: 0.0, y: 0.5 },
+ colors: [ 0xFFFFFF99, 0xFFEEDD00 ],
+ stops: [ 0.0, 1.0 ],
+ tileMode: "mirror",
+ },
+ shadows: switch state.down {
+ false: [ { blurRadius: 4.0, spreadRadius: 0.5, offset: { x: 1.0, y: 1.0, } } ],
+ default: [],
+ },
+ },
+ child: DefaultTextStyle(
+ style: {
+ color: 0xFF000000,
+ fontSize: 32.0,
+ },
+ child: args.child,
+ ),
+ ),
+);
+
+widget Display = Container(
+ height: 80.0,
+ margin: [18.0],
+ padding: [20.0, 12.0],
+ decoration: {
+ type: 'shape',
+ gradient: {
+ type: "linear",
+ begin: { x: 0.0, y: -0.5 },
+ end: { x: 0.05, y: 1.0 },
+ colors: [ 0xFFFFFFFF, 0xFFDDDDDD ],
+ stops: [ 0.0, 1.0 ],
+ tileMode: "mirror",
+ },
+ shape: {
+ type: "stadium",
+ borderRadius: [ { x: 40.0 } ],
+ side: { color: 0xFFCCCCCC, },
+ }
+ },
+ child: SizedBoxExpand(
+ child: FittedBox(
+ alignment: { x: 1.0, y: 0.0, },
+ child: Text(text: args.text, style: { color: 0xFF000000 }),
+ ),
+ ),
+);
diff --git a/packages/rfw/example/wasm/logic/calculator.wasm b/packages/rfw/example/wasm/logic/calculator.wasm
new file mode 100755
index 0000000..685c41e
--- /dev/null
+++ b/packages/rfw/example/wasm/logic/calculator.wasm
Binary files differ
diff --git a/packages/rfw/example/wasm/logic/encode.dart b/packages/rfw/example/wasm/logic/encode.dart
new file mode 100644
index 0000000..610c0b6
--- /dev/null
+++ b/packages/rfw/example/wasm/logic/encode.dart
@@ -0,0 +1,19 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:io';
+
+import 'package:rfw/formats.dart';
+
+void main(List<String> arguments) {
+ if (arguments.length != 2) {
+ print('usage: dart encode.dart source.rfwtxt output.rfw');
+ exit(1);
+ }
+ File(arguments[1]).writeAsBytesSync(
+ encodeLibraryBlob(
+ parseLibraryFile(File(arguments[0]).readAsStringSync()),
+ ),
+ );
+}
diff --git a/packages/rfw/example/wasm/macos/.gitignore b/packages/rfw/example/wasm/macos/.gitignore
new file mode 100644
index 0000000..d2fd377
--- /dev/null
+++ b/packages/rfw/example/wasm/macos/.gitignore
@@ -0,0 +1,6 @@
+# Flutter-related
+**/Flutter/ephemeral/
+**/Pods/
+
+# Xcode-related
+**/xcuserdata/
diff --git a/packages/rfw/example/wasm/macos/Flutter/Flutter-Debug.xcconfig b/packages/rfw/example/wasm/macos/Flutter/Flutter-Debug.xcconfig
new file mode 100644
index 0000000..c2efd0b
--- /dev/null
+++ b/packages/rfw/example/wasm/macos/Flutter/Flutter-Debug.xcconfig
@@ -0,0 +1 @@
+#include "ephemeral/Flutter-Generated.xcconfig"
diff --git a/packages/rfw/example/wasm/macos/Flutter/Flutter-Release.xcconfig b/packages/rfw/example/wasm/macos/Flutter/Flutter-Release.xcconfig
new file mode 100644
index 0000000..c2efd0b
--- /dev/null
+++ b/packages/rfw/example/wasm/macos/Flutter/Flutter-Release.xcconfig
@@ -0,0 +1 @@
+#include "ephemeral/Flutter-Generated.xcconfig"
diff --git a/packages/rfw/example/wasm/macos/Runner.xcodeproj/project.pbxproj b/packages/rfw/example/wasm/macos/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..19d957a
--- /dev/null
+++ b/packages/rfw/example/wasm/macos/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,572 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 51;
+ objects = {
+
+/* Begin PBXAggregateTarget section */
+ 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */;
+ buildPhases = (
+ 33CC111E2044C6BF0003C045 /* ShellScript */,
+ );
+ dependencies = (
+ );
+ name = "Flutter Assemble";
+ productName = FLX;
+ };
+/* End PBXAggregateTarget section */
+
+/* Begin PBXBuildFile section */
+ 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
+ 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
+ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
+ 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
+ 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 33CC111A2044C6BA0003C045;
+ remoteInfo = FLX;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 33CC110E2044A8840003C045 /* Bundle Framework */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Bundle Framework";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
+ 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
+ 33CC10ED2044A3C60003C045 /* wasm.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "wasm.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
+ 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
+ 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
+ 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = "<group>"; };
+ 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = "<group>"; };
+ 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = "<group>"; };
+ 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = "<group>"; };
+ 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = "<group>"; };
+ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
+ 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
+ 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 33CC10EA2044A3C60003C045 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 33BA886A226E78AF003329D5 /* Configs */ = {
+ isa = PBXGroup;
+ children = (
+ 33E5194F232828860026EE4D /* AppInfo.xcconfig */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 333000ED22D3DE5D00554162 /* Warnings.xcconfig */,
+ );
+ path = Configs;
+ sourceTree = "<group>";
+ };
+ 33CC10E42044A3C60003C045 = {
+ isa = PBXGroup;
+ children = (
+ 33FAB671232836740065AC1E /* Runner */,
+ 33CEB47122A05771004F2AC0 /* Flutter */,
+ 33CC10EE2044A3C60003C045 /* Products */,
+ D73912EC22F37F3D000D13A0 /* Frameworks */,
+ );
+ sourceTree = "<group>";
+ };
+ 33CC10EE2044A3C60003C045 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10ED2044A3C60003C045 /* wasm.app */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 33CC11242044D66E0003C045 /* Resources */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10F22044A3C60003C045 /* Assets.xcassets */,
+ 33CC10F42044A3C60003C045 /* MainMenu.xib */,
+ 33CC10F72044A3C60003C045 /* Info.plist */,
+ );
+ name = Resources;
+ path = ..;
+ sourceTree = "<group>";
+ };
+ 33CEB47122A05771004F2AC0 /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,
+ 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,
+ 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,
+ 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,
+ );
+ path = Flutter;
+ sourceTree = "<group>";
+ };
+ 33FAB671232836740065AC1E /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10F02044A3C60003C045 /* AppDelegate.swift */,
+ 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,
+ 33E51913231747F40026EE4D /* DebugProfile.entitlements */,
+ 33E51914231749380026EE4D /* Release.entitlements */,
+ 33CC11242044D66E0003C045 /* Resources */,
+ 33BA886A226E78AF003329D5 /* Configs */,
+ );
+ path = Runner;
+ sourceTree = "<group>";
+ };
+ D73912EC22F37F3D000D13A0 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 33CC10EC2044A3C60003C045 /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ 33CC10E92044A3C60003C045 /* Sources */,
+ 33CC10EA2044A3C60003C045 /* Frameworks */,
+ 33CC10EB2044A3C60003C045 /* Resources */,
+ 33CC110E2044A8840003C045 /* Bundle Framework */,
+ 3399D490228B24CF009A79C7 /* ShellScript */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 33CC11202044C79F0003C045 /* PBXTargetDependency */,
+ );
+ name = Runner;
+ productName = Runner;
+ productReference = 33CC10ED2044A3C60003C045 /* wasm.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 33CC10E52044A3C60003C045 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastSwiftUpdateCheck = 0920;
+ LastUpgradeCheck = 1300;
+ ORGANIZATIONNAME = "";
+ TargetAttributes = {
+ 33CC10EC2044A3C60003C045 = {
+ CreatedOnToolsVersion = 9.2;
+ LastSwiftMigration = 1100;
+ ProvisioningStyle = Automatic;
+ SystemCapabilities = {
+ com.apple.Sandbox = {
+ enabled = 1;
+ };
+ };
+ };
+ 33CC111A2044C6BA0003C045 = {
+ CreatedOnToolsVersion = 9.2;
+ ProvisioningStyle = Manual;
+ };
+ };
+ };
+ buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 33CC10E42044A3C60003C045;
+ productRefGroup = 33CC10EE2044A3C60003C045 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 33CC10EC2044A3C60003C045 /* Runner */,
+ 33CC111A2044C6BA0003C045 /* Flutter Assemble */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 33CC10EB2044A3C60003C045 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,
+ 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 3399D490228B24CF009A79C7 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ );
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n";
+ };
+ 33CC111E2044C6BF0003C045 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ Flutter/ephemeral/FlutterInputs.xcfilelist,
+ );
+ inputPaths = (
+ Flutter/ephemeral/tripwire,
+ );
+ outputFileListPaths = (
+ Flutter/ephemeral/FlutterOutputs.xcfilelist,
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 33CC10E92044A3C60003C045 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,
+ 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,
+ 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 33CC11202044C79F0003C045 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;
+ targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+ 33CC10F42044A3C60003C045 /* MainMenu.xib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 33CC10F52044A3C60003C045 /* Base */,
+ );
+ name = MainMenu.xib;
+ path = Runner;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 338D0CE9231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = macosx;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ };
+ name = Profile;
+ };
+ 338D0CEA231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Profile;
+ };
+ 338D0CEB231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Manual;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Profile;
+ };
+ 33CC10F92044A3C60003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ 33CC10FA2044A3C60003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = macosx;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ };
+ name = Release;
+ };
+ 33CC10FC2044A3C60003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Debug;
+ };
+ 33CC10FD2044A3C60003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Release;
+ };
+ 33CC111C2044C6BA0003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Manual;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ 33CC111D2044C6BA0003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Automatic;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC10F92044A3C60003C045 /* Debug */,
+ 33CC10FA2044A3C60003C045 /* Release */,
+ 338D0CE9231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC10FC2044A3C60003C045 /* Debug */,
+ 33CC10FD2044A3C60003C045 /* Release */,
+ 338D0CEA231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC111C2044C6BA0003C045 /* Debug */,
+ 33CC111D2044C6BA0003C045 /* Release */,
+ 338D0CEB231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 33CC10E52044A3C60003C045 /* Project object */;
+}
diff --git a/packages/rfw/example/wasm/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/rfw/example/wasm/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/packages/rfw/example/wasm/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IDEDidComputeMac32BitWarning</key>
+ <true/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/wasm/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/rfw/example/wasm/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 0000000..3ac0eab
--- /dev/null
+++ b/packages/rfw/example/wasm/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "1300"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "33CC10EC2044A3C60003C045"
+ BuildableName = "wasm.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "33CC10EC2044A3C60003C045"
+ BuildableName = "wasm.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <Testables>
+ </Testables>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "33CC10EC2044A3C60003C045"
+ BuildableName = "wasm.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Profile"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "33CC10EC2044A3C60003C045"
+ BuildableName = "wasm.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/packages/rfw/example/wasm/macos/Runner.xcworkspace/contents.xcworkspacedata b/packages/rfw/example/wasm/macos/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..1d526a1
--- /dev/null
+++ b/packages/rfw/example/wasm/macos/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "group:Runner.xcodeproj">
+ </FileRef>
+</Workspace>
diff --git a/packages/rfw/example/wasm/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/rfw/example/wasm/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/packages/rfw/example/wasm/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IDEDidComputeMac32BitWarning</key>
+ <true/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/wasm/macos/Runner/AppDelegate.swift b/packages/rfw/example/wasm/macos/Runner/AppDelegate.swift
new file mode 100644
index 0000000..5cec4c4
--- /dev/null
+++ b/packages/rfw/example/wasm/macos/Runner/AppDelegate.swift
@@ -0,0 +1,13 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import Cocoa
+import FlutterMacOS
+
+@NSApplicationMain
+class AppDelegate: FlutterAppDelegate {
+ override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
+ return true
+ }
+}
diff --git a/packages/rfw/example/wasm/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/rfw/example/wasm/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..a2ec33f
--- /dev/null
+++ b/packages/rfw/example/wasm/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,68 @@
+{
+ "images" : [
+ {
+ "size" : "16x16",
+ "idiom" : "mac",
+ "filename" : "app_icon_16.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "16x16",
+ "idiom" : "mac",
+ "filename" : "app_icon_32.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "32x32",
+ "idiom" : "mac",
+ "filename" : "app_icon_32.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "32x32",
+ "idiom" : "mac",
+ "filename" : "app_icon_64.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "128x128",
+ "idiom" : "mac",
+ "filename" : "app_icon_128.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "128x128",
+ "idiom" : "mac",
+ "filename" : "app_icon_256.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "256x256",
+ "idiom" : "mac",
+ "filename" : "app_icon_256.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "256x256",
+ "idiom" : "mac",
+ "filename" : "app_icon_512.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "app_icon_512.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "app_icon_1024.png",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/packages/rfw/example/wasm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/packages/rfw/example/wasm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
new file mode 100644
index 0000000..3c4935a
--- /dev/null
+++ b/packages/rfw/example/wasm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
Binary files differ
diff --git a/packages/rfw/example/wasm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/packages/rfw/example/wasm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png
new file mode 100644
index 0000000..ed4cc16
--- /dev/null
+++ b/packages/rfw/example/wasm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png
Binary files differ
diff --git a/packages/rfw/example/wasm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/packages/rfw/example/wasm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png
new file mode 100644
index 0000000..483be61
--- /dev/null
+++ b/packages/rfw/example/wasm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png
Binary files differ
diff --git a/packages/rfw/example/wasm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/packages/rfw/example/wasm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png
new file mode 100644
index 0000000..bcbf36d
--- /dev/null
+++ b/packages/rfw/example/wasm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png
Binary files differ
diff --git a/packages/rfw/example/wasm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/packages/rfw/example/wasm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png
new file mode 100644
index 0000000..9c0a652
--- /dev/null
+++ b/packages/rfw/example/wasm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png
Binary files differ
diff --git a/packages/rfw/example/wasm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/packages/rfw/example/wasm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png
new file mode 100644
index 0000000..e71a726
--- /dev/null
+++ b/packages/rfw/example/wasm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png
Binary files differ
diff --git a/packages/rfw/example/wasm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/packages/rfw/example/wasm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png
new file mode 100644
index 0000000..8a31fe2
--- /dev/null
+++ b/packages/rfw/example/wasm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png
Binary files differ
diff --git a/packages/rfw/example/wasm/macos/Runner/Base.lproj/MainMenu.xib b/packages/rfw/example/wasm/macos/Runner/Base.lproj/MainMenu.xib
new file mode 100644
index 0000000..537341a
--- /dev/null
+++ b/packages/rfw/example/wasm/macos/Runner/Base.lproj/MainMenu.xib
@@ -0,0 +1,339 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+ <dependencies>
+ <deployment identifier="macosx"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+ </dependencies>
+ <objects>
+ <customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
+ <connections>
+ <outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
+ </connections>
+ </customObject>
+ <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+ <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+ <customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="Runner" customModuleProvider="target">
+ <connections>
+ <outlet property="applicationMenu" destination="uQy-DD-JDr" id="XBo-yE-nKs"/>
+ <outlet property="mainFlutterWindow" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/>
+ </connections>
+ </customObject>
+ <customObject id="YLy-65-1bz" customClass="NSFontManager"/>
+ <menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
+ <items>
+ <menuItem title="APP_NAME" id="1Xt-HY-uBw">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="APP_NAME" systemMenu="apple" id="uQy-DD-JDr">
+ <items>
+ <menuItem title="About APP_NAME" id="5kV-Vb-QxS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
+ <menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
+ <menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
+ <menuItem title="Services" id="NMo-om-nkz">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
+ <menuItem title="Hide APP_NAME" keyEquivalent="h" id="Olw-nP-bQN">
+ <connections>
+ <action selector="hide:" target="-1" id="PnN-Uc-m68"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Show All" id="Kd2-mp-pUS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
+ <menuItem title="Quit APP_NAME" keyEquivalent="q" id="4sb-4s-VLi">
+ <connections>
+ <action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Edit" id="5QF-Oa-p0T">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Edit" id="W48-6f-4Dl">
+ <items>
+ <menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
+ <connections>
+ <action selector="undo:" target="-1" id="M6e-cu-g7V"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
+ <connections>
+ <action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
+ <menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
+ <connections>
+ <action selector="cut:" target="-1" id="YJe-68-I9s"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
+ <connections>
+ <action selector="copy:" target="-1" id="G1f-GL-Joy"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
+ <connections>
+ <action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Delete" id="pa3-QI-u2k">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
+ <connections>
+ <action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
+ <menuItem title="Find" id="4EN-yA-p0u">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Find" id="1b7-l0-nxx">
+ <items>
+ <menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
+ <connections>
+ <action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
+ <connections>
+ <action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
+ <connections>
+ <action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
+ <connections>
+ <action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
+ <connections>
+ <action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
+ <items>
+ <menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
+ <connections>
+ <action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
+ <connections>
+ <action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
+ <menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Substitutions" id="9ic-FL-obx">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
+ <items>
+ <menuItem title="Show Substitutions" id="z6F-FW-3nz">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
+ <menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smart Quotes" id="hQb-2v-fYv">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smart Dashes" id="rgM-f4-ycn">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smart Links" id="cwL-P1-jid">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Data Detectors" id="tRr-pd-1PS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Text Replacement" id="HFQ-gK-NFA">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Transformations" id="2oI-Rn-ZJC">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Transformations" id="c8a-y6-VQd">
+ <items>
+ <menuItem title="Make Upper Case" id="vmV-6d-7jI">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Make Lower Case" id="d9M-CD-aMd">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Capitalize" id="UEZ-Bs-lqG">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Speech" id="xrE-MZ-jX0">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Speech" id="3rS-ZA-NoH">
+ <items>
+ <menuItem title="Start Speaking" id="Ynk-f8-cLZ">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Stop Speaking" id="Oyz-dy-DGm">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="View" id="H8h-7b-M4v">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="View" id="HyV-fh-RgO">
+ <items>
+ <menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
+ <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+ <connections>
+ <action selector="toggleFullScreen:" target="-1" id="dU3-MA-1Rq"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Window" id="aUF-d1-5bR">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
+ <items>
+ <menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
+ <connections>
+ <action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Zoom" id="R4o-n2-Eq4">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
+ <menuItem title="Bring All to Front" id="LE2-aR-0XJ">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ </items>
+ <point key="canvasLocation" x="142" y="-258"/>
+ </menu>
+ <window title="APP_NAME" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g" customClass="MainFlutterWindow" customModule="Runner" customModuleProvider="target">
+ <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
+ <rect key="contentRect" x="335" y="390" width="800" height="600"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="2560" height="1577"/>
+ <view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
+ <rect key="frame" x="0.0" y="0.0" width="800" height="600"/>
+ <autoresizingMask key="autoresizingMask"/>
+ </view>
+ </window>
+ </objects>
+</document>
diff --git a/packages/rfw/example/wasm/macos/Runner/Configs/AppInfo.xcconfig b/packages/rfw/example/wasm/macos/Runner/Configs/AppInfo.xcconfig
new file mode 100644
index 0000000..76ca237
--- /dev/null
+++ b/packages/rfw/example/wasm/macos/Runner/Configs/AppInfo.xcconfig
@@ -0,0 +1,14 @@
+// Application-level settings for the Runner target.
+//
+// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the
+// future. If not, the values below would default to using the project name when this becomes a
+// 'flutter create' template.
+
+// The application's name. By default this is also the title of the Flutter window.
+PRODUCT_NAME = wasm
+
+// The application's bundle identifier
+PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.rfw.examples.wasm
+
+// The copyright displayed in application information
+PRODUCT_COPYRIGHT = Copyright © 2021 dev.flutter.rfw.examples. All rights reserved.
diff --git a/packages/rfw/example/wasm/macos/Runner/Configs/Debug.xcconfig b/packages/rfw/example/wasm/macos/Runner/Configs/Debug.xcconfig
new file mode 100644
index 0000000..36b0fd9
--- /dev/null
+++ b/packages/rfw/example/wasm/macos/Runner/Configs/Debug.xcconfig
@@ -0,0 +1,2 @@
+#include "../../Flutter/Flutter-Debug.xcconfig"
+#include "Warnings.xcconfig"
diff --git a/packages/rfw/example/wasm/macos/Runner/Configs/Release.xcconfig b/packages/rfw/example/wasm/macos/Runner/Configs/Release.xcconfig
new file mode 100644
index 0000000..dff4f49
--- /dev/null
+++ b/packages/rfw/example/wasm/macos/Runner/Configs/Release.xcconfig
@@ -0,0 +1,2 @@
+#include "../../Flutter/Flutter-Release.xcconfig"
+#include "Warnings.xcconfig"
diff --git a/packages/rfw/example/wasm/macos/Runner/Configs/Warnings.xcconfig b/packages/rfw/example/wasm/macos/Runner/Configs/Warnings.xcconfig
new file mode 100644
index 0000000..42bcbf4
--- /dev/null
+++ b/packages/rfw/example/wasm/macos/Runner/Configs/Warnings.xcconfig
@@ -0,0 +1,13 @@
+WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings
+GCC_WARN_UNDECLARED_SELECTOR = YES
+CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES
+CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
+CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
+CLANG_WARN_PRAGMA_PACK = YES
+CLANG_WARN_STRICT_PROTOTYPES = YES
+CLANG_WARN_COMMA = YES
+GCC_WARN_STRICT_SELECTOR_MATCH = YES
+CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES
+CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
+GCC_WARN_SHADOW = YES
+CLANG_WARN_UNREACHABLE_CODE = YES
diff --git a/packages/rfw/example/wasm/macos/Runner/DebugProfile.entitlements b/packages/rfw/example/wasm/macos/Runner/DebugProfile.entitlements
new file mode 100644
index 0000000..dddb8a3
--- /dev/null
+++ b/packages/rfw/example/wasm/macos/Runner/DebugProfile.entitlements
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.security.app-sandbox</key>
+ <true/>
+ <key>com.apple.security.cs.allow-jit</key>
+ <true/>
+ <key>com.apple.security.network.server</key>
+ <true/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/wasm/macos/Runner/Info.plist b/packages/rfw/example/wasm/macos/Runner/Info.plist
new file mode 100644
index 0000000..4789daa
--- /dev/null
+++ b/packages/rfw/example/wasm/macos/Runner/Info.plist
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>$(DEVELOPMENT_LANGUAGE)</string>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>$(PRODUCT_NAME)</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>$(FLUTTER_BUILD_NAME)</string>
+ <key>CFBundleVersion</key>
+ <string>$(FLUTTER_BUILD_NUMBER)</string>
+ <key>LSMinimumSystemVersion</key>
+ <string>$(MACOSX_DEPLOYMENT_TARGET)</string>
+ <key>NSHumanReadableCopyright</key>
+ <string>$(PRODUCT_COPYRIGHT)</string>
+ <key>NSMainNibFile</key>
+ <string>MainMenu</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+</dict>
+</plist>
diff --git a/packages/rfw/example/wasm/macos/Runner/MainFlutterWindow.swift b/packages/rfw/example/wasm/macos/Runner/MainFlutterWindow.swift
new file mode 100644
index 0000000..32aaeed
--- /dev/null
+++ b/packages/rfw/example/wasm/macos/Runner/MainFlutterWindow.swift
@@ -0,0 +1,19 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import Cocoa
+import FlutterMacOS
+
+class MainFlutterWindow: NSWindow {
+ override func awakeFromNib() {
+ let flutterViewController = FlutterViewController.init()
+ let windowFrame = self.frame
+ self.contentViewController = flutterViewController
+ self.setFrame(windowFrame, display: true)
+
+ RegisterGeneratedPlugins(registry: flutterViewController)
+
+ super.awakeFromNib()
+ }
+}
diff --git a/packages/rfw/example/wasm/macos/Runner/Release.entitlements b/packages/rfw/example/wasm/macos/Runner/Release.entitlements
new file mode 100644
index 0000000..852fa1a
--- /dev/null
+++ b/packages/rfw/example/wasm/macos/Runner/Release.entitlements
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.security.app-sandbox</key>
+ <true/>
+</dict>
+</plist>
diff --git a/packages/rfw/example/wasm/pubspec.yaml b/packages/rfw/example/wasm/pubspec.yaml
new file mode 100644
index 0000000..96af1a5
--- /dev/null
+++ b/packages/rfw/example/wasm/pubspec.yaml
@@ -0,0 +1,19 @@
+name: rfw_wasm
+description: Example of using Wasm with RFW
+publish_to: 'none' # Remove this line if you wish to publish to pub.dev
+version: 1.0.0+1
+
+environment:
+ sdk: ">=2.13.0 <3.0.0"
+
+dependencies:
+ flutter:
+ sdk: flutter
+ path: ^1.8.0
+ path_provider: ^2.0.2
+ rfw:
+ path: ../../
+ wasm: ">=0.1.0+1 <=2.0.0"
+
+flutter:
+ uses-material-design: true
diff --git a/packages/rfw/example/wasm/web/favicon.png b/packages/rfw/example/wasm/web/favicon.png
new file mode 100644
index 0000000..8aaa46a
--- /dev/null
+++ b/packages/rfw/example/wasm/web/favicon.png
Binary files differ
diff --git a/packages/rfw/example/wasm/web/icons/Icon-192.png b/packages/rfw/example/wasm/web/icons/Icon-192.png
new file mode 100644
index 0000000..b749bfe
--- /dev/null
+++ b/packages/rfw/example/wasm/web/icons/Icon-192.png
Binary files differ
diff --git a/packages/rfw/example/wasm/web/icons/Icon-512.png b/packages/rfw/example/wasm/web/icons/Icon-512.png
new file mode 100644
index 0000000..88cfd48
--- /dev/null
+++ b/packages/rfw/example/wasm/web/icons/Icon-512.png
Binary files differ
diff --git a/packages/rfw/example/wasm/web/icons/Icon-maskable-192.png b/packages/rfw/example/wasm/web/icons/Icon-maskable-192.png
new file mode 100644
index 0000000..eb9b4d7
--- /dev/null
+++ b/packages/rfw/example/wasm/web/icons/Icon-maskable-192.png
Binary files differ
diff --git a/packages/rfw/example/wasm/web/icons/Icon-maskable-512.png b/packages/rfw/example/wasm/web/icons/Icon-maskable-512.png
new file mode 100644
index 0000000..d69c566
--- /dev/null
+++ b/packages/rfw/example/wasm/web/icons/Icon-maskable-512.png
Binary files differ
diff --git a/packages/rfw/example/wasm/web/index.html b/packages/rfw/example/wasm/web/index.html
new file mode 100644
index 0000000..da703c9
--- /dev/null
+++ b/packages/rfw/example/wasm/web/index.html
@@ -0,0 +1,107 @@
+<!DOCTYPE html>
+<!-- Copyright 2013 The Flutter Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file. -->
+<html>
+<head>
+ <!--
+ If you are serving your web app in a path other than the root, change the
+ href value below to reflect the base path you are serving from.
+
+ The path provided below has to start and end with a slash "/" in order for
+ it to work correctly.
+
+ For more details:
+ * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
+
+ This is a placeholder for base href that will be replaced by the value of
+ the `--base-href` argument provided to `flutter build`.
+ -->
+ <base href="$FLUTTER_BASE_HREF">
+
+ <meta charset="UTF-8">
+ <meta content="IE=Edge" http-equiv="X-UA-Compatible">
+ <meta name="description" content="Example of using Wasm with RFW">
+
+ <!-- iOS meta tags & icons -->
+ <meta name="apple-mobile-web-app-capable" content="yes">
+ <meta name="apple-mobile-web-app-status-bar-style" content="black">
+ <meta name="apple-mobile-web-app-title" content="wasm">
+ <link rel="apple-touch-icon" href="icons/Icon-192.png">
+
+ <!-- Favicon -->
+ <link rel="icon" type="image/png" href="favicon.png"/>
+
+ <title>wasm</title>
+ <link rel="manifest" href="manifest.json">
+</head>
+<body>
+ <!-- This script installs service_worker.js to provide PWA functionality to
+ application. For more information, see:
+ https://developers.google.com/web/fundamentals/primers/service-workers -->
+ <script>
+ var serviceWorkerVersion = null;
+ var scriptLoaded = false;
+ function loadMainDartJs() {
+ if (scriptLoaded) {
+ return;
+ }
+ scriptLoaded = true;
+ var scriptTag = document.createElement('script');
+ scriptTag.src = 'main.dart.js';
+ scriptTag.type = 'application/javascript';
+ document.body.append(scriptTag);
+ }
+
+ if ('serviceWorker' in navigator) {
+ // Service workers are supported. Use them.
+ window.addEventListener('load', function () {
+ // Wait for registration to finish before dropping the <script> tag.
+ // Otherwise, the browser will load the script multiple times,
+ // potentially different versions.
+ var serviceWorkerUrl = 'flutter_service_worker.js?v=' + serviceWorkerVersion;
+ navigator.serviceWorker.register(serviceWorkerUrl)
+ .then((reg) => {
+ function waitForActivation(serviceWorker) {
+ serviceWorker.addEventListener('statechange', () => {
+ if (serviceWorker.state == 'activated') {
+ console.log('Installed new service worker.');
+ loadMainDartJs();
+ }
+ });
+ }
+ if (!reg.active && (reg.installing || reg.waiting)) {
+ // No active web worker and we have installed or are installing
+ // one for the first time. Simply wait for it to activate.
+ waitForActivation(reg.installing || reg.waiting);
+ } else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {
+ // When the app updates the serviceWorkerVersion changes, so we
+ // need to ask the service worker to update.
+ console.log('New service worker available.');
+ reg.update();
+ waitForActivation(reg.installing);
+ } else {
+ // Existing service worker is still good.
+ console.log('Loading app from service worker.');
+ loadMainDartJs();
+ }
+ });
+
+ // If service worker doesn't succeed in a reasonable amount of time,
+ // fallback to plaint <script> tag.
+ setTimeout(() => {
+ if (!scriptLoaded) {
+ console.warn(
+ 'Failed to load app from service worker. Falling back to plain <script> tag.',
+ );
+ loadMainDartJs();
+ }
+ }, 4000);
+ });
+ } else {
+ // Service workers not supported. Just drop the <script> tag.
+ loadMainDartJs();
+ }
+ </script>
+</body>
+</html>
diff --git a/packages/rfw/example/wasm/web/manifest.json b/packages/rfw/example/wasm/web/manifest.json
new file mode 100644
index 0000000..5f8a447
--- /dev/null
+++ b/packages/rfw/example/wasm/web/manifest.json
@@ -0,0 +1,35 @@
+{
+ "name": "wasm",
+ "short_name": "wasm",
+ "start_url": ".",
+ "display": "standalone",
+ "background_color": "#0175C2",
+ "theme_color": "#0175C2",
+ "description": "Example of using Wasm with RFW",
+ "orientation": "portrait-primary",
+ "prefer_related_applications": false,
+ "icons": [
+ {
+ "src": "icons/Icon-192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ },
+ {
+ "src": "icons/Icon-512.png",
+ "sizes": "512x512",
+ "type": "image/png"
+ },
+ {
+ "src": "icons/Icon-maskable-192.png",
+ "sizes": "192x192",
+ "type": "image/png",
+ "purpose": "maskable"
+ },
+ {
+ "src": "icons/Icon-maskable-512.png",
+ "sizes": "512x512",
+ "type": "image/png",
+ "purpose": "maskable"
+ }
+ ]
+}
diff --git a/packages/rfw/example/wasm/windows/.gitignore b/packages/rfw/example/wasm/windows/.gitignore
new file mode 100644
index 0000000..d492d0d
--- /dev/null
+++ b/packages/rfw/example/wasm/windows/.gitignore
@@ -0,0 +1,17 @@
+flutter/ephemeral/
+
+# Visual Studio user-specific files.
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# Visual Studio build-related files.
+x64/
+x86/
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
diff --git a/packages/rfw/example/wasm/windows/CMakeLists.txt b/packages/rfw/example/wasm/windows/CMakeLists.txt
new file mode 100644
index 0000000..697f916
--- /dev/null
+++ b/packages/rfw/example/wasm/windows/CMakeLists.txt
@@ -0,0 +1,95 @@
+cmake_minimum_required(VERSION 3.15)
+project(wasm LANGUAGES CXX)
+
+set(BINARY_NAME "wasm")
+
+cmake_policy(SET CMP0063 NEW)
+
+set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
+
+# Configure build options.
+get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+if(IS_MULTICONFIG)
+ set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
+ CACHE STRING "" FORCE)
+else()
+ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+ set(CMAKE_BUILD_TYPE "Debug" CACHE
+ STRING "Flutter build mode" FORCE)
+ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
+ "Debug" "Profile" "Release")
+ endif()
+endif()
+
+set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
+set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
+set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
+set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
+
+# Use Unicode for all projects.
+add_definitions(-DUNICODE -D_UNICODE)
+
+# Compilation settings that should be applied to most targets.
+function(APPLY_STANDARD_SETTINGS TARGET)
+ target_compile_features(${TARGET} PUBLIC cxx_std_17)
+ target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
+ target_compile_options(${TARGET} PRIVATE /EHsc)
+ target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
+ target_compile_definitions(${TARGET} PRIVATE "$<$<CONFIG:Debug>:_DEBUG>")
+endfunction()
+
+set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
+
+# Flutter library and tool build rules.
+add_subdirectory(${FLUTTER_MANAGED_DIR})
+
+# Application build
+add_subdirectory("runner")
+
+# Generated plugin build rules, which manage building the plugins and adding
+# them to the application.
+include(flutter/generated_plugins.cmake)
+
+
+# === Installation ===
+# Support files are copied into place next to the executable, so that it can
+# run in place. This is done instead of making a separate bundle (as on Linux)
+# so that building and running from within Visual Studio will work.
+set(BUILD_BUNDLE_DIR "$<TARGET_FILE_DIR:${BINARY_NAME}>")
+# Make the "install" step default, as it's required to run.
+set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
+if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+ set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
+endif()
+
+set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
+set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
+
+install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+
+if(PLUGIN_BUNDLED_LIBRARIES)
+ install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
+ DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endif()
+
+# Fully re-copy the assets directory on each build to avoid having stale files
+# from a previous install.
+set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
+install(CODE "
+ file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
+ " COMPONENT Runtime)
+install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
+ DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
+
+# Install the AOT library on non-Debug builds only.
+install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ CONFIGURATIONS Profile;Release
+ COMPONENT Runtime)
diff --git a/packages/rfw/example/wasm/windows/flutter/CMakeLists.txt b/packages/rfw/example/wasm/windows/flutter/CMakeLists.txt
new file mode 100644
index 0000000..b02c548
--- /dev/null
+++ b/packages/rfw/example/wasm/windows/flutter/CMakeLists.txt
@@ -0,0 +1,103 @@
+cmake_minimum_required(VERSION 3.15)
+
+set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
+
+# Configuration provided via flutter tool.
+include(${EPHEMERAL_DIR}/generated_config.cmake)
+
+# TODO: Move the rest of this into files in ephemeral. See
+# https://github.com/flutter/flutter/issues/57146.
+set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
+
+# === Flutter Library ===
+set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
+
+# Published to parent scope for install step.
+set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
+set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
+set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
+set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE)
+
+list(APPEND FLUTTER_LIBRARY_HEADERS
+ "flutter_export.h"
+ "flutter_windows.h"
+ "flutter_messenger.h"
+ "flutter_plugin_registrar.h"
+ "flutter_texture_registrar.h"
+)
+list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/")
+add_library(flutter INTERFACE)
+target_include_directories(flutter INTERFACE
+ "${EPHEMERAL_DIR}"
+)
+target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib")
+add_dependencies(flutter flutter_assemble)
+
+# === Wrapper ===
+list(APPEND CPP_WRAPPER_SOURCES_CORE
+ "core_implementations.cc"
+ "standard_codec.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
+list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
+ "plugin_registrar.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
+list(APPEND CPP_WRAPPER_SOURCES_APP
+ "flutter_engine.cc"
+ "flutter_view_controller.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
+
+# Wrapper sources needed for a plugin.
+add_library(flutter_wrapper_plugin STATIC
+ ${CPP_WRAPPER_SOURCES_CORE}
+ ${CPP_WRAPPER_SOURCES_PLUGIN}
+)
+apply_standard_settings(flutter_wrapper_plugin)
+set_target_properties(flutter_wrapper_plugin PROPERTIES
+ POSITION_INDEPENDENT_CODE ON)
+set_target_properties(flutter_wrapper_plugin PROPERTIES
+ CXX_VISIBILITY_PRESET hidden)
+target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
+target_include_directories(flutter_wrapper_plugin PUBLIC
+ "${WRAPPER_ROOT}/include"
+)
+add_dependencies(flutter_wrapper_plugin flutter_assemble)
+
+# Wrapper sources needed for the runner.
+add_library(flutter_wrapper_app STATIC
+ ${CPP_WRAPPER_SOURCES_CORE}
+ ${CPP_WRAPPER_SOURCES_APP}
+)
+apply_standard_settings(flutter_wrapper_app)
+target_link_libraries(flutter_wrapper_app PUBLIC flutter)
+target_include_directories(flutter_wrapper_app PUBLIC
+ "${WRAPPER_ROOT}/include"
+)
+add_dependencies(flutter_wrapper_app flutter_assemble)
+
+# === Flutter tool backend ===
+# _phony_ is a non-existent file to force this command to run every time,
+# since currently there's no way to get a full input/output list from the
+# flutter tool.
+set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_")
+set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE)
+add_custom_command(
+ OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
+ ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
+ ${CPP_WRAPPER_SOURCES_APP}
+ ${PHONY_OUTPUT}
+ COMMAND ${CMAKE_COMMAND} -E env
+ ${FLUTTER_TOOL_ENVIRONMENT}
+ "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
+ windows-x64 $<CONFIG>
+ VERBATIM
+)
+add_custom_target(flutter_assemble DEPENDS
+ "${FLUTTER_LIBRARY}"
+ ${FLUTTER_LIBRARY_HEADERS}
+ ${CPP_WRAPPER_SOURCES_CORE}
+ ${CPP_WRAPPER_SOURCES_PLUGIN}
+ ${CPP_WRAPPER_SOURCES_APP}
+)
diff --git a/packages/rfw/example/wasm/windows/flutter/generated_plugins.cmake b/packages/rfw/example/wasm/windows/flutter/generated_plugins.cmake
new file mode 100644
index 0000000..4d10c25
--- /dev/null
+++ b/packages/rfw/example/wasm/windows/flutter/generated_plugins.cmake
@@ -0,0 +1,15 @@
+#
+# Generated file, do not edit.
+#
+
+list(APPEND FLUTTER_PLUGIN_LIST
+)
+
+set(PLUGIN_BUNDLED_LIBRARIES)
+
+foreach(plugin ${FLUTTER_PLUGIN_LIST})
+ add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})
+ target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
+endforeach(plugin)
diff --git a/packages/rfw/example/wasm/windows/runner/CMakeLists.txt b/packages/rfw/example/wasm/windows/runner/CMakeLists.txt
new file mode 100644
index 0000000..0b899a0
--- /dev/null
+++ b/packages/rfw/example/wasm/windows/runner/CMakeLists.txt
@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 3.15)
+project(runner LANGUAGES CXX)
+
+add_executable(${BINARY_NAME} WIN32
+ "flutter_window.cpp"
+ "main.cpp"
+ "utils.cpp"
+ "win32_window.cpp"
+ "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
+ "Runner.rc"
+ "runner.exe.manifest"
+)
+apply_standard_settings(${BINARY_NAME})
+target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
+target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
+target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
+add_dependencies(${BINARY_NAME} flutter_assemble)
diff --git a/packages/rfw/example/wasm/windows/runner/Runner.rc b/packages/rfw/example/wasm/windows/runner/Runner.rc
new file mode 100644
index 0000000..7535ea4
--- /dev/null
+++ b/packages/rfw/example/wasm/windows/runner/Runner.rc
@@ -0,0 +1,121 @@
+// Microsoft Visual C++ generated resource script.
+//
+#pragma code_page(65001)
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (United States) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""winres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_APP_ICON ICON "resources\\app_icon.ico"
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+#ifdef FLUTTER_BUILD_NUMBER
+#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER
+#else
+#define VERSION_AS_NUMBER 1,0,0
+#endif
+
+#ifdef FLUTTER_BUILD_NAME
+#define VERSION_AS_STRING #FLUTTER_BUILD_NAME
+#else
+#define VERSION_AS_STRING "1.0.0"
+#endif
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VERSION_AS_NUMBER
+ PRODUCTVERSION VERSION_AS_NUMBER
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+#ifdef _DEBUG
+ FILEFLAGS VS_FF_DEBUG
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_APP
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "CompanyName", "dev.flutter.rfw.examples" "\0"
+ VALUE "FileDescription", "Example of using Wasm with RFW" "\0"
+ VALUE "FileVersion", VERSION_AS_STRING "\0"
+ VALUE "InternalName", "wasm" "\0"
+ VALUE "LegalCopyright", "Copyright (C) 2021 dev.flutter.rfw.examples. All rights reserved." "\0"
+ VALUE "OriginalFilename", "wasm.exe" "\0"
+ VALUE "ProductName", "wasm" "\0"
+ VALUE "ProductVersion", VERSION_AS_STRING "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252
+ END
+END
+
+#endif // English (United States) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
diff --git a/packages/rfw/example/wasm/windows/runner/flutter_window.cpp b/packages/rfw/example/wasm/windows/runner/flutter_window.cpp
new file mode 100644
index 0000000..8254bd9
--- /dev/null
+++ b/packages/rfw/example/wasm/windows/runner/flutter_window.cpp
@@ -0,0 +1,65 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "flutter_window.h"
+
+#include <optional>
+
+#include "flutter/generated_plugin_registrant.h"
+
+FlutterWindow::FlutterWindow(const flutter::DartProject& project)
+ : project_(project) {}
+
+FlutterWindow::~FlutterWindow() {}
+
+bool FlutterWindow::OnCreate() {
+ if (!Win32Window::OnCreate()) {
+ return false;
+ }
+
+ RECT frame = GetClientArea();
+
+ // The size here must match the window dimensions to avoid unnecessary surface
+ // creation / destruction in the startup path.
+ flutter_controller_ = std::make_unique<flutter::FlutterViewController>(
+ frame.right - frame.left, frame.bottom - frame.top, project_);
+ // Ensure that basic setup of the controller was successful.
+ if (!flutter_controller_->engine() || !flutter_controller_->view()) {
+ return false;
+ }
+ RegisterPlugins(flutter_controller_->engine());
+ SetChildContent(flutter_controller_->view()->GetNativeWindow());
+ return true;
+}
+
+void FlutterWindow::OnDestroy() {
+ if (flutter_controller_) {
+ flutter_controller_ = nullptr;
+ }
+
+ Win32Window::OnDestroy();
+}
+
+LRESULT
+FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept {
+ // Give Flutter, including plugins, an opportunity to handle window messages.
+ if (flutter_controller_) {
+ std::optional<LRESULT> result =
+ flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
+ lparam);
+ if (result) {
+ return *result;
+ }
+ }
+
+ switch (message) {
+ case WM_FONTCHANGE:
+ flutter_controller_->engine()->ReloadSystemFonts();
+ break;
+ }
+
+ return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
+}
diff --git a/packages/rfw/example/wasm/windows/runner/flutter_window.h b/packages/rfw/example/wasm/windows/runner/flutter_window.h
new file mode 100644
index 0000000..f1fc669
--- /dev/null
+++ b/packages/rfw/example/wasm/windows/runner/flutter_window.h
@@ -0,0 +1,37 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef RUNNER_FLUTTER_WINDOW_H_
+#define RUNNER_FLUTTER_WINDOW_H_
+
+#include <flutter/dart_project.h>
+#include <flutter/flutter_view_controller.h>
+
+#include <memory>
+
+#include "win32_window.h"
+
+// A window that does nothing but host a Flutter view.
+class FlutterWindow : public Win32Window {
+ public:
+ // Creates a new FlutterWindow hosting a Flutter view running |project|.
+ explicit FlutterWindow(const flutter::DartProject& project);
+ virtual ~FlutterWindow();
+
+ protected:
+ // Win32Window:
+ bool OnCreate() override;
+ void OnDestroy() override;
+ LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
+ LPARAM const lparam) noexcept override;
+
+ private:
+ // The project to run.
+ flutter::DartProject project_;
+
+ // The Flutter instance hosted by this window.
+ std::unique_ptr<flutter::FlutterViewController> flutter_controller_;
+};
+
+#endif // RUNNER_FLUTTER_WINDOW_H_
diff --git a/packages/rfw/example/wasm/windows/runner/main.cpp b/packages/rfw/example/wasm/windows/runner/main.cpp
new file mode 100644
index 0000000..e18e5a4
--- /dev/null
+++ b/packages/rfw/example/wasm/windows/runner/main.cpp
@@ -0,0 +1,46 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <flutter/dart_project.h>
+#include <flutter/flutter_view_controller.h>
+#include <windows.h>
+
+#include "flutter_window.h"
+#include "utils.h"
+
+int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
+ _In_ wchar_t *command_line, _In_ int show_command) {
+ // Attach to console when present (e.g., 'flutter run') or create a
+ // new console when running with a debugger.
+ if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
+ CreateAndAttachConsole();
+ }
+
+ // Initialize COM, so that it is available for use in the library and/or
+ // plugins.
+ ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
+
+ flutter::DartProject project(L"data");
+
+ std::vector<std::string> command_line_arguments = GetCommandLineArguments();
+
+ project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
+
+ FlutterWindow window(project);
+ Win32Window::Point origin(10, 10);
+ Win32Window::Size size(1280, 720);
+ if (!window.CreateAndShow(L"wasm", origin, size)) {
+ return EXIT_FAILURE;
+ }
+ window.SetQuitOnClose(true);
+
+ ::MSG msg;
+ while (::GetMessage(&msg, nullptr, 0, 0)) {
+ ::TranslateMessage(&msg);
+ ::DispatchMessage(&msg);
+ }
+
+ ::CoUninitialize();
+ return EXIT_SUCCESS;
+}
diff --git a/packages/rfw/example/wasm/windows/runner/resource.h b/packages/rfw/example/wasm/windows/runner/resource.h
new file mode 100644
index 0000000..d5d958d
--- /dev/null
+++ b/packages/rfw/example/wasm/windows/runner/resource.h
@@ -0,0 +1,16 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by Runner.rc
+//
+#define IDI_APP_ICON 101
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 102
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/packages/rfw/example/wasm/windows/runner/resources/app_icon.ico b/packages/rfw/example/wasm/windows/runner/resources/app_icon.ico
new file mode 100644
index 0000000..c04e20c
--- /dev/null
+++ b/packages/rfw/example/wasm/windows/runner/resources/app_icon.ico
Binary files differ
diff --git a/packages/rfw/example/wasm/windows/runner/runner.exe.manifest b/packages/rfw/example/wasm/windows/runner/runner.exe.manifest
new file mode 100644
index 0000000..c977c4a
--- /dev/null
+++ b/packages/rfw/example/wasm/windows/runner/runner.exe.manifest
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <application xmlns="urn:schemas-microsoft-com:asm.v3">
+ <windowsSettings>
+ <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
+ </windowsSettings>
+ </application>
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <!-- Windows 10 -->
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
+ <!-- Windows 8.1 -->
+ <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
+ <!-- Windows 8 -->
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
+ <!-- Windows 7 -->
+ <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
+ </application>
+ </compatibility>
+</assembly>
diff --git a/packages/rfw/example/wasm/windows/runner/utils.cpp b/packages/rfw/example/wasm/windows/runner/utils.cpp
new file mode 100644
index 0000000..fb7e945
--- /dev/null
+++ b/packages/rfw/example/wasm/windows/runner/utils.cpp
@@ -0,0 +1,67 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "utils.h"
+
+#include <flutter_windows.h>
+#include <io.h>
+#include <stdio.h>
+#include <windows.h>
+
+#include <iostream>
+
+void CreateAndAttachConsole() {
+ if (::AllocConsole()) {
+ FILE* unused;
+ if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
+ _dup2(_fileno(stdout), 1);
+ }
+ if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
+ _dup2(_fileno(stdout), 2);
+ }
+ std::ios::sync_with_stdio();
+ FlutterDesktopResyncOutputStreams();
+ }
+}
+
+std::vector<std::string> GetCommandLineArguments() {
+ // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.
+ int argc;
+ wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
+ if (argv == nullptr) {
+ return std::vector<std::string>();
+ }
+
+ std::vector<std::string> command_line_arguments;
+
+ // Skip the first argument as it's the binary name.
+ for (int i = 1; i < argc; i++) {
+ command_line_arguments.push_back(Utf8FromUtf16(argv[i]));
+ }
+
+ ::LocalFree(argv);
+
+ return command_line_arguments;
+}
+
+std::string Utf8FromUtf16(const wchar_t* utf16_string) {
+ if (utf16_string == nullptr) {
+ return std::string();
+ }
+ int target_length =
+ ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1,
+ nullptr, 0, nullptr, nullptr);
+ if (target_length == 0) {
+ return std::string();
+ }
+ std::string utf8_string;
+ utf8_string.resize(target_length);
+ int converted_length = ::WideCharToMultiByte(
+ CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, utf8_string.data(),
+ target_length, nullptr, nullptr);
+ if (converted_length == 0) {
+ return std::string();
+ }
+ return utf8_string;
+}
diff --git a/packages/rfw/example/wasm/windows/runner/utils.h b/packages/rfw/example/wasm/windows/runner/utils.h
new file mode 100644
index 0000000..bd81e1e
--- /dev/null
+++ b/packages/rfw/example/wasm/windows/runner/utils.h
@@ -0,0 +1,23 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef RUNNER_UTILS_H_
+#define RUNNER_UTILS_H_
+
+#include <string>
+#include <vector>
+
+// Creates a console for the process, and redirects stdout and stderr to
+// it for both the runner and the Flutter library.
+void CreateAndAttachConsole();
+
+// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string
+// encoded in UTF-8. Returns an empty std::string on failure.
+std::string Utf8FromUtf16(const wchar_t* utf16_string);
+
+// Gets the command line arguments passed in as a std::vector<std::string>,
+// encoded in UTF-8. Returns an empty std::vector<std::string> on failure.
+std::vector<std::string> GetCommandLineArguments();
+
+#endif // RUNNER_UTILS_H_
diff --git a/packages/rfw/example/wasm/windows/runner/win32_window.cpp b/packages/rfw/example/wasm/windows/runner/win32_window.cpp
new file mode 100644
index 0000000..85aa361
--- /dev/null
+++ b/packages/rfw/example/wasm/windows/runner/win32_window.cpp
@@ -0,0 +1,241 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "win32_window.h"
+
+#include <flutter_windows.h>
+
+#include "resource.h"
+
+namespace {
+
+constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
+
+// The number of Win32Window objects that currently exist.
+static int g_active_window_count = 0;
+
+using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
+
+// Scale helper to convert logical scaler values to physical using passed in
+// scale factor
+int Scale(int source, double scale_factor) {
+ return static_cast<int>(source * scale_factor);
+}
+
+// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
+// This API is only needed for PerMonitor V1 awareness mode.
+void EnableFullDpiSupportIfAvailable(HWND hwnd) {
+ HMODULE user32_module = LoadLibraryA("User32.dll");
+ if (!user32_module) {
+ return;
+ }
+ auto enable_non_client_dpi_scaling =
+ reinterpret_cast<EnableNonClientDpiScaling*>(
+ GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
+ if (enable_non_client_dpi_scaling != nullptr) {
+ enable_non_client_dpi_scaling(hwnd);
+ FreeLibrary(user32_module);
+ }
+}
+
+} // namespace
+
+// Manages the Win32Window's window class registration.
+class WindowClassRegistrar {
+ public:
+ ~WindowClassRegistrar() = default;
+
+ // Returns the singleton registar instance.
+ static WindowClassRegistrar* GetInstance() {
+ if (!instance_) {
+ instance_ = new WindowClassRegistrar();
+ }
+ return instance_;
+ }
+
+ // Returns the name of the window class, registering the class if it hasn't
+ // previously been registered.
+ const wchar_t* GetWindowClass();
+
+ // Unregisters the window class. Should only be called if there are no
+ // instances of the window.
+ void UnregisterWindowClass();
+
+ private:
+ WindowClassRegistrar() = default;
+
+ static WindowClassRegistrar* instance_;
+
+ bool class_registered_ = false;
+};
+
+WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;
+
+const wchar_t* WindowClassRegistrar::GetWindowClass() {
+ if (!class_registered_) {
+ WNDCLASS window_class{};
+ window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
+ window_class.lpszClassName = kWindowClassName;
+ window_class.style = CS_HREDRAW | CS_VREDRAW;
+ window_class.cbClsExtra = 0;
+ window_class.cbWndExtra = 0;
+ window_class.hInstance = GetModuleHandle(nullptr);
+ window_class.hIcon =
+ LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
+ window_class.hbrBackground = 0;
+ window_class.lpszMenuName = nullptr;
+ window_class.lpfnWndProc = Win32Window::WndProc;
+ RegisterClass(&window_class);
+ class_registered_ = true;
+ }
+ return kWindowClassName;
+}
+
+void WindowClassRegistrar::UnregisterWindowClass() {
+ UnregisterClass(kWindowClassName, nullptr);
+ class_registered_ = false;
+}
+
+Win32Window::Win32Window() { ++g_active_window_count; }
+
+Win32Window::~Win32Window() {
+ --g_active_window_count;
+ Destroy();
+}
+
+bool Win32Window::CreateAndShow(const std::wstring& title, const Point& origin,
+ const Size& size) {
+ Destroy();
+
+ const wchar_t* window_class =
+ WindowClassRegistrar::GetInstance()->GetWindowClass();
+
+ const POINT target_point = {static_cast<LONG>(origin.x),
+ static_cast<LONG>(origin.y)};
+ HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
+ UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
+ double scale_factor = dpi / 96.0;
+
+ HWND window = CreateWindow(
+ window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
+ Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
+ Scale(size.width, scale_factor), Scale(size.height, scale_factor),
+ nullptr, nullptr, GetModuleHandle(nullptr), this);
+
+ if (!window) {
+ return false;
+ }
+
+ return OnCreate();
+}
+
+// static
+LRESULT CALLBACK Win32Window::WndProc(HWND const window, UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept {
+ if (message == WM_NCCREATE) {
+ auto window_struct = reinterpret_cast<CREATESTRUCT*>(lparam);
+ SetWindowLongPtr(window, GWLP_USERDATA,
+ reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));
+
+ auto that = static_cast<Win32Window*>(window_struct->lpCreateParams);
+ EnableFullDpiSupportIfAvailable(window);
+ that->window_handle_ = window;
+ } else if (Win32Window* that = GetThisFromHandle(window)) {
+ return that->MessageHandler(window, message, wparam, lparam);
+ }
+
+ return DefWindowProc(window, message, wparam, lparam);
+}
+
+LRESULT
+Win32Window::MessageHandler(HWND hwnd, UINT const message, WPARAM const wparam,
+ LPARAM const lparam) noexcept {
+ switch (message) {
+ case WM_DESTROY:
+ window_handle_ = nullptr;
+ Destroy();
+ if (quit_on_close_) {
+ PostQuitMessage(0);
+ }
+ return 0;
+
+ case WM_DPICHANGED: {
+ auto newRectSize = reinterpret_cast<RECT*>(lparam);
+ LONG newWidth = newRectSize->right - newRectSize->left;
+ LONG newHeight = newRectSize->bottom - newRectSize->top;
+
+ SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,
+ newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
+
+ return 0;
+ }
+ case WM_SIZE: {
+ RECT rect = GetClientArea();
+ if (child_content_ != nullptr) {
+ // Size and position the child window.
+ MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,
+ rect.bottom - rect.top, TRUE);
+ }
+ return 0;
+ }
+
+ case WM_ACTIVATE:
+ if (child_content_ != nullptr) {
+ SetFocus(child_content_);
+ }
+ return 0;
+ }
+
+ return DefWindowProc(window_handle_, message, wparam, lparam);
+}
+
+void Win32Window::Destroy() {
+ OnDestroy();
+
+ if (window_handle_) {
+ DestroyWindow(window_handle_);
+ window_handle_ = nullptr;
+ }
+ if (g_active_window_count == 0) {
+ WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
+ }
+}
+
+Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
+ return reinterpret_cast<Win32Window*>(
+ GetWindowLongPtr(window, GWLP_USERDATA));
+}
+
+void Win32Window::SetChildContent(HWND content) {
+ child_content_ = content;
+ SetParent(content, window_handle_);
+ RECT frame = GetClientArea();
+
+ MoveWindow(content, frame.left, frame.top, frame.right - frame.left,
+ frame.bottom - frame.top, true);
+
+ SetFocus(child_content_);
+}
+
+RECT Win32Window::GetClientArea() {
+ RECT frame;
+ GetClientRect(window_handle_, &frame);
+ return frame;
+}
+
+HWND Win32Window::GetHandle() { return window_handle_; }
+
+void Win32Window::SetQuitOnClose(bool quit_on_close) {
+ quit_on_close_ = quit_on_close;
+}
+
+bool Win32Window::OnCreate() {
+ // No-op; provided for subclasses.
+ return true;
+}
+
+void Win32Window::OnDestroy() {
+ // No-op; provided for subclasses.
+}
diff --git a/packages/rfw/example/wasm/windows/runner/win32_window.h b/packages/rfw/example/wasm/windows/runner/win32_window.h
new file mode 100644
index 0000000..d2a7300
--- /dev/null
+++ b/packages/rfw/example/wasm/windows/runner/win32_window.h
@@ -0,0 +1,99 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef RUNNER_WIN32_WINDOW_H_
+#define RUNNER_WIN32_WINDOW_H_
+
+#include <windows.h>
+
+#include <functional>
+#include <memory>
+#include <string>
+
+// A class abstraction for a high DPI-aware Win32 Window. Intended to be
+// inherited from by classes that wish to specialize with custom
+// rendering and input handling
+class Win32Window {
+ public:
+ struct Point {
+ unsigned int x;
+ unsigned int y;
+ Point(unsigned int x, unsigned int y) : x(x), y(y) {}
+ };
+
+ struct Size {
+ unsigned int width;
+ unsigned int height;
+ Size(unsigned int width, unsigned int height)
+ : width(width), height(height) {}
+ };
+
+ Win32Window();
+ virtual ~Win32Window();
+
+ // Creates and shows a win32 window with |title| and position and size using
+ // |origin| and |size|. New windows are created on the default monitor. Window
+ // sizes are specified to the OS in physical pixels, hence to ensure a
+ // consistent size to will treat the width height passed in to this function
+ // as logical pixels and scale to appropriate for the default monitor. Returns
+ // true if the window was created successfully.
+ bool CreateAndShow(const std::wstring& title, const Point& origin,
+ const Size& size);
+
+ // Release OS resources associated with window.
+ void Destroy();
+
+ // Inserts |content| into the window tree.
+ void SetChildContent(HWND content);
+
+ // Returns the backing Window handle to enable clients to set icon and other
+ // window properties. Returns nullptr if the window has been destroyed.
+ HWND GetHandle();
+
+ // If true, closing this window will quit the application.
+ void SetQuitOnClose(bool quit_on_close);
+
+ // Return a RECT representing the bounds of the current client area.
+ RECT GetClientArea();
+
+ protected:
+ // Processes and route salient window messages for mouse handling,
+ // size change and DPI. Delegates handling of these to member overloads that
+ // inheriting classes can handle.
+ virtual LRESULT MessageHandler(HWND window, UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept;
+
+ // Called when CreateAndShow is called, allowing subclass window-related
+ // setup. Subclasses should return false if setup fails.
+ virtual bool OnCreate();
+
+ // Called when Destroy is called.
+ virtual void OnDestroy();
+
+ private:
+ friend class WindowClassRegistrar;
+
+ // OS callback called by message pump. Handles the WM_NCCREATE message which
+ // is passed when the non-client area is being created and enables automatic
+ // non-client DPI scaling so that the non-client area automatically
+ // responsponds to changes in DPI. All other messages are handled by
+ // MessageHandler.
+ static LRESULT CALLBACK WndProc(HWND const window, UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept;
+
+ // Retrieves a class instance pointer for |window|
+ static Win32Window* GetThisFromHandle(HWND const window) noexcept;
+
+ bool quit_on_close_ = false;
+
+ // window handle for top level window.
+ HWND window_handle_ = nullptr;
+
+ // window handle for hosted content.
+ HWND child_content_ = nullptr;
+};
+
+#endif // RUNNER_WIN32_WINDOW_H_
diff --git a/packages/rfw/images/overview1.png b/packages/rfw/images/overview1.png
new file mode 100644
index 0000000..15fe045
--- /dev/null
+++ b/packages/rfw/images/overview1.png
Binary files differ
diff --git a/packages/rfw/images/overview2.png b/packages/rfw/images/overview2.png
new file mode 100644
index 0000000..33885cf
--- /dev/null
+++ b/packages/rfw/images/overview2.png
Binary files differ
diff --git a/packages/rfw/lib/dart/binary.dart b/packages/rfw/lib/dart/binary.dart
new file mode 100644
index 0000000..f2094db
--- /dev/null
+++ b/packages/rfw/lib/dart/binary.dart
@@ -0,0 +1,632 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is hand-formatted.
+
+// This file must not import `dart:ui`, directly or indirectly, as it is
+// intended to function even in pure Dart server or CLI environments.
+import 'dart:convert';
+import 'dart:typed_data';
+
+import 'model.dart';
+
+/// Encode data as a Remote Flutter Widgets binary data blob.
+///
+/// See also:
+///
+/// * [decodeDataBlob], which decodes this format.
+/// * [encodeLibraryBlob], which uses a superset of this format to encode
+/// Remote Flutter Widgets binary library blobs.
+Uint8List encodeDataBlob(Object value) {
+ final _BlobEncoder encoder = _BlobEncoder();
+ encoder.writeSignature(<int>[0xFE, 0x52, 0x57, 0x44]);
+ encoder.writeValue(value);
+ return encoder.bytes.toBytes();
+}
+
+/// Decode a Remote Flutter Widgets binary data blob.
+///
+/// This data is usually used in conjunction with [DynamicContent].
+///
+/// This method supports a subset of the format supported by
+/// [decodeLibraryBlob]; specifically, it reads a _value_ from that format
+/// (rather than a _library_), and disallows values other than maps, lists,
+/// ints, doubles, booleans, and strings. See [decodeLibraryBlob] for a
+/// description of the format.
+///
+/// The first four bytes of the file (in hex) are FE 52 57 44.
+///
+/// See also:
+///
+/// * [encodeDataBlob], which encodes this format.
+/// * [decodeLibraryBlob], which uses a superset of this format to decode
+/// Remote Flutter Widgets binary library blobs.
+/// * [parseDataFile], which parses the text variant of this format.
+Object decodeDataBlob(Uint8List bytes) {
+ final _BlobDecoder decoder = _BlobDecoder(bytes.buffer.asByteData(bytes.offsetInBytes, bytes.lengthInBytes));
+ decoder.expectSignature(<int>[0xFE, 0x52, 0x57, 0x44]);
+ final Object result = decoder.readValue();
+ if (!decoder.finished) {
+ throw const FormatException('Unexpected trailing bytes after value.');
+ }
+ return result;
+}
+
+/// Encode data as a Remote Flutter Widgets binary library blob.
+///
+/// See also:
+///
+/// * [decodeLibraryBlob], which decodes this format.
+/// * [encodeDataBlob], which uses a subset of this format to decode
+/// Remote Flutter Widgets binary data blobs.
+/// * [parseLibraryFile], which parses the text variant of this format.
+Uint8List encodeLibraryBlob(RemoteWidgetLibrary value) {
+ final _BlobEncoder encoder = _BlobEncoder();
+ encoder.writeSignature(<int>[0xFE, 0x52, 0x46, 0x57]);
+ encoder.writeLibrary(value);
+ return encoder.bytes.toBytes();
+}
+
+/// Decode a Remote Flutter Widgets binary library blob.
+///
+/// Remote widget libraries are usually used in conjunction with a [Runtime].
+///
+/// ## Format
+///
+/// This format is a depth-first serialization of the in-memory data structures,
+/// using a one-byte tag to identify types when necessary, and using 64 bit
+/// integers to encode lengths when necessary.
+///
+/// The first four bytes of the file (in hex) are FE 52 46 57.
+///
+/// Primitives in this format are as follows:
+///
+/// * Integers are encoded as little-endian two's complement 64 bit integers.
+/// For example, the number 513 (0x0000000000000201) is encoded as a 0x01
+/// byte, a 0x02 byte, and six 0x00 bytes, in that order.
+///
+/// * Doubles are encoded as little-endian IEEE binary64 numbers.
+///
+/// * Strings are encoded as an integer length followed by that many UTF-8
+/// encoded bytes.
+///
+/// For example, the string "Hello" would be encoded as:
+///
+/// 05 00 00 00 00 00 00 00 48 65 6C 6C 6F
+///
+/// * Lists are encoded as an integer length, followed by that many values
+/// back to back. When lists are of specific types (e.g. lists of imports),
+/// each value in the list is encoded directly (untagged lists); when the list
+/// can have multiple types, each value is prefixed by a tag giving the type,
+/// followed by the value (tagged lists). For example, a list of integers with
+/// the values 1 and 2 in that order would be encoded as:
+///
+/// 02 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00.
+///
+/// A list of arbitrary values that happens to contain one string "Hello"
+/// would be encoded as follows; 0x04 is the tag for "String" (the full list
+/// of tags is described below):
+///
+/// 01 00 00 00 00 00 00 00 04 05 00 00 00 00 00 00 00 48 65 6C 6C 6F
+///
+/// A list of length zero is eight zero bytes with no additional payload.
+///
+/// * Maps are encoded as an integer length, followed by key/value pairs. For
+/// maps where all the keys are strings (e.g. when encoding a [DynamicMap]),
+/// the keys are given without tags (an untagged map). For maps where the keys
+/// are of arbitrary values, the keys are prefixed by a tag byte (a tagged
+/// map; this is only used when encoding [Switch]es). The _values_ are always
+/// prefixed by a tag byte (all maps are over values of arbitrary types).
+///
+/// For example, the map `{ a: 15 }` (when the keys are known to always be
+/// strings, so they are untagged) is encoded as follows (0x02 is the tag for
+/// integers):
+///
+/// 01 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 61 02 0F 00 00 00 00 00 00 00
+///
+/// Objects are encoded as follows:
+///
+/// * [RemoteWidgetLibrary] objects are encoded as an untagged list of
+/// imports and an untagged list of widget declarations.
+///
+/// * Imports are encoded as an untagged list of strings, each of which is
+/// one of the subparts of the imported library name. For example, `import
+/// a.b` is encoded as:
+///
+/// 02 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 61 01 00 00 00 00 00 00 00 62
+///
+/// * Widget declarations are encoded as a string giving the declaration name,
+/// an untagged map for the initial state, and finally the value that
+/// represents the root of the widget declaration ([WidgetDeclaration.root],
+/// which is always either a [Switch] or a [ConstructorCall]).
+///
+/// When the widget's initial state is null, it is encoded as an empty map. By
+/// extension, this means no distinction is made between a "stateless" remote
+/// widget and a "stateful" remote widget whose initial state is empty. (This
+/// is reasonable since if the initial state is empty, no state can ever be
+/// changed, so the widget is in fact _de facto_ stateless.)
+///
+/// Values are encoded as a tag byte followed by their data, as follows:
+///
+/// * Booleans are encoded as just a tag, with the tag being 0x00 for false and
+/// 0x01 for true.
+///
+/// * Integers have the tag 0x02, and are encoded as described above (two's
+/// complement, little-endian, 64 bit).
+///
+/// * Doubles have the tag 0x03, and are encoded as described above
+/// (little-endian binary64).
+///
+/// * Strings have the tag 0x04, and are encoded as described above (length
+/// followed by UTF-8 bytes).
+///
+/// * Lists ([DynamicList]) have the tag 0x05, are encoded as described above
+/// (length followed by tagged values). (Lists of untagged values are never
+/// found in a "value" context.)
+///
+/// * Maps ([DynamicMap]) have the tag 0x07, are encoded as described above
+/// (length followed by pairs of strings and tagged values). (Tagged maps,
+/// i.e. those with tagged keys, are never found in a "value" context.)
+///
+/// * Loops ([Loop]) have the tag 0x08. They are encoded as two tagged values,
+/// the [Loop.input] and the [Loop.output].
+///
+/// * Constructor calls ([ConstructorCall]) have the tag 0x09. They are encoded
+/// as a string for the [ConstructorCall.name] followed by an untagged map
+/// describing the [ConstructorCall.arguments].
+///
+/// * Argument, data, and state references ([ArgsReference], [DataReference],
+/// and [StateReference] respectively) have tags 0x0A, 0x0B, and 0x0D
+/// respectively, and are encoded as tagged lists of strings or integers
+/// giving the [Reference.parts] of the reference.
+///
+/// * Loop references ([LoopReference]) have the tag 0x0C, and are encoded as an
+/// integer giving the number of [Loop] objects between the reference and the
+/// loop being referenced (this is similar to a De Bruijn index), followed by
+/// a tagged list of strings or integers giving the [Reference.parts] of the
+/// reference.
+///
+/// * Switches ([Switch]) have the tag 0x0F. They are encoded as a tagged value
+/// describing the control value ([Switch.input]), followed by a tagged map
+/// for the various case values ([Switch.outputs]). The default case is
+/// represented by a value with tag 0x10 (and no data).
+///
+/// For example, this switch:
+///
+/// ```
+/// switch (args.a) {
+/// 0: 'z',
+/// 1: 'o',
+/// default: 'd',
+/// }
+/// ```
+///
+/// ...is encoded as follows (including the tag for the switch itself):
+///
+/// 0F 0A 01 00 00 00 00 00 00 00 61 03 00 00 00 00
+/// 00 00 00 02 00 00 00 00 00 00 00 00 04 01 00 00
+/// 00 00 00 00 00 7A 02 01 00 00 00 00 00 00 00 04
+/// 01 00 00 00 00 00 00 00 6F 10 04 01 00 00 00 00
+/// 00 00 00 64
+///
+/// * Event handlers have the tag 0x0E, and are encoded as a string
+/// ([EventHandler.eventName]) and an untagged map
+/// ([EventHandler.eventArguments]).
+///
+/// * State-setting handlers have the tag 0x11, and are encoded as a tagged list
+/// of strings or integers giving the [Reference.parts] of the state reference
+/// ([SetStateHandler.stateReference]), followed by the tagged value to which
+/// to set that state entry ([SetStateHandler.value]).
+///
+/// See also:
+///
+/// * [encodeLibraryBlob], which encodes this format.
+/// * [decodeDataBlob], which uses a subset of this format to decode
+/// Remote Flutter Widgets binary data blobs.
+/// * [parseDataFile], which parses the text variant of this format.
+RemoteWidgetLibrary decodeLibraryBlob(Uint8List bytes) {
+ final _BlobDecoder decoder = _BlobDecoder(bytes.buffer.asByteData(bytes.offsetInBytes, bytes.lengthInBytes));
+ decoder.expectSignature(<int>[0xFE, 0x52, 0x46, 0x57]);
+ final RemoteWidgetLibrary result = decoder.readLibrary();
+ if (!decoder.finished) {
+ throw const FormatException('Unexpected trailing bytes after constructors.');
+ }
+ return result;
+}
+
+// endianess used by this format
+const Endian _blobEndian = Endian.little;
+
+// magic signatures
+const int _msFalse = 0x00;
+const int _msTrue = 0x01;
+const int _msInt64 = 0x02;
+const int _msBinary64 = 0x03;
+const int _msString = 0x04;
+const int _msList = 0x05;
+const int _msMap = 0x07;
+const int _msLoop = 0x08;
+const int _msWidget = 0x09;
+const int _msArgsReference = 0x0A;
+const int _msDataReference = 0x0B;
+const int _msLoopReference = 0x0C;
+const int _msStateReference = 0x0D;
+const int _msEvent = 0x0E;
+const int _msSwitch = 0x0F;
+const int _msDefault = 0x10;
+const int _msSetState = 0x11;
+
+/// API for decoding Remote Flutter Widgets binary blobs.
+///
+/// Binary data blobs can be decoded by using [readValue].
+///
+/// Binary library blobs can be decoded by using [readLibrary].
+///
+/// In either case, if [finished] returns false after parsing the root token,
+/// then there is unexpected further data in the file.
+class _BlobDecoder {
+ _BlobDecoder(this.bytes);
+
+ final ByteData bytes;
+
+ int _cursor = 0;
+
+ bool get finished => _cursor >= bytes.lengthInBytes;
+
+ void _advance(String context, int length) {
+ if (_cursor + length > bytes.lengthInBytes) {
+ throw FormatException('Could not read $context at offset $_cursor: unexpected end of file.');
+ }
+ _cursor += length;
+ }
+
+ int _readByte() {
+ final int byteOffset = _cursor;
+ _advance('byte', 1);
+ return bytes.getUint8(byteOffset);
+ }
+
+ int _readInt64() {
+ final int byteOffset = _cursor;
+ _advance('int64', 8);
+ return bytes.getInt64(byteOffset, _blobEndian);
+ }
+
+ double _readDouble() {
+ final int byteOffset = _cursor;
+ _advance('double', 8);
+ return bytes.getFloat64(byteOffset, _blobEndian);
+ }
+
+ String _readString() {
+ final int length = _readInt64();
+ final int byteOffset = _cursor;
+ _advance('string', length);
+ return utf8.decode(bytes.buffer.asUint8List(bytes.offsetInBytes + byteOffset, length));
+ }
+
+ List<Object> _readPartList() {
+ return List<Object>.generate(_readInt64(), (int index) {
+ final int type = _readByte();
+ switch (type) {
+ case _msString:
+ return _readString();
+ case _msInt64:
+ return _readInt64();
+ default:
+ throw FormatException('Invalid reference type 0x${type.toRadixString(16).toUpperCase().padLeft(2, "0")} while decoding blob.');
+ }
+ });
+ }
+
+ Map<String, Object?>? _readMap(Object Function() readNode, { bool nullIfEmpty = false }) {
+ final int count = _readInt64();
+ if (count == 0 && nullIfEmpty) {
+ return null;
+ }
+ return DynamicMap.fromEntries(
+ Iterable<MapEntry<String, Object>>.generate(
+ count,
+ (int index) => MapEntry<String, Object>(
+ _readString(),
+ readNode(),
+ ),
+ ),
+ );
+ }
+
+ Object? _readSwitchKey() {
+ final int type = _readByte();
+ if (type == _msDefault) {
+ return null;
+ }
+ return _parseArgument(type);
+ }
+
+ Switch _readSwitch() {
+ final Object value = _readArgument();
+ final int count = _readInt64();
+ final Map<Object?, Object> cases = Map<Object?, Object>.fromEntries(
+ Iterable<MapEntry<Object?, Object>>.generate(
+ count,
+ (int index) => MapEntry<Object?, Object>(
+ _readSwitchKey(),
+ _readArgument(),
+ ),
+ ),
+ );
+ return Switch(value, cases);
+ }
+
+ Object _parseValue(int type, Object Function() readNode) {
+ switch (type) {
+ case _msFalse:
+ return false;
+ case _msTrue:
+ return true;
+ case _msInt64:
+ return _readInt64();
+ case _msBinary64:
+ return _readDouble();
+ case _msString:
+ return _readString();
+ case _msList:
+ return DynamicList.generate(_readInt64(), (int index) => readNode());
+ case _msMap:
+ return _readMap(readNode)!;
+ default: throw FormatException('Unrecognized data type 0x${type.toRadixString(16).toUpperCase().padLeft(2, "0")} while decoding blob.');
+ }
+ }
+
+ Object readValue() {
+ final int type = _readByte();
+ return _parseValue(type, readValue);
+ }
+
+ Object _parseArgument(int type) {
+ switch (type) {
+ case _msLoop:
+ return Loop(_readArgument(), _readArgument());
+ case _msWidget:
+ return _readWidget();
+ case _msArgsReference:
+ return ArgsReference(_readPartList());
+ case _msDataReference:
+ return DataReference(_readPartList());
+ case _msLoopReference:
+ return LoopReference(_readInt64(), _readPartList());
+ case _msStateReference:
+ return StateReference(_readPartList());
+ case _msEvent:
+ return EventHandler(_readString(), _readMap(_readArgument)!);
+ case _msSwitch:
+ return _readSwitch();
+ case _msSetState:
+ return SetStateHandler(StateReference(_readPartList()), _readArgument());
+ default:
+ return _parseValue(type, _readArgument);
+ }
+ }
+
+ Object _readArgument() {
+ final int type = _readByte();
+ return _parseArgument(type);
+ }
+
+ ConstructorCall _readWidget() {
+ final String name = _readString();
+ return ConstructorCall(name, _readMap(_readArgument)!);
+ }
+
+ WidgetDeclaration _readDeclaration() {
+ final String name = _readString();
+ final DynamicMap? initialState = _readMap(readValue, nullIfEmpty: true);
+ final int type = _readByte();
+ final BlobNode root;
+ switch (type) {
+ case _msSwitch:
+ root = _readSwitch();
+ break;
+ case _msWidget:
+ root = _readWidget();
+ break;
+ default:
+ throw FormatException('Unrecognized data type 0x${type.toRadixString(16).toUpperCase().padLeft(2, "0")} while decoding widget declaration root.');
+ }
+ return WidgetDeclaration(name, initialState, root);
+ }
+
+ List<WidgetDeclaration> _readDeclarationList() {
+ return List<WidgetDeclaration>.generate(_readInt64(), (int index) => _readDeclaration());
+ }
+
+ Import _readImport() {
+ return Import(LibraryName(List<String>.generate(_readInt64(), (int index) => _readString())));
+ }
+
+ List<Import> _readImportList() {
+ return List<Import>.generate(_readInt64(), (int index) => _readImport());
+ }
+
+ RemoteWidgetLibrary readLibrary() {
+ return RemoteWidgetLibrary(_readImportList(), _readDeclarationList());
+ }
+
+ void expectSignature(List<int> signature) {
+ assert(signature.length == 4);
+ final List<int> bytes = <int>[];
+ bool match = true;
+ for (final int byte in signature) {
+ final int read = _readByte();
+ bytes.add(read);
+ if (read != byte) {
+ match = false;
+ }
+ }
+ if (!match) {
+ throw FormatException(
+ 'File signature mismatch. '
+ 'Expected ${signature.map<String>((int byte) => byte.toRadixString(16).toUpperCase().padLeft(2, "0")).join(" ")} '
+ 'but found ${bytes.map<String>((int byte) => byte.toRadixString(16).toUpperCase().padLeft(2, "0")).join(" ")}.'
+ );
+ }
+ }
+}
+
+/// API for encoding Remote Flutter Widgets binary blobs.
+///
+/// Binary data blobs can be serialized using [writeValue].
+///
+/// Binary library blobs can be serialized using [writeLibrary].
+///
+/// The output is in [bytes], and can be cleared manually to reuse the [_BlobEncoder].
+class _BlobEncoder {
+ _BlobEncoder();
+
+ static final Uint8List _scratchOut = Uint8List(8);
+ static final ByteData _scratchIn = _scratchOut.buffer.asByteData(_scratchOut.offsetInBytes, _scratchOut.lengthInBytes);
+
+ final BytesBuilder bytes = BytesBuilder(); // copying builder -- we repeatedly add _scratchOut after changing it
+
+ void _writeInt64(int value) {
+ _scratchIn.setInt64(0, value, _blobEndian);
+ bytes.add(_scratchOut);
+ }
+
+ void _writeString(String value) {
+ final Uint8List buffer = utf8.encode(value) as Uint8List;
+ _writeInt64(buffer.length);
+ bytes.add(buffer);
+ }
+
+ void _writeMap(DynamicMap value, void Function(Object? value) recurse) {
+ _writeInt64(value.length);
+ value.forEach((String key, Object? value) {
+ _writeString(key);
+ recurse(value);
+ });
+ }
+
+ void _writePart(Object? value) {
+ if (value is int) {
+ bytes.addByte(_msInt64);
+ _writeInt64(value);
+ } else if (value is String) {
+ bytes.addByte(_msString);
+ _writeString(value);
+ } else {
+ throw StateError('Unexpected type ${value.runtimeType} while encoding blob.');
+ }
+ }
+
+ void _writeValue(Object? value, void Function(Object? value) recurse) {
+ if (value == false) {
+ bytes.addByte(_msFalse);
+ } else if (value == true) {
+ bytes.addByte(_msTrue);
+ } else if (value is double) {
+ bytes.addByte(_msBinary64);
+ _scratchIn.setFloat64(0, value, _blobEndian);
+ bytes.add(_scratchOut);
+ } else if (value is DynamicList) {
+ bytes.addByte(_msList);
+ _writeInt64(value.length);
+ value.forEach(recurse);
+ } else if (value is DynamicMap) {
+ bytes.addByte(_msMap);
+ _writeMap(value, recurse);
+ } else {
+ _writePart(value);
+ }
+ }
+
+ void writeValue(Object? value) {
+ _writeValue(value, writeValue);
+ }
+
+ void _writeArgument(Object? value) {
+ if (value is Loop) {
+ bytes.addByte(_msLoop);
+ _writeArgument(value.input);
+ _writeArgument(value.output);
+ } else if (value is ConstructorCall) {
+ bytes.addByte(_msWidget);
+ _writeString(value.name);
+ _writeMap(value.arguments, _writeArgument);
+ } else if (value is ArgsReference) {
+ bytes.addByte(_msArgsReference);
+ _writeInt64(value.parts.length);
+ value.parts.forEach(_writePart);
+ } else if (value is DataReference) {
+ bytes.addByte(_msDataReference);
+ _writeInt64(value.parts.length);
+ value.parts.forEach(_writePart);
+ } else if (value is LoopReference) {
+ bytes.addByte(_msLoopReference);
+ _writeInt64(value.loop);
+ _writeInt64(value.parts.length);
+ value.parts.forEach(_writePart);
+ } else if (value is StateReference) {
+ bytes.addByte(_msStateReference);
+ _writeInt64(value.parts.length);
+ value.parts.forEach(_writePart);
+ } else if (value is EventHandler) {
+ bytes.addByte(_msEvent);
+ _writeString(value.eventName);
+ _writeMap(value.eventArguments, _writeArgument);
+ } else if (value is Switch) {
+ bytes.addByte(_msSwitch);
+ _writeArgument(value.input);
+ _writeInt64(value.outputs.length);
+ value.outputs.forEach((Object? key, Object value) {
+ if (key == null) {
+ bytes.addByte(_msDefault);
+ } else {
+ _writeArgument(key);
+ }
+ _writeArgument(value);
+ });
+ } else if (value is SetStateHandler) {
+ bytes.addByte(_msSetState);
+ final StateReference reference = value.stateReference as StateReference;
+ _writeInt64(reference.parts.length);
+ reference.parts.forEach(_writePart);
+ _writeArgument(value.value);
+ } else {
+ assert(value is! BlobNode);
+ _writeValue(value, _writeArgument);
+ }
+ }
+
+ void _writeDeclarationList(List<WidgetDeclaration> value) {
+ _writeInt64(value.length);
+ for (final WidgetDeclaration declaration in value) {
+ _writeString(declaration.name);
+ if (declaration.initialState != null) {
+ _writeMap(declaration.initialState!, _writeArgument);
+ } else {
+ _writeInt64(0);
+ }
+ _writeArgument(declaration.root);
+ }
+ }
+
+ void _writeImportList(List<Import> value) {
+ _writeInt64(value.length);
+ for (final Import import in value) {
+ _writeInt64(import.name.parts.length);
+ import.name.parts.forEach(_writeString);
+ }
+ }
+
+ void writeLibrary(RemoteWidgetLibrary library) {
+ _writeImportList(library.imports);
+ _writeDeclarationList(library.widgets);
+ }
+
+ void writeSignature(List<int> signature) {
+ assert(signature.length == 4);
+ bytes.add(signature);
+ }
+}
diff --git a/packages/rfw/lib/dart/model.dart b/packages/rfw/lib/dart/model.dart
new file mode 100644
index 0000000..13d8382
--- /dev/null
+++ b/packages/rfw/lib/dart/model.dart
@@ -0,0 +1,649 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is hand-formatted.
+
+// This file must not import `dart:ui`, directly or indirectly, as it is
+// intended to function even in pure Dart server or CLI environments.
+import 'package:meta/meta.dart';
+
+/// A map whose keys are strings and whose values are [DynamicMap],
+/// [DynamicList], int, double, bool, string, and [BlobNode] objects.
+///
+/// Part of the data type for [DynamicContent] objects.
+typedef DynamicMap = Map<String, Object?>;
+
+/// A list whose values are [DynamicMap], [DynamicList], int, double, bool,
+/// string, and [BlobNode] objects.
+///
+/// Part of the data type for [DynamicContent] objects.
+typedef DynamicList = List<Object?>;
+
+/// Base class of nodes that appear in the output of [decodeDataBlob] and
+/// [decodeLibraryBlob].
+///
+/// In addition to this, the following types can be found in that output:
+///
+/// * [DynamicMap]
+/// * [DynamicList]
+/// * [int]
+/// * [double]
+/// * [bool]
+/// * [String]
+abstract class BlobNode {
+ /// Abstract const constructor. This constructor enables subclasses to provide
+ /// const constructors so that they can be used in const expressions.
+ const BlobNode();
+}
+
+bool _listEquals<T>(List<T>? a, List<T>? b) {
+ if (identical(a, b)) {
+ return true;
+ }
+ if (a == null || b == null || a.length != b.length) {
+ return false;
+ }
+ for (int index = 0; index < a.length; index += 1) {
+ if (a[index] != b[index]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/// The name of a widgets library in the RFW package.
+///
+/// Libraries are typically referred to with names like "core.widgets" or
+/// "com.example.shopping.cart". This class represents these names as lists of
+/// tokens, in those cases `['core', 'widgets']` and `['com', 'example',
+/// 'shopping', 'cart']`, for example.
+@immutable
+class LibraryName implements Comparable<LibraryName> {
+ /// Wrap the given list as a [LibraryName].
+ ///
+ /// The given list is not copied; it is an error to modify it after creating
+ /// the [LibraryName].
+ const LibraryName(this.parts);
+
+ /// The components of the structured library name.
+ final List<String> parts;
+
+ @override
+ bool operator ==(Object other) {
+ if (other.runtimeType != runtimeType) {
+ return false;
+ }
+ return other is LibraryName
+ && _listEquals<String>(parts, other.parts);
+ }
+
+ @override
+ int get hashCode => Object.hashAll(parts);
+
+ @override
+ String toString() => parts.join('.');
+
+ @override
+ int compareTo(LibraryName other) {
+ for (int index = 0; index < parts.length; index += 1) {
+ if (other.parts.length <= index) {
+ return 1;
+ }
+ final int result = parts[index].compareTo(other.parts[index]);
+ if (result != 0) {
+ return result;
+ }
+ }
+ assert(other.parts.length >= parts.length);
+ return parts.length - other.parts.length;
+ }
+}
+
+/// The name of a widget used by the RFW package, including its library name.
+///
+/// This can be used to identify both local widgets and remote widgets.
+@immutable
+class FullyQualifiedWidgetName implements Comparable<FullyQualifiedWidgetName> {
+ /// Wrap the given library name and widget name in a [FullyQualifiedWidgetName].
+ const FullyQualifiedWidgetName(this.library, this.widget);
+
+ /// The name of the library in which [widget] can be found.
+ final LibraryName library;
+
+ /// The name of the widget, which should be in the specified [library].
+ final String widget;
+
+ @override
+ bool operator ==(Object other) {
+ if (other.runtimeType != runtimeType) {
+ return false;
+ }
+ return other is FullyQualifiedWidgetName
+ && library == other.library && widget == other.widget;
+ }
+
+ @override
+ int get hashCode => Object.hash(library, widget);
+
+ @override
+ String toString() => '$library:$widget';
+
+ @override
+ int compareTo(FullyQualifiedWidgetName other) {
+ final int result = library.compareTo(other.library);
+ if (result != 0) {
+ return result;
+ }
+ return widget.compareTo(other.widget);
+ }
+}
+
+/// The type of the [missing] value.
+///
+/// This is used internally by the RFW package to avoid needing to use nullable
+/// types.
+class Missing extends BlobNode {
+ const Missing._();
+
+ @override
+ String toString() => '<missing>';
+}
+
+/// The value used by [DynamicContent] to represent missing data.
+///
+/// This is return from [DynamicContent.subscribe] when the specified key is not
+/// present.
+///
+/// Content in a [DynamicContent] should not contain [missing] values.
+const Missing missing = Missing._();
+
+/// Representation of the `...for` construct in Remote Flutter Widgets library
+/// blobs.
+class Loop extends BlobNode {
+ /// Creates a [Loop] with the given [input] and [output].
+ ///
+ /// The provided objects must not be mutated after being given to the
+ /// constructor (e.g. the ownership of any lists and maps passes to this
+ /// object).
+ const Loop(this.input, this.output);
+
+ /// The list on which to iterate.
+ ///
+ /// This is typically some sort of [Reference], but could be a [DynamicList].
+ ///
+ /// It is an error for this to be a value that does not resolve to a list.
+ final Object input;
+
+ /// The template to apply for each value on [input].
+ final Object output;
+
+ @override
+ String toString() => '...for loop in $input: $output';
+}
+
+/// Representation of the `switch` construct in Remote Flutter Widgets library
+/// blobs.
+class Switch extends BlobNode {
+ /// Creates a [Switch] with the given [input] and [outputs].
+ ///
+ /// The provided objects must not be mutated after being given to the
+ /// constructor. In particular, changing the [outputs] map after creating the
+ /// [Switch] is an error.
+ const Switch(this.input, this.outputs);
+
+ /// The value to switch on (after resolution).
+ final Object input;
+
+ /// The cases for this switch. Keys correspond to values to compare with
+ /// [input]. The null value is used as the default case.
+ ///
+ /// At runtime, if none of the keys match [input] and there is no null key,
+ /// the [Switch] as a whole is treated as if it was [missing]. If the [Switch]
+ /// is used where a [ConstructorCall] was expected, the result is an
+ /// [ErrorWidget].
+ final Map<Object?, Object> outputs;
+
+ @override
+ String toString() => 'switch $input $outputs';
+}
+
+/// Representation of references to widgets in Remote Flutter Widgets library
+/// blobs.
+class ConstructorCall extends BlobNode {
+ /// Creates a [ConstructorCall] for a widget of the given name in the current
+ /// library's scope, with the given [arguments].
+ ///
+ /// The [arguments] must not be mutated after the object is created.
+ const ConstructorCall(this.name, this.arguments);
+
+ /// The name of the widget to create.
+ ///
+ /// The name is looked up in the current library, or, failing that, in a
+ /// depth-first search of this library's dependencies.
+ final String name;
+
+ /// The arguments to pass to the constructor.
+ ///
+ /// Constructors in RFW only have named arguments. This differs from Dart
+ /// (where arguments can also be positional.)
+ final DynamicMap arguments;
+
+ @override
+ String toString() => '$name($arguments)';
+}
+
+/// Base class for various kinds of references in the RFW data structures.
+abstract class Reference extends BlobNode {
+ /// Abstract const constructor. This constructor enables subclasses to provide
+ /// const constructors so that they can be used in const expressions.
+ ///
+ /// The [parts] must not be mutated after the object is created.
+ const Reference(this.parts);
+
+ /// The components of the reference. Each entry must be either a String (to
+ /// index into a [DynamicMap]) an integer (to index into a [DynamicList]).
+ ///
+ /// It is an error for any of the parts to be of any other type.
+ final List<Object> parts;
+}
+
+/// Unbound reference to arguments.
+///
+/// This class is used to represent references of the form "args.foo.bar" after
+/// parsing, before the arguments are bound.
+class ArgsReference extends Reference {
+ /// Wraps the given [parts] as an [ArgsReference].
+ ///
+ /// The [parts] must not be mutated after the object is created.
+ const ArgsReference(List<Object> parts): super(parts);
+
+ /// Binds the arguments reference to a specific set of arguments.
+ ///
+ /// Returns a [BoundArgsReference] with the same [parts] and whose
+ /// [BoundArgsReference.arguments] is given by `arguments`.
+ BoundArgsReference bind(Object arguments) {
+ return BoundArgsReference(arguments, parts);
+ }
+
+ @override
+ String toString() => 'args.${parts.join(".")}';
+}
+
+/// Bound reference to arguments.
+///
+/// This class is used to represent references of the form "args.foo.bar" after
+/// a widget declaration has been bound to specific arguments via a constructor
+/// call. The [arguments] property is a reference to the
+/// [ConstructorCall.arguments] object (or, more typically, a clone of that
+/// object that itself has had references within it bound).
+///
+/// This class is an internal detail of the RFW [Runtime] and is generally not
+/// used directly.
+class BoundArgsReference extends Reference {
+ /// Wraps the given [parts] and [arguments] as an [ArgsReference].
+ ///
+ /// The parameters must not be mutated after the object is created.
+ ///
+ /// Generally this class is created using [ArgsReference.bind].
+ const BoundArgsReference(this.arguments, List<Object> parts): super(parts);
+
+ /// The object into which [parts] will be indexed.
+ ///
+ /// This could contain [Loop]s, which is why it cannot be indexed immediately
+ /// upon creation.
+ final Object arguments;
+
+ @override
+ String toString() => 'args($arguments).${parts.join(".")}';
+}
+
+/// Reference to the [DynamicContent] data that is passed into the widget (see
+/// [Runtime.build]'s `data` argument).
+class DataReference extends Reference {
+ /// Wraps the given [parts] as an [DataReference].
+ ///
+ /// The [parts] must not be mutated after the object is created.
+ const DataReference(List<Object> parts): super(parts);
+
+ /// Creates a new [DataRefererence] that indexes even deeper than this one.
+ ///
+ /// For example, suppose a widget's arguments consisted of a map with one key,
+ /// "a", whose value was a [DataRefererence] referencing "data.foo.bar". Now
+ /// suppose that the widget itself has an [ArgsReference] that references
+ /// "args.a.baz". The "args.a" part identifies the aforementioned
+ /// [DataReference], and so the resulting reference is actually to
+ /// "data.foo.bar.baz".
+ ///
+ /// In this example, the [DataReference] to "data.foo.bar" would have its
+ /// [constructReference] method invoked by the runtime, with `["baz"]` as the
+ /// `moreParts` argument, so that the resulting [DataReference]'s [parts] is a
+ /// combination of the original's (`["foo", "bar"]`) and the additional parts
+ /// provided to the method.
+ DataReference constructReference(List<Object> moreParts) {
+ return DataReference(parts + moreParts);
+ }
+
+ @override
+ String toString() => 'data.${parts.join(".")}';
+}
+
+/// Unbound reference to a [Loop].
+class LoopReference extends Reference {
+ /// Wraps the given [loop] and [parts] as a [LoopReference].
+ ///
+ /// The [parts] must not be mutated after the object is created.
+ const LoopReference(this.loop, List<Object> parts): super(parts);
+
+ /// The index to the referenced loop.
+ ///
+ /// Loop indices count up, so the nearest loop ancestor of the reference has
+ /// index zero, with indices counting up when going up the tree towards the
+ /// root.
+ final int loop; // this is basically a De Bruijn index
+
+ /// Creates a new [LoopRefererence] that indexes even deeper than this one.
+ ///
+ /// For example, suppose a widget's arguments consisted of a map with one key,
+ /// "a", whose value was a [LoopRefererence] referencing "loop0.foo.bar". Now
+ /// suppose that the widget itself has an [ArgsReference] that references
+ /// "args.a.baz". The "args.a" part identifies the aforementioned
+ /// [LoopReference], and so the resulting reference is actually to
+ /// "data.foo.bar.baz".
+ ///
+ /// In this example, the [LoopReference] to "loop0.foo.bar" would have its
+ /// [constructReference] method invoked by the runtime, with `["baz"]` as the
+ /// `moreParts` argument, so that the resulting [LoopReference]'s [parts] is a
+ /// combination of the original's (`["foo", "bar"]`) and the additional parts
+ /// provided to the method.
+ ///
+ /// The [loop] index is maintained in the new object.
+ LoopReference constructReference(List<Object> moreParts) {
+ return LoopReference(loop, parts + moreParts);
+ }
+
+ /// Binds the loop reference to a specific value.
+ ///
+ /// Returns a [BoundLoopReference] with the same [parts] and whose
+ /// [BoundLoopReference.value] is given by `value`. The [loop] index is
+ /// dropped in the process.
+ BoundLoopReference bind(Object value) {
+ return BoundLoopReference(value, parts);
+ }
+
+ @override
+ String toString() => 'loop$loop.${parts.join(".")}';
+}
+
+/// Bound reference to a [Loop].
+///
+/// This class is used to represent references of the form "loopvar.foo.bar"
+/// after the list containing the relevant loop has been dereferenced so that
+/// the loop variable refers to a specific value in the list. The [value] is
+/// that resolved value.
+///
+/// This class is an internal detail of the RFW [Runtime] and is generally not
+/// used directly.
+class BoundLoopReference extends Reference {
+ /// Wraps the given [value] and [parts] as a [BoundLoopReference].
+ ///
+ /// The [parts] must not be mutated after the object is created.
+ ///
+ /// Generally this class is created using [LoopReference.bind].
+ const BoundLoopReference(this.value, List<Object> parts): super(parts);
+
+ /// The object into which [parts] will index.
+ ///
+ /// This could contain further [Loop]s or unbound [LoopReference]s, which is
+ /// why it cannot be indexed immediately upon creation.
+ final Object value;
+
+ /// Creates a new [BoundLoopRefererence] that indexes even deeper than this
+ /// one.
+ ///
+ /// For example, suppose a widget's arguments consisted of a map with one key,
+ /// "a", whose value was a [BoundLoopRefererence] referencing "loop0.foo.bar".
+ /// Now suppose that the widget itself has an [ArgsReference] that references
+ /// "args.a.baz". The "args.a" part identifies the aforementioned
+ /// [BoundLoopReference], and so the resulting reference is actually to
+ /// "data.foo.bar.baz".
+ ///
+ /// In this example, the [BoundLoopReference] to "loop0.foo.bar" would have
+ /// its [constructReference] method invoked by the runtime, with `["baz"]` as
+ /// the `moreParts` argument, so that the resulting [BoundLoopReference]'s
+ /// [parts] is a combination of the original's (`["foo", "bar"]`) and the
+ /// additional parts provided to the method.
+ ///
+ /// The resolved [value] (which is what the [parts] will eventually index
+ /// into) is maintained in the new object.
+ BoundLoopReference constructReference(List<Object> moreParts) {
+ return BoundLoopReference(value, parts + moreParts);
+ }
+
+ @override
+ String toString() => 'loop($value).${parts.join(".")}';
+}
+
+/// Base class for [StateReference] and [BoundStateReference].
+///
+/// This is used to ensure [SetStateHandler]'s [SetStateHandler.stateReference]
+/// property can only hold a state reference.
+abstract class AnyStateReference extends Reference {
+ /// Abstract const constructor. This constructor enables subclasses to provide
+ /// const constructors so that they can be used in const expressions.
+ ///
+ /// The [parts] must not be mutated after the object is created.
+ const AnyStateReference(List<Object> parts): super(parts);
+}
+
+/// Unbound reference to remote widget's state.
+///
+/// This class is used to represent references of the form "state.foo.bar".
+class StateReference extends AnyStateReference {
+ /// Wraps the given [parts] as a [StateReference].
+ ///
+ /// The [parts] must not be mutated after the object is created.
+ const StateReference(List<Object> parts): super(parts);
+
+ /// Binds the state reference to a specific widget (identified by depth).
+ ///
+ /// Returns a [BoundStateReference] with the same [parts] and whose
+ /// [BoundLoopReference.depth] is given by `depth`.
+ BoundStateReference bind(int depth) {
+ return BoundStateReference(depth, parts);
+ }
+
+ @override
+ String toString() => 'state.${parts.join(".")}';
+}
+
+/// Bound reference to a remote widget's state.
+///
+/// This class is used to represent references of the form "state.foo.bar" after
+/// the widgets have been constructed, so that the right state can be
+/// identified.
+///
+/// This class is an internal detail of the RFW [Runtime] and is generally not
+/// used directly.
+class BoundStateReference extends AnyStateReference {
+ /// Wraps the given [depth] and [parts] as a [BoundStateReference].
+ ///
+ /// The [parts] must not be mutated after the object is created.
+ ///
+ /// Generally this class is created using [StateReference.bind].
+ const BoundStateReference(this.depth, List<Object> parts): super(parts);
+
+ /// The widget to whose state the state reference refers.
+ ///
+ /// This identifies the widget by depth starting at the widget that was
+ /// created by [Runtime.build] (or a [RemoteWidget], which uses that method).
+ ///
+ /// Since state references always go up the tree, this is an unambiguous way
+ /// to reference state, even though in practice in the entire tree multiple
+ /// widgets may be stateful at the same depth.
+ final int depth;
+
+ /// Creates a new [BoundStateRefererence] that indexes even deeper than this
+ /// one (deeper into the specified widget's state, not into a deeper widget!).
+ ///
+ /// For example, suppose a widget's arguments consisted of a map with one key,
+ /// "a", whose value was a [BoundStateRefererence] referencing "state.foo.bar".
+ /// Now suppose that the widget itself has an [ArgsReference] that references
+ /// "args.a.baz". The "args.a" part identifies the aforementioned
+ /// [BoundStateReference], and so the resulting reference is actually to
+ /// "state.foo.bar.baz".
+ ///
+ /// In this example, the [BoundStateReference] to "state.foo.bar" would have
+ /// its [constructReference] method invoked by the runtime, with `["baz"]` as
+ /// the `moreParts` argument, so that the resulting [BoundStateReference]'s
+ /// [parts] is a combination of the original's (`["foo", "bar"]`) and the
+ /// additional parts provided to the method.
+ ///
+ /// The [depth] is maintained in the new object.
+ BoundStateReference constructReference(List<Object> moreParts) {
+ return BoundStateReference(depth, parts + moreParts);
+ }
+
+ @override
+ String toString() => 'state^$depth.${parts.join(".")}';
+}
+
+/// Base class for [EventHandler] and [SetStateHandler].
+///
+/// This is used by the [Runtime] to quickly filter out objects that are not
+/// event handlers of any kind.
+abstract class AnyEventHandler extends BlobNode {
+ /// Abstract const constructor. This constructor enables subclasses to provide
+ /// const constructors so that they can be used in const expressions.
+ const AnyEventHandler();
+}
+
+/// Description of a callback in an RFW widget declaration.
+///
+/// This represents a signal to send to the application using the RFW package.
+/// Typically applications either handle such messages locally, or forward them
+/// to a server for further processing.
+class EventHandler extends AnyEventHandler {
+ /// Wraps the given event name and arguments in an [EventHandler] object.
+ ///
+ /// The [eventArguments] must not be mutated after the object is created.
+ const EventHandler(this.eventName, this.eventArguments);
+
+ /// A string to identify the event. This provides an unambiguous identifier
+ /// for the event, avoiding the need to establish a convention in the
+ /// [eventArguments].
+ final String eventName;
+
+ /// The payload to provide with the event.
+ final DynamicMap eventArguments;
+
+ @override
+ String toString() => 'event $eventName $eventArguments';
+}
+
+/// Description of a state setter in an RFW widget declaration.
+///
+/// This event handler is handled by the RFW [Runtime] itself by setting the
+/// state referenced by [stateReference] to the value represented by [value]
+/// when the event handler would be invoked.
+class SetStateHandler extends AnyEventHandler {
+ /// Wraps the given [stateReference] and [value] in a [SetStateHandler] object.
+ ///
+ /// The [value] must not be mutated after the object is created (e.g. in the
+ /// event that it is a [DynamicMap] or [DynamicList]).
+ const SetStateHandler(this.stateReference, this.value);
+
+ /// Identifies the member in the widget's state to mutate.
+ final AnyStateReference stateReference;
+
+ /// The value to which the specified state will be set.
+ final Object value;
+
+ @override
+ String toString() => 'set $stateReference = $value';
+}
+
+/// A library import.
+///
+/// Used to describe which libraries a remote widget libraries depends on. The
+/// identified libraries can be local or remote. Import loops are invalid.
+class Import extends BlobNode {
+ /// Wraps the given library [name] in an [Import] object.
+ const Import(this.name);
+
+ /// The name of the library to import.
+ final LibraryName name;
+
+ @override
+ String toString() => 'import $name;';
+}
+
+/// A description of a widget in a remote widget library.
+///
+/// The [root] must be either a [ConstructorCall] or a [Switch] that evaluates
+/// to a [ConstructorCall]. (In principle one can imagine that an
+/// [ArgsReference] that evaluates to a [ConstructorCall] would also be valid,
+/// but such a construct would be redundant and would not provide any additional
+/// expressivity, so it is disallowed.)
+///
+/// The tree rooted at [root] must not contain (directly or indirectly) a
+/// [ConstructorCall] that references the widget declared by this
+/// [WidgetDeclaration]: widget loops, even indirect loops or loops that would
+/// in principle be terminated by use of a [Switch], are not allowed.
+class WidgetDeclaration extends BlobNode {
+ /// Binds the given [name] to the definition given by [root].
+ ///
+ /// The [initialState] may be null. If it is not, this represents a stateful widget.
+ const WidgetDeclaration(this.name, this.initialState, this.root) : assert(root is ConstructorCall || root is Switch);
+
+ /// The name of the widget that this declaration represents.
+ ///
+ /// This is the left hand side of a widget declaration.
+ final String name;
+
+ /// If non-null, this is a stateful widget; the value is used to create the
+ /// initial copy of the state when the widget is created.
+ final DynamicMap? initialState;
+
+ /// The widget to return when this widget is used.
+ ///
+ /// This is usually a [ConstructorCall], but may be a [Switch] (so long as
+ /// that [Switch] resolves to a [ConstructorCall]. Other values (or a [Switch]
+ /// that does not resolve to a constructor call) will result in an
+ /// [ErrorWidget] being used.
+ final BlobNode root; // ConstructorCall or Switch
+
+ @override
+ String toString() => 'widget $name = $root;';
+}
+
+/// Base class for widget libraries.
+abstract class WidgetLibrary {
+ /// Abstract const constructor. This constructor enables subclasses to provide
+ /// const constructors so that they can be used in const expressions.
+ const WidgetLibrary();
+}
+
+/// The in-memory representation of the output of [parseTextLibraryFile] or
+/// [decodeLibraryBlob].
+class RemoteWidgetLibrary extends WidgetLibrary {
+ /// Wraps a set of [imports] and [widgets] (widget declarations) in a
+ /// [RemoteWidgetLibrary] object.
+ ///
+ /// The provided lists must not be mutated once the library is created.
+ const RemoteWidgetLibrary(this.imports, this.widgets);
+
+ /// The list of libraries that this library depends on.
+ ///
+ /// This must not be empty, since at least one local widget library must be in
+ /// scope in order for the remote widget library to be useful.
+ final List<Import> imports;
+
+ /// The list of widgets declared by this library.
+ ///
+ /// This can be empty.
+ final List<WidgetDeclaration> widgets;
+
+ @override
+ String toString() => const Iterable<Object>.empty().followedBy(imports).followedBy(widgets).join('\n');
+}
diff --git a/packages/rfw/lib/dart/text.dart b/packages/rfw/lib/dart/text.dart
new file mode 100644
index 0000000..da596b7
--- /dev/null
+++ b/packages/rfw/lib/dart/text.dart
@@ -0,0 +1,2454 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is hand-formatted.
+
+// This file must not import `dart:ui`, directly or indirectly, as it is
+// intended to function even in pure Dart server or CLI environments.
+
+import 'model.dart';
+
+/// Parse a Remote Flutter Widgets text data file.
+///
+/// This data is usually used in conjunction with [DynamicContent].
+///
+/// Parsing this format is about ten times slower than parsing the binary
+/// variant; see [decodeDataBlob]. As such it is strongly discouraged,
+/// especially in resource-constrained contexts like mobile applications.
+///
+/// ## Format
+///
+/// This format is inspired by JSON, but with the following changes:
+///
+/// * end-of-line comments are supported, using the "//" syntax from C (and
+/// Dart).
+///
+/// * map keys may be unquoted when they are alphanumeric.
+///
+/// * "null" is not a valid value except as a value in maps, where it is
+/// equivalent to omitting the corresponding key entirely. This is allowed
+/// primarily as a development aid to explicitly indicate missing keys. (The
+/// data model itself, and the binary format (see [decodeDataBlob]), do not
+/// contain this feature.)
+///
+/// * integers and doubles are distinguished explicitly, via the presence of
+/// the decimal point and/or exponent characters. Integers may also be
+/// expressed as hex literals. Numbers are explicitly 64 bit precision;
+/// specifically, signed 64 bit integers, or binary64 floating point numbers.
+///
+/// * files are always rooted at a map. (Different versions of JSON are
+/// ambiguous or contradictory about this.)
+///
+/// Here is the BNF:
+///
+/// ```bnf
+/// root ::= WS* map WS*
+/// ```
+///
+/// Every Remote Flutter Widget text data file must match the `root` production.
+///
+/// ```bnf
+/// map ::= "{" ( WS* entry WS* "," )* WS* entry? WS* "}"
+/// entry ::= ( identifier | string ) WS* ":" WS* ( value | "null" )
+/// ```
+///
+/// Maps are comma-separated lists of name-value pairs (a single trailing comma
+/// is permitted), surrounded by braces. The key must be either a quoted string
+/// or an unquoted identifier. The value must be either the keyword "null",
+/// which is equivalent to the entry being omitted, or a value as defined below.
+/// Duplicates (notwithstanding entries that use the keyword "null") are not
+/// permitted. In general the use of the "null" keyword is discouraged and is
+/// supported only to allow mechanically-generated data to consistently include
+/// every key even when one has no value.
+///
+/// ```bnf
+/// value ::= map | list | integer | double | string | "true" | "false"
+/// ```
+///
+/// The "true" and "false" keywords represented the boolean true and false
+/// values respectively.
+///
+/// ```bnf
+/// list ::= "[" ( WS* value WS* "," )* WS* value? WS* "]"
+/// ```
+///
+/// Following the pattern set by maps, lists are comma-separated lists of values
+/// (a single trailing comma is permitted), surrounded by brackets.
+///
+/// ```bnf
+/// identifier ::= letter ( letter | digit )*
+/// letter ::= <a character in the ranges A-Z, a-z, or the underscore>
+/// digit ::= <a character in the range 0-9>
+/// ```
+///
+/// Identifiers are alphanumeric sequences (with the underscore considered a
+/// letter) that do not start with a digit.
+///
+/// ```bnf
+/// string ::= DQ stringbodyDQ* DQ | SQ stringbodySQ* SQ
+/// DQ ::= <U+0022>
+/// stringbodyDQ ::= escape | characterDQ
+/// characterDQ ::= <U+0000..U+10FFFF except U+000A, U+0022, U+005C>
+/// SQ ::= <U+0027>
+/// stringbodySQ ::= escape | characterSQ
+/// characterSQ ::= <U+0000..U+10FFFF except U+000A, U+0027, U+005C>
+/// escape ::= <U+005C> ("b" | "f" | "n" | "r" | "t" | symbol | unicode)
+/// symbol ::= <U+0022, U+0027, U+005C, or U+002F>
+/// unicode ::= "u" hex hex hex hex
+/// hex ::= <a character in the ranges A-F, a-f, or 0-9>
+/// ```
+///
+/// Strings are double-quoted (U+0022, ") or single-quoted (U+0027, ') sequences
+/// of zero or more Unicode characters that do not include a newline, the quote
+/// character used, or the backslash character, mixed with zero or more escapes.
+///
+/// Escapes are a backslash character followed another character, as follows:
+///
+/// * `\b`: represents U+0008
+/// * `\f`: represents U+000C
+/// * `\n`: represents U+000A
+/// * `\r`: represents U+000D
+/// * `\t`: represents U+0009
+/// * `\uXXXX`: represents the UTF-16 codepoint with code XXXX.
+///
+/// Characters outside the basic multilingual plane must be represented either
+/// as literal characters, or as two escapes identifying UTF-16 surrogate pairs.
+/// (This is for compatibility with JSON.)
+///
+/// ```bnf
+/// integer ::= "-"? decimal | hexadecimal
+/// decimal ::= digit+
+/// hexadecimal ::= ("0x" | "0X") hex+
+/// ```
+///
+/// The "digit" (0-9) and "hex" (0-9, A-F, a-f) terminals are described earlier.
+///
+/// In the "decimal" form, the digits represent their value in base ten. A
+/// leading hyphen indicates the number is negative. In the "hexadecimal" form,
+/// the number is always positive, and is represented by the hex digits
+/// interpreted in base sixteen.
+///
+/// The numbers represented must be in the range -9,223,372,036,854,775,808 to
+/// 9,223,372,036,854,775,807.
+///
+/// ```
+/// double ::= "-"? digit+ ("." digit+)? (("e" | "E") "-"? digit+)?
+/// ```
+///
+/// Floating point numbers are represented by an optional negative sign
+/// indicating the number is negative, a significand with optional fractional
+/// component in the form of digits in base ten giving the integer component
+/// followed optionally by a decimal point and further base ten digits giving
+/// the fractional component, and an exponent which itself is represented by an
+/// optional negative sign indicating a negative exponent and a sequence of
+/// digits giving the base ten exponent itself.
+///
+/// The numbers represented must be values that can be expressed in the IEEE754
+/// binary64 format.
+///
+/// ```bnf
+/// WS ::= ( <U+0020> | <U+000A> | "//" comment* <U+000A or EOF> )
+/// comment ::= <U+0000..U+10FFFF except U+000A>
+/// ```
+///
+/// The `WS` token is used to represent where whitespace and comments are
+/// allowed.
+///
+/// See also:
+///
+/// * [parseLibraryFile], which uses a superset of this format to decode
+/// Remote Flutter Widgets text library files.
+/// * [decodeDataBlob], which decodes the binary variant of this format.
+DynamicMap parseDataFile(String file) {
+ final _Parser parser = _Parser(_tokenize(file));
+ return parser.readDataFile();
+}
+
+/// Parses a Remote Flutter Widgets text library file.
+///
+/// Remote widget libraries are usually used in conjunction with a [Runtime].
+///
+/// Parsing this format is about ten times slower than parsing the binary
+/// variant; see [decodeLibraryBlob]. As such it is strongly discouraged,
+/// especially in resource-constrained contexts like mobile applications.
+///
+/// ## Format
+///
+/// The format is a superset of the format defined by [parseDataFile].
+///
+/// Remote Flutter Widgets text library files consist of a list of imports
+/// followed by a list of widget declarations.
+///
+/// ### Imports
+///
+/// A remote widget library file is identified by a name which consists of
+/// several parts, which are by convention expressed separated by periods; for
+/// example, `core.widgets` or `net.example.x`.
+///
+/// A library's name is specified when the library is provided to the runtime
+/// using [Runtime.update].
+///
+/// A remote widget library depends on one or more other libraries that define
+/// the widgets that the primary library depends on. These dependencies can
+/// themselves be remote widget libraries, for example describing commonly-used
+/// widgets like branded buttons, or "local widget libraries" which are declared
+/// and hard-coded in the client itself and that provide a way to reference
+/// actual Flutter widgets (see [LocalWidgetLibrary]).
+///
+/// The Remote Flutter Widgets package ships with two local widget libraries,
+/// usually given the names `core.widgets` (see [createCoreWidgets]) and
+/// `core.material` (see [createMaterialWidgets]). An application can declare
+/// other local widget libraries for use by their remote widgets. These could
+/// correspond to UI controls, e.g. branded widgets used by other parts of the
+/// application, or to complete experiences, e.g. core parts of the application.
+/// For example, a blogging application might use Remote Flutter Widgets to
+/// represent the CRM parts of the experience, with the rich text editor being
+/// implemented on the client as a custom widget exposed to the remote libraries
+/// as a widget in a local widget library.
+///
+/// A library lists the other libraries that it depends on by name. When a
+/// widget is referenced, it is looked up by name first by examining the widget
+/// declarations in the file itself, then by examining the declarations of each
+/// dependency in turn, in a depth-first search.
+///
+/// It is an error for there to be a loop in the imports.
+///
+/// Imports have this form:
+///
+/// ```
+/// import library.name;
+/// ```
+///
+/// For example:
+///
+/// ```
+/// import core.widgets;
+/// ```
+///
+/// ### Widget declarations
+///
+/// The primary purpose of a remote widget library is to provide widget
+/// declarations. Each declaration defines a new widget. Widgets are defined in
+/// terms of other widgets, like stateless and stateful widgets in Flutter
+/// itself. As such, a widget declaration consists of a widget constructor call.
+///
+/// The widget declarations come after the imports.
+///
+/// To declare a widget named A in terms of a widget B, the following form is used:
+///
+/// ```
+/// widget A = B();
+/// ```
+///
+/// This declares a widget A, whose implementation is simply to use widget B.
+///
+/// If the widget A is to be stateful, a map is inserted before the equals sign:
+///
+/// ```
+/// widget A { } = B();
+/// ```
+///
+/// The map describes the default values of the state. For example, a button
+/// might have a "down" state, which is initially false:
+///
+/// ```
+/// widget Button { down: false } = Container();
+/// ```
+///
+/// _See the section on State below._
+///
+/// ### Widget constructor calls
+///
+/// A widget constructor call is an invocation of a remote or local widget
+/// declaration, along with its arguments. Arguments are a map of key-value
+/// pairs, where the values can be any of the types in the data model defined
+/// above plus any of the types defined below in this section, such as
+/// references to arguments, the data model, loops, state, switches, or
+/// event handlers.
+///
+/// In this example, several constructor calls are nested together:
+///
+/// ```
+/// widget Foo = Column(
+/// children: [
+/// Container(
+/// child: Text(text: "Hello"),
+/// ),
+/// ],
+/// );
+/// ```
+///
+/// The `Foo` widget is defined to create a `Column` widget. The `Column(...)`
+/// is a constructor call with one argument, named `children`, whose value is a
+/// list which itself contains a single constructor call, to `Container`. That
+/// constructor call also has only one argument, `child`, whose value, again, is
+/// a constructor call, in this case creating a `Text` widget.
+///
+/// ### References
+///
+/// Remote widget libraries typically contain _references_, e.g. to the
+/// arguments of a widget, or to the [DynamicContent] data, or to a stateful
+/// widget's state.
+///
+/// The various kinds of references all have the same basic pattern, a prefix
+/// followed by period-separated identifiers, strings, or integers. Identifiers
+/// and strings are used to index into maps, while integers are used to index
+/// into lists.
+///
+/// For example, "foo.2.fruit" would reference the key with the value "Kiwi" in
+/// the following structure:
+///
+/// ```c
+/// {
+/// foo: [
+/// { fruit: "Apple" },
+/// { fruit: "Banana" },
+/// { fruit: "Kiwi" }, // foo.2.fruit is the string "Kiwi" here
+/// ],
+/// bar: [ ],
+/// }
+/// ```
+///
+/// Peferences to a widget's arguments use "args" as the first component:
+///
+/// <code>args<i>.foo.bar</i></code>
+///
+/// References to the data model use use "data" as the first component, and
+/// references to state use "state" as the first component:
+///
+/// <code>data<i>.foo.bar</i></code>
+///
+/// <code>state<i>.foo.bar</i></code>
+///
+/// Finally, references to loop variables use the identifier specified in the
+/// loop as the first component:
+///
+/// <code><i>ident.foo.bar</i></code>
+///
+/// #### Argument references
+///
+/// Instead of passing literal values as arguments in widget constructor calls,
+/// a reference to one of the arguments of the remote widget being defined
+/// itself can be provided instead.
+///
+/// For example, suppose one instantiated a widget Foo as follows:
+///
+/// ```
+/// Foo(name: "Bobbins")
+/// ```
+///
+/// ...then in the definition of Foo, one might pass the value of this "name"
+/// argument to another widget, say a Text widget, as follows:
+///
+/// ```
+/// widget Foo = Text(text: args.name);
+/// ```
+///
+/// The arguments can have structure. For example, if the argument passed to Foo
+/// was:
+///
+/// ```
+/// Foo(show: { name: "Cracking the Cryptic", phrase: "Bobbins" })
+/// ```
+///
+/// ...then to specify the leaf node whose value is the string "Bobbins", one
+/// would specify an argument reference consisting of the values "show" and
+/// "phrase", as in `args.show.phrase`. For example:
+///
+/// ```
+/// widget Foo = Text(text: args.show.phrase);
+/// ```
+///
+/// #### Data model references
+///
+/// Instead of passing literal values as arguments in widget constructor calls,
+/// or references to one's own arguments, a reference to one of the nodes in the
+/// data model can be provided instead.
+///
+/// The data model is a tree of maps and lists with leaves formed of integers,
+/// doubles, bools, and strings (see [DynamicContent]). For example, if the data
+/// model looks like this:
+///
+/// ```
+/// { server: { cart: [ { name: "Apple"}, { name: "Banana"} ] }
+/// ```
+///
+/// ...then to specify the leaf node whose value is the string "Banana", one
+/// would specify a data model reference consisting of the values "server",
+/// "cart", 1, and "name", as in `data.server.cart.1.name`. For example:
+///
+/// ```
+/// Text(text: data.server.cart.1.name)
+/// ```
+///
+/// ### Loops
+///
+/// In a list, a loop can be employed to map another list into the host list,
+/// mapping values of the embedded list according to a provided template. Within
+/// the template, references to the value from the embedded list being expanded
+/// can be provided using a loop reference, which is similar to argument and
+/// data references.
+///
+/// A widget that shows all the values from a list in a [ListView] might look
+/// like this:
+///
+/// ```
+/// widget Items = ListView(
+/// children: [
+/// ...for item in args.list:
+/// Text(text: item),
+/// ],
+/// );
+/// ```
+///
+/// Such a widget would be used like this:
+///
+/// ```
+/// Items(list: [ "Hello", "World" ])
+/// ```
+///
+/// The syntax for a loop uses the following form:
+///
+/// <code>...for <i>ident</i> in <i>list</i>: <i>template</i></code>
+/// ```
+///
+/// ...where _ident_ is the identifier to bind to each value in the list, _list_
+/// is some value that evaluates to a list, and _template_ is a value that is to
+/// be evaluated for each item in _list_.
+///
+/// This loop syntax is only valid inside lists.
+///
+/// Loop references use the _ident_. In the example above, that is `item`. In
+/// more elaborate examples, it can include subreferences. For example:
+///
+/// ```
+/// widget Items = ListView(
+/// children: [
+/// Text(text: 'Products:'),
+/// ...for item in args.products:
+/// Text(text: product.name.displayName),
+/// Text(text: 'End of list.'),
+/// ],
+/// );
+/// ```
+///
+/// This might be used as follows:
+///
+/// ```
+/// Items(products: [
+/// { name: { abbreviation: "TI4", displayName: "Twilight Imperium IV" }, price: 120.0 },
+/// { name: { abbreviation: "POK", displayName: "Prophecy of Kings" }, price: 100.0 },
+/// ])
+/// ```
+///
+/// ### State
+///
+/// A widget declaration can say that it has an "initial state", the structure
+/// of which is the same as the data model structure (maps and lists of
+/// primitive types, the root is a map).
+///
+/// Here a button is described as having a "down" state whose first value is
+/// "false":
+///
+/// ```
+/// widget Button { down: false } = Container(
+/// // ...
+/// );
+/// ```
+///
+/// If a widget has state, then it can be referenced in the same way as the
+/// widget's arguments and the data model can be referenced, and it can be
+/// changed using event handlers as described below.
+///
+/// Here, the button's state is referenced (in a pretty nonsensical way;
+/// controlling whether its label wraps based on the value of the state):
+///
+/// ```
+/// widget Button { down: false } = Container(
+/// child: Text(text: 'Hello World', softWrap: state.down),
+/// );
+/// ```
+///
+/// State is usually used with Switches and state-setting handlers.
+///
+/// ### Switches
+///
+/// Anywhere in a widget declaration, a switch can be employed to change the
+/// evaluated value used at runtime. A switch has a value that is being used to
+/// control the switch, and then a series of cases with values to use if the
+/// control value matches the case value. A default can be provided.
+///
+/// The control value is usually a reference to arguments, data, state, or a
+/// loop variable.
+///
+/// The syntax for a switch uses the following form:
+///
+/// ```
+/// switch value {
+/// case1: template1,
+/// case2: template2,
+/// case3: template3,
+/// // ...
+/// default: templateD,
+/// }
+/// ```
+///
+/// ...where _value_ is the control value that will be compared to each case,
+/// the _caseX_ values are the values to which the control value is compared,
+/// _templateX_ are the templates to use, and _templateD_ is the default
+/// template to use if none of the cases can be met. Any number of cases can be
+/// specified; the template of the first one that exactly matches the given
+/// control value is the one that is used. The default entry is optional. If no
+/// value matches and there is no default, the switch evaluates to the "missing"
+/// value (null).
+///
+/// Extending the earlier button, this would move the margin around so that it
+/// appeared pressed when the "down" state was true (but note that we still
+/// don't have anything to toggle that state!):
+///
+/// ```
+/// widget Button { down: false } = Container(
+/// margin: switch state.down {
+/// false: [ 0.0, 0.0, 8.0, 8.0 ],
+/// true: [ 8.0, 8.0, 0.0, 0.0 ],
+/// },
+/// decoration: { type: "box", border: [ {} ] },
+/// child: args.child,
+/// );
+/// ```
+///
+/// ### Event handlers
+///
+/// There are two kinds of event handlers: those that signal an event for the
+/// host to handle (potentially by forwarding it to a server), and those that
+/// change the widget's state.
+///
+/// Signalling event handlers have a name and an arguments map:
+///
+/// ```
+/// event "..." { }
+/// ```
+///
+/// Tthe string is the name of the event, and the arguments map is the data to
+/// send with the event.
+///
+/// For example, the event handler in the following sequence sends the event
+/// called "hello" with a map containing just one key, "id", whose value is 1:
+///
+/// ```
+/// Button(
+/// onPressed: event "hello" { id: 1 },
+/// child: Text(text: "Greetings"),
+/// );
+/// ```
+///
+/// Event handlers that set state have a reference to a state, and a new value
+/// to assign to that state. Such handlers are only meaningful within widgets
+/// that have state, as described above. They have this form:
+///
+/// ```
+/// set state.foo.bar = value
+/// ```
+///
+/// The `state.foo.bar` part is a state reference (which must identify a part of
+/// the state that exists), and `value` is the new value to assign to that state.
+///
+/// This lets us finish the earlier button:
+///
+/// ```
+/// widget Button { down: false } = GestureDetector(
+/// onTapDown: set state.down = true,
+/// onTapUp: set state.down = false,
+/// onTapCancel: set state.down = false,
+/// onTap: args.onPressed,
+/// child: Container(
+/// margin: switch state.down {
+/// false: [ 0.0, 0.0, 8.0, 8.0 ],
+/// true: [ 8.0, 8.0, 0.0, 0.0 ],
+/// },
+/// decoration: { type: "box", border: [ {} ] },
+/// child: args.child,
+/// ),
+/// );
+/// ```
+///
+/// See also:
+///
+/// * [encodeLibraryBlob], which encodes the output of this method
+/// into the binary variant of this format.
+/// * [parseDataFile], which uses a subset of this format to decode
+/// Remote Flutter Widgets text data files.
+/// * [decodeLibraryBlob], which decodes the binary variant of this format.
+RemoteWidgetLibrary parseLibraryFile(String file) {
+ final _Parser parser = _Parser(_tokenize(file));
+ return parser.readLibraryFile();
+}
+
+const Set<String> _reservedWords = <String>{
+ 'args',
+ 'data',
+ 'event',
+ 'false',
+ 'set',
+ 'state',
+ 'true',
+};
+
+abstract class _Token {
+ _Token(this.line, this.column);
+ final int line;
+ final int column;
+}
+
+class _SymbolToken extends _Token {
+ _SymbolToken(this.symbol, int line, int column): super(line, column);
+ final int symbol;
+
+ static const int dot = 0x2E;
+ static const int tripleDot = 0x2026;
+ static const int openParen = 0x28; // U+0028 LEFT PARENTHESIS character (()
+ static const int closeParen = 0x29; // U+0029 RIGHT PARENTHESIS character ())
+ static const int comma = 0x2C; // U+002C COMMA character (,)
+ static const int colon = 0x3A; // U+003A COLON character (:)
+ static const int semicolon = 0x3B; // U+003B SEMICOLON character (;)
+ static const int equals = 0x3D; // U+003D EQUALS SIGN character (=)
+ static const int openBracket = 0x5B; // U+005B LEFT SQUARE BRACKET character ([)
+ static const int closeBracket = 0x5D; // U+005D RIGHT SQUARE BRACKET character (])
+ static const int openBrace = 0x7B; // U+007B LEFT CURLY BRACKET character ({)
+ static const int closeBrace = 0x7D; // U+007D RIGHT CURLY BRACKET character (})
+
+ @override
+ String toString() => String.fromCharCode(symbol);
+}
+
+class _IntegerToken extends _Token {
+ _IntegerToken(this.value, int line, int column): super(line, column);
+ final int value;
+
+ @override
+ String toString() => '$value';
+}
+
+class _DoubleToken extends _Token {
+ _DoubleToken(this.value, int line, int column): super(line, column);
+ final double value;
+
+ @override
+ String toString() => '$value';
+}
+
+class _IdentifierToken extends _Token {
+ _IdentifierToken(this.value, int line, int column): super(line, column);
+ final String value;
+
+ @override
+ String toString() => value;
+}
+
+class _StringToken extends _Token {
+ _StringToken(this.value, int line, int column): super(line, column);
+ final String value;
+
+ @override
+ String toString() => '"$value"';
+}
+
+class _EofToken extends _Token {
+ _EofToken(int line, int column): super(line, column);
+
+ @override
+ String toString() => '<EOF>';
+}
+
+/// Indicates that there an error was detected while parsing a file.
+///
+/// This is used by [parseDataFile] and [parseLibraryFile] to indicate that the
+/// given file has a syntax or semantic error.
+///
+/// The [line] and [column] describe how far the parser had reached when the
+/// error was detected (this may not precisely indicate the actual location of
+/// the error).
+///
+/// The [message] property is a human-readable string describing the error.
+class ParserException implements Exception {
+ /// Create an instance of [ParserException].
+ ///
+ /// The arguments must not be null. See [message] for details on
+ /// the expected syntax of the error description.
+ const ParserException(this.message, this.line, this.column);
+
+ factory ParserException._fromToken(String message, _Token token) {
+ return ParserException(message, token.line, token.column);
+ }
+
+ factory ParserException._expected(String what, _Token token) {
+ return ParserException('Expected $what but found $token', token.line, token.column);
+ }
+
+ factory ParserException._unexpected(_Token token) {
+ return ParserException('Unexpected $token', token.line, token.column);
+ }
+
+ /// The error that was detected by the parser.
+ ///
+ /// This should be the start of a sentence which will make sense when " at
+ /// line ... column ..." is appended. So, for example, it should not end with
+ /// a period.
+ final String message;
+
+ /// The line number (using 1-based indexing) that the parser had reached when
+ /// the error was detected.
+ final int line;
+
+ /// The column number (using 1-based indexing) that the parser had reached
+ /// when the error was detected.
+ ///
+ /// This is measured in UTF-16 code units, even if the source file was UTF-8.
+ final int column;
+
+ @override
+ String toString() => '$message at line $line column $column.';
+}
+
+enum _TokenizerMode {
+ main,
+ minus,
+ zero,
+ minusInteger,
+ integer,
+ integerOnly,
+ numericDot,
+ fraction,
+ e,
+ negativeExponent,
+ exponent,
+ x,
+ hex,
+ dot1,
+ dot2,
+ identifier,
+ quote,
+ doubleQuote,
+ quoteEscape,
+ quoteEscapeUnicode1,
+ quoteEscapeUnicode2,
+ quoteEscapeUnicode3,
+ quoteEscapeUnicode4,
+ endQuote,
+ doubleQuoteEscape,
+ doubleQuoteEscapeUnicode1,
+ doubleQuoteEscapeUnicode2,
+ doubleQuoteEscapeUnicode3,
+ doubleQuoteEscapeUnicode4,
+ endDoubleQuote,
+ slash,
+ comment,
+}
+
+String _describeRune(int current) {
+ assert(current >= 0);
+ assert(current < 0x10FFFF);
+ if (current > 0x20) {
+ return 'U+${current.toRadixString(16).toUpperCase().padLeft(4, "0")} ("${String.fromCharCode(current)}")';
+ }
+ return 'U+${current.toRadixString(16).toUpperCase().padLeft(4, "0")}';
+}
+
+Iterable<_Token> _tokenize(String file) sync* {
+ final List<int> characters = file.runes.toList();
+ int index = 0;
+ int line = 1;
+ int column = 0;
+ final List<int> buffer = <int>[];
+ final List<int> buffer2 = <int>[];
+ _TokenizerMode mode = _TokenizerMode.main;
+ while (true) {
+ final int current;
+ if (index >= characters.length) {
+ current = -1;
+ } else {
+ current = characters[index];
+ if (current == 0x0A) {
+ line += 1;
+ column = 0;
+ } else {
+ column += 1;
+ }
+ }
+ switch (mode) {
+
+ case _TokenizerMode.main:
+ switch (current) {
+ case -1:
+ yield _EofToken(line, column);
+ return;
+ case 0x0A: // U+000A LINE FEED (LF)
+ case 0x20: // U+0020 SPACE character
+ break;
+ case 0x28: // U+0028 LEFT PARENTHESIS character (()
+ case 0x29: // U+0029 RIGHT PARENTHESIS character ())
+ case 0x2C: // U+002C COMMA character (,)
+ case 0x3A: // U+003A COLON character (:)
+ case 0x3B: // U+003B SEMICOLON character (;)
+ case 0x3D: // U+003D EQUALS SIGN character (=)
+ case 0x5B: // U+005B LEFT SQUARE BRACKET character ([)
+ case 0x5D: // U+005D RIGHT SQUARE BRACKET character (])
+ case 0x7B: // U+007B LEFT CURLY BRACKET character ({)
+ case 0x7D: // U+007D RIGHT CURLY BRACKET character (})
+ yield _SymbolToken(current, line, column);
+ break;
+ case 0x22: // U+0022 QUOTATION MARK character (")
+ assert(buffer.isEmpty);
+ mode = _TokenizerMode.doubleQuote;
+ break;
+ case 0x27: // U+0027 APOSTROPHE character (')
+ assert(buffer.isEmpty);
+ mode = _TokenizerMode.quote;
+ break;
+ case 0x2D: // U+002D HYPHEN-MINUS character (-)
+ assert(buffer.isEmpty);
+ mode = _TokenizerMode.minus;
+ buffer.add(current);
+ break;
+ case 0x2E: // U+002E FULL STOP character (.)
+ mode = _TokenizerMode.dot1;
+ break;
+ case 0x2F: // U+002F SOLIDUS character (/)
+ mode = _TokenizerMode.slash;
+ break;
+ case 0x30: // U+0030 DIGIT ZERO character (0)
+ assert(buffer.isEmpty);
+ mode = _TokenizerMode.zero;
+ buffer.add(current);
+ break;
+ case 0x31: // U+0031 DIGIT ONE character (1)
+ case 0x32: // U+0032 DIGIT TWO character (2)
+ case 0x33: // U+0033 DIGIT THREE character (3)
+ case 0x34: // U+0034 DIGIT FOUR character (4)
+ case 0x35: // U+0035 DIGIT FIVE character (5)
+ case 0x36: // U+0036 DIGIT SIX character (6)
+ case 0x37: // U+0037 DIGIT SEVEN character (7)
+ case 0x38: // U+0038 DIGIT EIGHT character (8)
+ case 0x39: // U+0039 DIGIT NINE character (9)
+ assert(buffer.isEmpty);
+ mode = _TokenizerMode.integer;
+ buffer.add(current);
+ break;
+ case 0x41: // U+0041 LATIN CAPITAL LETTER A character
+ case 0x42: // U+0042 LATIN CAPITAL LETTER B character
+ case 0x43: // U+0043 LATIN CAPITAL LETTER C character
+ case 0x44: // U+0044 LATIN CAPITAL LETTER D character
+ case 0x45: // U+0045 LATIN CAPITAL LETTER E character
+ case 0x46: // U+0046 LATIN CAPITAL LETTER F character
+ case 0x47: // U+0047 LATIN CAPITAL LETTER G character
+ case 0x48: // U+0048 LATIN CAPITAL LETTER H character
+ case 0x49: // U+0049 LATIN CAPITAL LETTER I character
+ case 0x4A: // U+004A LATIN CAPITAL LETTER J character
+ case 0x4B: // U+004B LATIN CAPITAL LETTER K character
+ case 0x4C: // U+004C LATIN CAPITAL LETTER L character
+ case 0x4D: // U+004D LATIN CAPITAL LETTER M character
+ case 0x4E: // U+004E LATIN CAPITAL LETTER N character
+ case 0x4F: // U+004F LATIN CAPITAL LETTER O character
+ case 0x50: // U+0050 LATIN CAPITAL LETTER P character
+ case 0x51: // U+0051 LATIN CAPITAL LETTER Q character
+ case 0x52: // U+0052 LATIN CAPITAL LETTER R character
+ case 0x53: // U+0053 LATIN CAPITAL LETTER S character
+ case 0x54: // U+0054 LATIN CAPITAL LETTER T character
+ case 0x55: // U+0055 LATIN CAPITAL LETTER U character
+ case 0x56: // U+0056 LATIN CAPITAL LETTER V character
+ case 0x57: // U+0057 LATIN CAPITAL LETTER W character
+ case 0x58: // U+0058 LATIN CAPITAL LETTER X character
+ case 0x59: // U+0059 LATIN CAPITAL LETTER Y character
+ case 0x5A: // U+005A LATIN CAPITAL LETTER Z character
+ case 0x61: // U+0061 LATIN SMALL LETTER A character
+ case 0x62: // U+0062 LATIN SMALL LETTER B character
+ case 0x63: // U+0063 LATIN SMALL LETTER C character
+ case 0x64: // U+0064 LATIN SMALL LETTER D character
+ case 0x65: // U+0065 LATIN SMALL LETTER E character
+ case 0x66: // U+0066 LATIN SMALL LETTER F character
+ case 0x67: // U+0067 LATIN SMALL LETTER G character
+ case 0x68: // U+0068 LATIN SMALL LETTER H character
+ case 0x69: // U+0069 LATIN SMALL LETTER I character
+ case 0x6A: // U+006A LATIN SMALL LETTER J character
+ case 0x6B: // U+006B LATIN SMALL LETTER K character
+ case 0x6C: // U+006C LATIN SMALL LETTER L character
+ case 0x6D: // U+006D LATIN SMALL LETTER M character
+ case 0x6E: // U+006E LATIN SMALL LETTER N character
+ case 0x6F: // U+006F LATIN SMALL LETTER O character
+ case 0x70: // U+0070 LATIN SMALL LETTER P character
+ case 0x71: // U+0071 LATIN SMALL LETTER Q character
+ case 0x72: // U+0072 LATIN SMALL LETTER R character
+ case 0x73: // U+0073 LATIN SMALL LETTER S character
+ case 0x74: // U+0074 LATIN SMALL LETTER T character
+ case 0x75: // U+0075 LATIN SMALL LETTER U character
+ case 0x76: // U+0076 LATIN SMALL LETTER V character
+ case 0x77: // U+0077 LATIN SMALL LETTER W character
+ case 0x78: // U+0078 LATIN SMALL LETTER X character
+ case 0x79: // U+0079 LATIN SMALL LETTER Y character
+ case 0x7A: // U+007A LATIN SMALL LETTER Z character
+ case 0x5F: // U+005F LOW LINE character (_)
+ assert(buffer.isEmpty);
+ mode = _TokenizerMode.identifier;
+ buffer.add(current);
+ break;
+ default:
+ throw ParserException('Unexpected character ${_describeRune(current)}', line, column);
+ }
+ break;
+
+ case _TokenizerMode.minus: // "-"
+ assert(buffer.length == 1 && buffer[0] == 0x2D);
+ switch (current) {
+ case -1:
+ throw ParserException('Unexpected end of file after minus sign', line, column);
+ case 0x30: // U+0030 DIGIT ZERO character (0)
+ case 0x31: // U+0031 DIGIT ONE character (1)
+ case 0x32: // U+0032 DIGIT TWO character (2)
+ case 0x33: // U+0033 DIGIT THREE character (3)
+ case 0x34: // U+0034 DIGIT FOUR character (4)
+ case 0x35: // U+0035 DIGIT FIVE character (5)
+ case 0x36: // U+0036 DIGIT SIX character (6)
+ case 0x37: // U+0037 DIGIT SEVEN character (7)
+ case 0x38: // U+0038 DIGIT EIGHT character (8)
+ case 0x39: // U+0039 DIGIT NINE character (9)
+ mode = _TokenizerMode.minusInteger;
+ buffer.add(current);
+ break;
+ default:
+ throw ParserException('Unexpected character ${_describeRune(current)} after minus sign (expected digit)', line, column);
+ }
+ break;
+
+ case _TokenizerMode.zero: // "0"
+ assert(buffer.length == 1 && buffer[0] == 0x30);
+ switch (current) {
+ case -1:
+ yield _IntegerToken(0, line, column);
+ yield _EofToken(line, column);
+ return;
+ case 0x0A: // U+000A LINE FEED (LF)
+ case 0x20: // U+0020 SPACE character
+ yield _IntegerToken(0, line, column);
+ buffer.clear();
+ mode = _TokenizerMode.main;
+ break;
+ case 0x28: // U+0028 LEFT PARENTHESIS character (()
+ case 0x29: // U+0029 RIGHT PARENTHESIS character ())
+ case 0x2C: // U+002C COMMA character (,)
+ case 0x3A: // U+003A COLON character (:)
+ case 0x3B: // U+003B SEMICOLON character (;)
+ case 0x3D: // U+003D EQUALS SIGN character (=)
+ case 0x5B: // U+005B LEFT SQUARE BRACKET character ([)
+ case 0x5D: // U+005D RIGHT SQUARE BRACKET character (])
+ case 0x7B: // U+007B LEFT CURLY BRACKET character ({)
+ case 0x7D: // U+007D RIGHT CURLY BRACKET character (})
+ yield _IntegerToken(0, line, column);
+ buffer.clear();
+ yield _SymbolToken(current, line, column);
+ mode = _TokenizerMode.main;
+ break;
+ case 0x2E: // U+002E FULL STOP character (.)
+ mode = _TokenizerMode.numericDot;
+ buffer.add(current);
+ break;
+ case 0x30: // U+0030 DIGIT ZERO character (0)
+ case 0x31: // U+0031 DIGIT ONE character (1)
+ case 0x32: // U+0032 DIGIT TWO character (2)
+ case 0x33: // U+0033 DIGIT THREE character (3)
+ case 0x34: // U+0034 DIGIT FOUR character (4)
+ case 0x35: // U+0035 DIGIT FIVE character (5)
+ case 0x36: // U+0036 DIGIT SIX character (6)
+ case 0x37: // U+0037 DIGIT SEVEN character (7)
+ case 0x38: // U+0038 DIGIT EIGHT character (8)
+ case 0x39: // U+0039 DIGIT NINE character (9)
+ mode = _TokenizerMode.integer;
+ buffer.add(current);
+ break;
+ case 0x45: // U+0045 LATIN CAPITAL LETTER E character
+ case 0x65: // U+0065 LATIN SMALL LETTER E character
+ mode = _TokenizerMode.e;
+ buffer.add(current);
+ break;
+ case 0x58: // U+0058 LATIN CAPITAL LETTER X character
+ case 0x78: // U+0078 LATIN SMALL LETTER X character
+ mode = _TokenizerMode.x;
+ buffer.clear();
+ break;
+ default:
+ throw ParserException('Unexpected character ${_describeRune(current)} after zero', line, column);
+ }
+ break;
+
+ case _TokenizerMode.minusInteger: // "-0"
+ switch (current) {
+ case -1:
+ yield _IntegerToken(int.parse(String.fromCharCodes(buffer), radix: 10), line, column);
+ yield _EofToken(line, column);
+ return;
+ case 0x0A: // U+000A LINE FEED (LF)
+ case 0x20: // U+0020 SPACE character
+ yield _IntegerToken(int.parse(String.fromCharCodes(buffer), radix: 10), line, column);
+ buffer.clear();
+ mode = _TokenizerMode.main;
+ break;
+ case 0x28: // U+0028 LEFT PARENTHESIS character (()
+ case 0x29: // U+0029 RIGHT PARENTHESIS character ())
+ case 0x2C: // U+002C COMMA character (,)
+ case 0x3A: // U+003A COLON character (:)
+ case 0x3B: // U+003B SEMICOLON character (;)
+ case 0x3D: // U+003D EQUALS SIGN character (=)
+ case 0x5B: // U+005B LEFT SQUARE BRACKET character ([)
+ case 0x5D: // U+005D RIGHT SQUARE BRACKET character (])
+ case 0x7B: // U+007B LEFT CURLY BRACKET character ({)
+ case 0x7D: // U+007D RIGHT CURLY BRACKET character (})
+ yield _IntegerToken(int.parse(String.fromCharCodes(buffer), radix: 10), line, column);
+ buffer.clear();
+ yield _SymbolToken(current, line, column);
+ mode = _TokenizerMode.main;
+ break;
+ case 0x2E: // U+002E FULL STOP character (.)
+ mode = _TokenizerMode.numericDot;
+ buffer.add(current);
+ break;
+ case 0x30: // U+0030 DIGIT ZERO character (0)
+ case 0x31: // U+0031 DIGIT ONE character (1)
+ case 0x32: // U+0032 DIGIT TWO character (2)
+ case 0x33: // U+0033 DIGIT THREE character (3)
+ case 0x34: // U+0034 DIGIT FOUR character (4)
+ case 0x35: // U+0035 DIGIT FIVE character (5)
+ case 0x36: // U+0036 DIGIT SIX character (6)
+ case 0x37: // U+0037 DIGIT SEVEN character (7)
+ case 0x38: // U+0038 DIGIT EIGHT character (8)
+ case 0x39: // U+0039 DIGIT NINE character (9)
+ mode = _TokenizerMode.integer;
+ buffer.add(current);
+ break;
+ case 0x45: // U+0045 LATIN CAPITAL LETTER E character
+ case 0x65: // U+0065 LATIN SMALL LETTER E character
+ mode = _TokenizerMode.e;
+ buffer.add(current);
+ break;
+ default:
+ throw ParserException('Unexpected character ${_describeRune(current)} after negative zero', line, column);
+ }
+ break;
+
+ case _TokenizerMode.integer: // "00", "1", "-00"
+ switch (current) {
+ case -1:
+ yield _IntegerToken(int.parse(String.fromCharCodes(buffer), radix: 10), line, column);
+ buffer.clear();
+ yield _EofToken(line, column);
+ return;
+ case 0x0A: // U+000A LINE FEED (LF)
+ case 0x20: // U+0020 SPACE character
+ yield _IntegerToken(int.parse(String.fromCharCodes(buffer), radix: 10), line, column);
+ buffer.clear();
+ mode = _TokenizerMode.main;
+ break;
+ case 0x28: // U+0028 LEFT PARENTHESIS character (()
+ case 0x29: // U+0029 RIGHT PARENTHESIS character ())
+ case 0x2C: // U+002C COMMA character (,)
+ case 0x3A: // U+003A COLON character (:)
+ case 0x3B: // U+003B SEMICOLON character (;)
+ case 0x3D: // U+003D EQUALS SIGN character (=)
+ case 0x5B: // U+005B LEFT SQUARE BRACKET character ([)
+ case 0x5D: // U+005D RIGHT SQUARE BRACKET character (])
+ case 0x7B: // U+007B LEFT CURLY BRACKET character ({)
+ case 0x7D: // U+007D RIGHT CURLY BRACKET character (})
+ yield _IntegerToken(int.parse(String.fromCharCodes(buffer), radix: 10), line, column);
+ buffer.clear();
+ yield _SymbolToken(current, line, column);
+ mode = _TokenizerMode.main;
+ break;
+ case 0x2E: // U+002E FULL STOP character (.)
+ mode = _TokenizerMode.numericDot;
+ buffer.add(current);
+ break;
+ case 0x30: // U+0030 DIGIT ZERO character (0)
+ case 0x31: // U+0031 DIGIT ONE character (1)
+ case 0x32: // U+0032 DIGIT TWO character (2)
+ case 0x33: // U+0033 DIGIT THREE character (3)
+ case 0x34: // U+0034 DIGIT FOUR character (4)
+ case 0x35: // U+0035 DIGIT FIVE character (5)
+ case 0x36: // U+0036 DIGIT SIX character (6)
+ case 0x37: // U+0037 DIGIT SEVEN character (7)
+ case 0x38: // U+0038 DIGIT EIGHT character (8)
+ case 0x39: // U+0039 DIGIT NINE character (9)
+ buffer.add(current);
+ break;
+ case 0x45: // U+0045 LATIN CAPITAL LETTER E character
+ case 0x65: // U+0065 LATIN SMALL LETTER E character
+ mode = _TokenizerMode.e;
+ buffer.add(current);
+ break;
+ default:
+ throw ParserException('Unexpected character ${_describeRune(current)}', line, column);
+ }
+ break;
+
+ case _TokenizerMode.integerOnly:
+ switch (current) {
+ case -1:
+ yield _IntegerToken(int.parse(String.fromCharCodes(buffer), radix: 10), line, column);
+ buffer.clear();
+ yield _EofToken(line, column);
+ return;
+ case 0x0A: // U+000A LINE FEED (LF)
+ case 0x20: // U+0020 SPACE character
+ yield _IntegerToken(int.parse(String.fromCharCodes(buffer), radix: 10), line, column);
+ buffer.clear();
+ mode = _TokenizerMode.main;
+ break;
+ case 0x28: // U+0028 LEFT PARENTHESIS character (()
+ case 0x29: // U+0029 RIGHT PARENTHESIS character ())
+ case 0x2C: // U+002C COMMA character (,)
+ case 0x3A: // U+003A COLON character (:)
+ case 0x3B: // U+003B SEMICOLON character (;)
+ case 0x3D: // U+003D EQUALS SIGN character (=)
+ case 0x5B: // U+005B LEFT SQUARE BRACKET character ([)
+ case 0x5D: // U+005D RIGHT SQUARE BRACKET character (])
+ case 0x7B: // U+007B LEFT CURLY BRACKET character ({)
+ case 0x7D: // U+007D RIGHT CURLY BRACKET character (})
+ yield _IntegerToken(int.parse(String.fromCharCodes(buffer), radix: 10), line, column);
+ buffer.clear();
+ yield _SymbolToken(current, line, column);
+ mode = _TokenizerMode.main;
+ break;
+ case 0x2E: // U+002E FULL STOP character (.)
+ yield _IntegerToken(int.parse(String.fromCharCodes(buffer), radix: 10), line, column);
+ buffer.clear();
+ mode = _TokenizerMode.dot1;
+ break;
+ case 0x30: // U+0030 DIGIT ZERO character (0)
+ case 0x31: // U+0031 DIGIT ONE character (1)
+ case 0x32: // U+0032 DIGIT TWO character (2)
+ case 0x33: // U+0033 DIGIT THREE character (3)
+ case 0x34: // U+0034 DIGIT FOUR character (4)
+ case 0x35: // U+0035 DIGIT FIVE character (5)
+ case 0x36: // U+0036 DIGIT SIX character (6)
+ case 0x37: // U+0037 DIGIT SEVEN character (7)
+ case 0x38: // U+0038 DIGIT EIGHT character (8)
+ case 0x39: // U+0039 DIGIT NINE character (9)
+ buffer.add(current);
+ break;
+ default:
+ throw ParserException('Unexpected character ${_describeRune(current)} in integer', line, column);
+ }
+ break;
+
+ case _TokenizerMode.numericDot: // "0.", "-0.", "00.", "1.", "-00."
+ switch (current) {
+ case -1:
+ throw ParserException('Unexpected end of file after decimal point', line, column);
+ case 0x30: // U+0030 DIGIT ZERO character (0)
+ case 0x31: // U+0031 DIGIT ONE character (1)
+ case 0x32: // U+0032 DIGIT TWO character (2)
+ case 0x33: // U+0033 DIGIT THREE character (3)
+ case 0x34: // U+0034 DIGIT FOUR character (4)
+ case 0x35: // U+0035 DIGIT FIVE character (5)
+ case 0x36: // U+0036 DIGIT SIX character (6)
+ case 0x37: // U+0037 DIGIT SEVEN character (7)
+ case 0x38: // U+0038 DIGIT EIGHT character (8)
+ case 0x39: // U+0039 DIGIT NINE character (9)
+ mode = _TokenizerMode.fraction;
+ buffer.add(current);
+ break;
+ default:
+ throw ParserException('Unexpected character ${_describeRune(current)} in fraction component', line, column);
+ }
+ break;
+
+ case _TokenizerMode.fraction: // "0.0", "-0.0", "00.0", "1.0", "-00.0"
+ switch (current) {
+ case -1:
+ yield _DoubleToken(double.parse(String.fromCharCodes(buffer)), line, column);
+ yield _EofToken(line, column);
+ return;
+ case 0x0A: // U+000A LINE FEED (LF)
+ case 0x20: // U+0020 SPACE character
+ yield _DoubleToken(double.parse(String.fromCharCodes(buffer)), line, column);
+ buffer.clear();
+ mode = _TokenizerMode.main;
+ break;
+ case 0x28: // U+0028 LEFT PARENTHESIS character (()
+ case 0x29: // U+0029 RIGHT PARENTHESIS character ())
+ case 0x2C: // U+002C COMMA character (,)
+ case 0x3A: // U+003A COLON character (:)
+ case 0x3B: // U+003B SEMICOLON character (;)
+ case 0x3D: // U+003D EQUALS SIGN character (=)
+ case 0x5B: // U+005B LEFT SQUARE BRACKET character ([)
+ case 0x5D: // U+005D RIGHT SQUARE BRACKET character (])
+ case 0x7B: // U+007B LEFT CURLY BRACKET character ({)
+ case 0x7D: // U+007D RIGHT CURLY BRACKET character (})
+ yield _DoubleToken(double.parse(String.fromCharCodes(buffer)), line, column);
+ buffer.clear();
+ yield _SymbolToken(current, line, column);
+ mode = _TokenizerMode.main;
+ break;
+ case 0x30: // U+0030 DIGIT ZERO character (0)
+ case 0x31: // U+0031 DIGIT ONE character (1)
+ case 0x32: // U+0032 DIGIT TWO character (2)
+ case 0x33: // U+0033 DIGIT THREE character (3)
+ case 0x34: // U+0034 DIGIT FOUR character (4)
+ case 0x35: // U+0035 DIGIT FIVE character (5)
+ case 0x36: // U+0036 DIGIT SIX character (6)
+ case 0x37: // U+0037 DIGIT SEVEN character (7)
+ case 0x38: // U+0038 DIGIT EIGHT character (8)
+ case 0x39: // U+0039 DIGIT NINE character (9)
+ buffer.add(current);
+ break;
+ case 0x45: // U+0045 LATIN CAPITAL LETTER E character
+ case 0x65: // U+0065 LATIN SMALL LETTER E character
+ mode = _TokenizerMode.e;
+ buffer.add(current);
+ break;
+ default:
+ throw ParserException('Unexpected character ${_describeRune(current)} in fraction component', line, column);
+ }
+ break;
+
+ case _TokenizerMode.e: // "0e", "-0e", "00e", "1e", "-00e", "0.0e", "-0.0e", "00.0e", "1.0e", "-00.0e"
+ switch (current) {
+ case -1:
+ throw ParserException('Unexpected end of file after exponent separator', line, column);
+ case 0x2D: // U+002D HYPHEN-MINUS character (-)
+ mode = _TokenizerMode.negativeExponent;
+ buffer.add(current);
+ break;
+ case 0x30: // U+0030 DIGIT ZERO character (0)
+ case 0x31: // U+0031 DIGIT ONE character (1)
+ case 0x32: // U+0032 DIGIT TWO character (2)
+ case 0x33: // U+0033 DIGIT THREE character (3)
+ case 0x34: // U+0034 DIGIT FOUR character (4)
+ case 0x35: // U+0035 DIGIT FIVE character (5)
+ case 0x36: // U+0036 DIGIT SIX character (6)
+ case 0x37: // U+0037 DIGIT SEVEN character (7)
+ case 0x38: // U+0038 DIGIT EIGHT character (8)
+ case 0x39: // U+0039 DIGIT NINE character (9)
+ mode = _TokenizerMode.exponent;
+ buffer.add(current);
+ break;
+ default:
+ throw ParserException('Unexpected character ${_describeRune(current)} after exponent separator', line, column);
+ }
+ break;
+
+ case _TokenizerMode.negativeExponent: // "0e-", "-0e-", "00e-", "1e-", "-00e-", "0.0e-", "-0.0e-", "00.0e-", "1.0e-", "-00.0e-"
+ switch (current) {
+ case -1:
+ throw ParserException('Unexpected end of file after exponent separator and minus sign', line, column);
+ case 0x30: // U+0030 DIGIT ZERO character (0)
+ case 0x31: // U+0031 DIGIT ONE character (1)
+ case 0x32: // U+0032 DIGIT TWO character (2)
+ case 0x33: // U+0033 DIGIT THREE character (3)
+ case 0x34: // U+0034 DIGIT FOUR character (4)
+ case 0x35: // U+0035 DIGIT FIVE character (5)
+ case 0x36: // U+0036 DIGIT SIX character (6)
+ case 0x37: // U+0037 DIGIT SEVEN character (7)
+ case 0x38: // U+0038 DIGIT EIGHT character (8)
+ case 0x39: // U+0039 DIGIT NINE character (9)
+ mode = _TokenizerMode.exponent;
+ buffer.add(current);
+ break;
+ default:
+ throw ParserException('Unexpected character ${_describeRune(current)} in exponent', line, column);
+ }
+ break;
+
+ case _TokenizerMode.exponent: // "0e0", "-0e0", "00e0", "1e0", "-00e0", "0.0e0", "-0.0e0", "00.0e0", "1.0e0", "-00.0e0", "0e-0", "-0e-0", "00e-0", "1e-0", "-00e-0", "0.0e-0", "-0.0e-0", "00.0e-0", "1.0e-0", "-00.0e-0"
+ switch (current) {
+ case -1:
+ yield _DoubleToken(double.parse(String.fromCharCodes(buffer)), line, column);
+ yield _EofToken(line, column);
+ return;
+ case 0x0A: // U+000A LINE FEED (LF)
+ case 0x20: // U+0020 SPACE character
+ yield _DoubleToken(double.parse(String.fromCharCodes(buffer)), line, column);
+ buffer.clear();
+ mode = _TokenizerMode.main;
+ break;
+ case 0x28: // U+0028 LEFT PARENTHESIS character (()
+ case 0x29: // U+0029 RIGHT PARENTHESIS character ())
+ case 0x2C: // U+002C COMMA character (,)
+ case 0x3A: // U+003A COLON character (:)
+ case 0x3B: // U+003B SEMICOLON character (;)
+ case 0x3D: // U+003D EQUALS SIGN character (=)
+ case 0x5B: // U+005B LEFT SQUARE BRACKET character ([)
+ case 0x5D: // U+005D RIGHT SQUARE BRACKET character (])
+ case 0x7B: // U+007B LEFT CURLY BRACKET character ({)
+ case 0x7D: // U+007D RIGHT CURLY BRACKET character (})
+ yield _DoubleToken(double.parse(String.fromCharCodes(buffer)), line, column);
+ buffer.clear();
+ yield _SymbolToken(current, line, column);
+ mode = _TokenizerMode.main;
+ break;
+ case 0x30: // U+0030 DIGIT ZERO character (0)
+ case 0x31: // U+0031 DIGIT ONE character (1)
+ case 0x32: // U+0032 DIGIT TWO character (2)
+ case 0x33: // U+0033 DIGIT THREE character (3)
+ case 0x34: // U+0034 DIGIT FOUR character (4)
+ case 0x35: // U+0035 DIGIT FIVE character (5)
+ case 0x36: // U+0036 DIGIT SIX character (6)
+ case 0x37: // U+0037 DIGIT SEVEN character (7)
+ case 0x38: // U+0038 DIGIT EIGHT character (8)
+ case 0x39: // U+0039 DIGIT NINE character (9)
+ buffer.add(current);
+ break;
+ default:
+ throw ParserException('Unexpected character ${_describeRune(current)} in exponent', line, column);
+ }
+ break;
+
+ case _TokenizerMode.x: // "0x", "0X"
+ switch (current) {
+ case -1:
+ throw ParserException('Unexpected end of file after 0x prefix', line, column);
+ case 0x30: // U+0030 DIGIT ZERO character (0)
+ case 0x31: // U+0031 DIGIT ONE character (1)
+ case 0x32: // U+0032 DIGIT TWO character (2)
+ case 0x33: // U+0033 DIGIT THREE character (3)
+ case 0x34: // U+0034 DIGIT FOUR character (4)
+ case 0x35: // U+0035 DIGIT FIVE character (5)
+ case 0x36: // U+0036 DIGIT SIX character (6)
+ case 0x37: // U+0037 DIGIT SEVEN character (7)
+ case 0x38: // U+0038 DIGIT EIGHT character (8)
+ case 0x39: // U+0039 DIGIT NINE character (9)
+ case 0x41: // U+0041 LATIN CAPITAL LETTER A character
+ case 0x42: // U+0042 LATIN CAPITAL LETTER B character
+ case 0x43: // U+0043 LATIN CAPITAL LETTER C character
+ case 0x44: // U+0044 LATIN CAPITAL LETTER D character
+ case 0x45: // U+0045 LATIN CAPITAL LETTER E character
+ case 0x46: // U+0046 LATIN CAPITAL LETTER F character
+ case 0x61: // U+0061 LATIN SMALL LETTER A character
+ case 0x62: // U+0062 LATIN SMALL LETTER B character
+ case 0x63: // U+0063 LATIN SMALL LETTER C character
+ case 0x64: // U+0064 LATIN SMALL LETTER D character
+ case 0x65: // U+0065 LATIN SMALL LETTER E character
+ case 0x66: // U+0066 LATIN SMALL LETTER F character
+ mode = _TokenizerMode.hex;
+ buffer.add(current);
+ break;
+ default:
+ throw ParserException('Unexpected character ${_describeRune(current)} after 0x prefix', line, column);
+ }
+ break;
+
+ case _TokenizerMode.hex:
+ switch (current) {
+ case -1:
+ yield _IntegerToken(int.parse(String.fromCharCodes(buffer), radix: 16), line, column);
+ yield _EofToken(line, column);
+ return;
+ case 0x0A: // U+000A LINE FEED (LF)
+ case 0x20: // U+0020 SPACE character
+ yield _IntegerToken(int.parse(String.fromCharCodes(buffer), radix: 16), line, column);
+ buffer.clear();
+ mode = _TokenizerMode.main;
+ break;
+ case 0x28: // U+0028 LEFT PARENTHESIS character (()
+ case 0x29: // U+0029 RIGHT PARENTHESIS character ())
+ case 0x2C: // U+002C COMMA character (,)
+ case 0x3A: // U+003A COLON character (:)
+ case 0x3B: // U+003B SEMICOLON character (;)
+ case 0x3D: // U+003D EQUALS SIGN character (=)
+ case 0x5B: // U+005B LEFT SQUARE BRACKET character ([)
+ case 0x5D: // U+005D RIGHT SQUARE BRACKET character (])
+ case 0x7B: // U+007B LEFT CURLY BRACKET character ({)
+ case 0x7D: // U+007D RIGHT CURLY BRACKET character (})
+ yield _IntegerToken(int.parse(String.fromCharCodes(buffer), radix: 16), line, column);
+ buffer.clear();
+ yield _SymbolToken(current, line, column);
+ mode = _TokenizerMode.main;
+ break;
+ case 0x30: // U+0030 DIGIT ZERO character (0)
+ case 0x31: // U+0031 DIGIT ONE character (1)
+ case 0x32: // U+0032 DIGIT TWO character (2)
+ case 0x33: // U+0033 DIGIT THREE character (3)
+ case 0x34: // U+0034 DIGIT FOUR character (4)
+ case 0x35: // U+0035 DIGIT FIVE character (5)
+ case 0x36: // U+0036 DIGIT SIX character (6)
+ case 0x37: // U+0037 DIGIT SEVEN character (7)
+ case 0x38: // U+0038 DIGIT EIGHT character (8)
+ case 0x39: // U+0039 DIGIT NINE character (9)
+ case 0x41: // U+0041 LATIN CAPITAL LETTER A character
+ case 0x42: // U+0042 LATIN CAPITAL LETTER B character
+ case 0x43: // U+0043 LATIN CAPITAL LETTER C character
+ case 0x44: // U+0044 LATIN CAPITAL LETTER D character
+ case 0x45: // U+0045 LATIN CAPITAL LETTER E character
+ case 0x46: // U+0046 LATIN CAPITAL LETTER F character
+ case 0x61: // U+0061 LATIN SMALL LETTER A character
+ case 0x62: // U+0062 LATIN SMALL LETTER B character
+ case 0x63: // U+0063 LATIN SMALL LETTER C character
+ case 0x64: // U+0064 LATIN SMALL LETTER D character
+ case 0x65: // U+0065 LATIN SMALL LETTER E character
+ case 0x66: // U+0066 LATIN SMALL LETTER F character
+ buffer.add(current);
+ break;
+ default:
+ throw ParserException('Unexpected character ${_describeRune(current)} in hex literal', line, column);
+ }
+ break;
+
+ case _TokenizerMode.dot1: // "."
+ switch (current) {
+ case -1:
+ yield _SymbolToken(0x2E, line, column);
+ yield _EofToken(line, column);
+ return;
+ case 0x0A: // U+000A LINE FEED (LF)
+ case 0x20: // U+0020 SPACE character
+ yield _SymbolToken(0x2E, line, column);
+ mode = _TokenizerMode.main;
+ break;
+ case 0x22: // U+0022 QUOTATION MARK character (")
+ yield _SymbolToken(0x2E, line, column);
+ assert(buffer.isEmpty);
+ mode = _TokenizerMode.doubleQuote;
+ break;
+ case 0x27: // U+0027 APOSTROPHE character (')
+ yield _SymbolToken(0x2E, line, column);
+ assert(buffer.isEmpty);
+ mode = _TokenizerMode.quote;
+ break;
+ case 0x28: // U+0028 LEFT PARENTHESIS character (()
+ case 0x29: // U+0029 RIGHT PARENTHESIS character ())
+ case 0x2C: // U+002C COMMA character (,)
+ case 0x3A: // U+003A COLON character (:)
+ case 0x3B: // U+003B SEMICOLON character (;)
+ case 0x3D: // U+003D EQUALS SIGN character (=)
+ case 0x5B: // U+005B LEFT SQUARE BRACKET character ([)
+ case 0x5D: // U+005D RIGHT SQUARE BRACKET character (])
+ case 0x7B: // U+007B LEFT CURLY BRACKET character ({)
+ case 0x7D: // U+007D RIGHT CURLY BRACKET character (})
+ yield _SymbolToken(0x2E, line, column);
+ yield _SymbolToken(current, line, column);
+ break;
+ case 0x2E: // U+002E FULL STOP character (.)
+ mode = _TokenizerMode.dot2;
+ break;
+ case 0x30: // U+0030 DIGIT ZERO character (0)
+ case 0x31: // U+0031 DIGIT ONE character (1)
+ case 0x32: // U+0032 DIGIT TWO character (2)
+ case 0x33: // U+0033 DIGIT THREE character (3)
+ case 0x34: // U+0034 DIGIT FOUR character (4)
+ case 0x35: // U+0035 DIGIT FIVE character (5)
+ case 0x36: // U+0036 DIGIT SIX character (6)
+ case 0x37: // U+0037 DIGIT SEVEN character (7)
+ case 0x38: // U+0038 DIGIT EIGHT character (8)
+ case 0x39: // U+0039 DIGIT NINE character (9)
+ yield _SymbolToken(0x2E, line, column);
+ assert(buffer.isEmpty);
+ mode = _TokenizerMode.integerOnly;
+ buffer.add(current);
+ break;
+ case 0x41: // U+0041 LATIN CAPITAL LETTER A character
+ case 0x42: // U+0042 LATIN CAPITAL LETTER B character
+ case 0x43: // U+0043 LATIN CAPITAL LETTER C character
+ case 0x44: // U+0044 LATIN CAPITAL LETTER D character
+ case 0x45: // U+0045 LATIN CAPITAL LETTER E character
+ case 0x46: // U+0046 LATIN CAPITAL LETTER F character
+ case 0x47: // U+0047 LATIN CAPITAL LETTER G character
+ case 0x48: // U+0048 LATIN CAPITAL LETTER H character
+ case 0x49: // U+0049 LATIN CAPITAL LETTER I character
+ case 0x4A: // U+004A LATIN CAPITAL LETTER J character
+ case 0x4B: // U+004B LATIN CAPITAL LETTER K character
+ case 0x4C: // U+004C LATIN CAPITAL LETTER L character
+ case 0x4D: // U+004D LATIN CAPITAL LETTER M character
+ case 0x4E: // U+004E LATIN CAPITAL LETTER N character
+ case 0x4F: // U+004F LATIN CAPITAL LETTER O character
+ case 0x50: // U+0050 LATIN CAPITAL LETTER P character
+ case 0x51: // U+0051 LATIN CAPITAL LETTER Q character
+ case 0x52: // U+0052 LATIN CAPITAL LETTER R character
+ case 0x53: // U+0053 LATIN CAPITAL LETTER S character
+ case 0x54: // U+0054 LATIN CAPITAL LETTER T character
+ case 0x55: // U+0055 LATIN CAPITAL LETTER U character
+ case 0x56: // U+0056 LATIN CAPITAL LETTER V character
+ case 0x57: // U+0057 LATIN CAPITAL LETTER W character
+ case 0x58: // U+0058 LATIN CAPITAL LETTER X character
+ case 0x59: // U+0059 LATIN CAPITAL LETTER Y character
+ case 0x5A: // U+005A LATIN CAPITAL LETTER Z character
+ case 0x61: // U+0061 LATIN SMALL LETTER A character
+ case 0x62: // U+0062 LATIN SMALL LETTER B character
+ case 0x63: // U+0063 LATIN SMALL LETTER C character
+ case 0x64: // U+0064 LATIN SMALL LETTER D character
+ case 0x65: // U+0065 LATIN SMALL LETTER E character
+ case 0x66: // U+0066 LATIN SMALL LETTER F character
+ case 0x67: // U+0067 LATIN SMALL LETTER G character
+ case 0x68: // U+0068 LATIN SMALL LETTER H character
+ case 0x69: // U+0069 LATIN SMALL LETTER I character
+ case 0x6A: // U+006A LATIN SMALL LETTER J character
+ case 0x6B: // U+006B LATIN SMALL LETTER K character
+ case 0x6C: // U+006C LATIN SMALL LETTER L character
+ case 0x6D: // U+006D LATIN SMALL LETTER M character
+ case 0x6E: // U+006E LATIN SMALL LETTER N character
+ case 0x6F: // U+006F LATIN SMALL LETTER O character
+ case 0x70: // U+0070 LATIN SMALL LETTER P character
+ case 0x71: // U+0071 LATIN SMALL LETTER Q character
+ case 0x72: // U+0072 LATIN SMALL LETTER R character
+ case 0x73: // U+0073 LATIN SMALL LETTER S character
+ case 0x74: // U+0074 LATIN SMALL LETTER T character
+ case 0x75: // U+0075 LATIN SMALL LETTER U character
+ case 0x76: // U+0076 LATIN SMALL LETTER V character
+ case 0x77: // U+0077 LATIN SMALL LETTER W character
+ case 0x78: // U+0078 LATIN SMALL LETTER X character
+ case 0x79: // U+0079 LATIN SMALL LETTER Y character
+ case 0x7A: // U+007A LATIN SMALL LETTER Z character
+ case 0x5F: // U+005F LOW LINE character (_)
+ yield _SymbolToken(0x2E, line, column);
+ assert(buffer.isEmpty);
+ mode = _TokenizerMode.identifier;
+ buffer.add(current);
+ break;
+ default:
+ throw ParserException('Unexpected character ${_describeRune(current)} after period', line, column);
+ }
+ break;
+
+ case _TokenizerMode.dot2: // ".."
+ switch (current) {
+ case -1:
+ throw ParserException('Unexpected end of file inside "..." symbol', line, column);
+ case 0x2E: // U+002E FULL STOP character (.)
+ yield _SymbolToken(_SymbolToken.tripleDot, line, column);
+ mode = _TokenizerMode.main;
+ break;
+ default:
+ throw ParserException('Unexpected character ${_describeRune(current)} inside "..." symbol', line, column);
+ }
+ break;
+
+ case _TokenizerMode.identifier:
+ switch (current) {
+ case -1:
+ yield _IdentifierToken(String.fromCharCodes(buffer), line, column);
+ yield _EofToken(line, column);
+ return;
+ case 0x0A: // U+000A LINE FEED (LF)
+ case 0x20: // U+0020 SPACE character
+ yield _IdentifierToken(String.fromCharCodes(buffer), line, column);
+ buffer.clear();
+ mode = _TokenizerMode.main;
+ break;
+ case 0x28: // U+0028 LEFT PARENTHESIS character (()
+ case 0x29: // U+0029 RIGHT PARENTHESIS character ())
+ case 0x2C: // U+002C COMMA character (,)
+ case 0x3A: // U+003A COLON character (:)
+ case 0x3B: // U+003B SEMICOLON character (;)
+ case 0x3D: // U+003D EQUALS SIGN character (=)
+ case 0x5B: // U+005B LEFT SQUARE BRACKET character ([)
+ case 0x5D: // U+005D RIGHT SQUARE BRACKET character (])
+ case 0x7B: // U+007B LEFT CURLY BRACKET character ({)
+ case 0x7D: // U+007D RIGHT CURLY BRACKET character (})
+ yield _IdentifierToken(String.fromCharCodes(buffer), line, column);
+ buffer.clear();
+ yield _SymbolToken(current, line, column);
+ mode = _TokenizerMode.main;
+ break;
+ case 0x2E: // U+002E FULL STOP character (.)
+ yield _IdentifierToken(String.fromCharCodes(buffer), line, column);
+ buffer.clear();
+ mode = _TokenizerMode.dot1;
+ break;
+ case 0x30: // U+0030 DIGIT ZERO character (0)
+ case 0x31: // U+0031 DIGIT ONE character (1)
+ case 0x32: // U+0032 DIGIT TWO character (2)
+ case 0x33: // U+0033 DIGIT THREE character (3)
+ case 0x34: // U+0034 DIGIT FOUR character (4)
+ case 0x35: // U+0035 DIGIT FIVE character (5)
+ case 0x36: // U+0036 DIGIT SIX character (6)
+ case 0x37: // U+0037 DIGIT SEVEN character (7)
+ case 0x38: // U+0038 DIGIT EIGHT character (8)
+ case 0x39: // U+0039 DIGIT NINE character (9)
+ case 0x41: // U+0041 LATIN CAPITAL LETTER A character
+ case 0x42: // U+0042 LATIN CAPITAL LETTER B character
+ case 0x43: // U+0043 LATIN CAPITAL LETTER C character
+ case 0x44: // U+0044 LATIN CAPITAL LETTER D character
+ case 0x45: // U+0045 LATIN CAPITAL LETTER E character
+ case 0x46: // U+0046 LATIN CAPITAL LETTER F character
+ case 0x47: // U+0047 LATIN CAPITAL LETTER G character
+ case 0x48: // U+0048 LATIN CAPITAL LETTER H character
+ case 0x49: // U+0049 LATIN CAPITAL LETTER I character
+ case 0x4A: // U+004A LATIN CAPITAL LETTER J character
+ case 0x4B: // U+004B LATIN CAPITAL LETTER K character
+ case 0x4C: // U+004C LATIN CAPITAL LETTER L character
+ case 0x4D: // U+004D LATIN CAPITAL LETTER M character
+ case 0x4E: // U+004E LATIN CAPITAL LETTER N character
+ case 0x4F: // U+004F LATIN CAPITAL LETTER O character
+ case 0x50: // U+0050 LATIN CAPITAL LETTER P character
+ case 0x51: // U+0051 LATIN CAPITAL LETTER Q character
+ case 0x52: // U+0052 LATIN CAPITAL LETTER R character
+ case 0x53: // U+0053 LATIN CAPITAL LETTER S character
+ case 0x54: // U+0054 LATIN CAPITAL LETTER T character
+ case 0x55: // U+0055 LATIN CAPITAL LETTER U character
+ case 0x56: // U+0056 LATIN CAPITAL LETTER V character
+ case 0x57: // U+0057 LATIN CAPITAL LETTER W character
+ case 0x58: // U+0058 LATIN CAPITAL LETTER X character
+ case 0x59: // U+0059 LATIN CAPITAL LETTER Y character
+ case 0x5A: // U+005A LATIN CAPITAL LETTER Z character
+ case 0x61: // U+0061 LATIN SMALL LETTER A character
+ case 0x62: // U+0062 LATIN SMALL LETTER B character
+ case 0x63: // U+0063 LATIN SMALL LETTER C character
+ case 0x64: // U+0064 LATIN SMALL LETTER D character
+ case 0x65: // U+0065 LATIN SMALL LETTER E character
+ case 0x66: // U+0066 LATIN SMALL LETTER F character
+ case 0x67: // U+0067 LATIN SMALL LETTER G character
+ case 0x68: // U+0068 LATIN SMALL LETTER H character
+ case 0x69: // U+0069 LATIN SMALL LETTER I character
+ case 0x6A: // U+006A LATIN SMALL LETTER J character
+ case 0x6B: // U+006B LATIN SMALL LETTER K character
+ case 0x6C: // U+006C LATIN SMALL LETTER L character
+ case 0x6D: // U+006D LATIN SMALL LETTER M character
+ case 0x6E: // U+006E LATIN SMALL LETTER N character
+ case 0x6F: // U+006F LATIN SMALL LETTER O character
+ case 0x70: // U+0070 LATIN SMALL LETTER P character
+ case 0x71: // U+0071 LATIN SMALL LETTER Q character
+ case 0x72: // U+0072 LATIN SMALL LETTER R character
+ case 0x73: // U+0073 LATIN SMALL LETTER S character
+ case 0x74: // U+0074 LATIN SMALL LETTER T character
+ case 0x75: // U+0075 LATIN SMALL LETTER U character
+ case 0x76: // U+0076 LATIN SMALL LETTER V character
+ case 0x77: // U+0077 LATIN SMALL LETTER W character
+ case 0x78: // U+0078 LATIN SMALL LETTER X character
+ case 0x79: // U+0079 LATIN SMALL LETTER Y character
+ case 0x7A: // U+007A LATIN SMALL LETTER Z character
+ case 0x5F: // U+005F LOW LINE character (_)
+ buffer.add(current);
+ break;
+ default:
+ throw ParserException('Unexpected character ${_describeRune(current)} inside identifier', line, column);
+ }
+ break;
+
+ case _TokenizerMode.quote:
+ switch (current) {
+ case -1:
+ throw ParserException('Unexpected end of file inside string', line, column);
+ case 0x0A: // U+000A LINE FEED (LF)
+ throw ParserException('Unexpected end of line inside string', line, column);
+ case 0x27: // U+0027 APOSTROPHE character (')
+ yield _StringToken(String.fromCharCodes(buffer), line, column);
+ buffer.clear();
+ mode = _TokenizerMode.endQuote;
+ break;
+ case 0x5C: // U+005C REVERSE SOLIDUS character (\)
+ mode = _TokenizerMode.quoteEscape;
+ break;
+ default:
+ buffer.add(current);
+ }
+ break;
+
+ case _TokenizerMode.quoteEscape:
+ switch (current) {
+ case -1:
+ throw ParserException('Unexpected end of file inside string', line, column);
+ case 0x22: // U+0022 QUOTATION MARK character (")
+ case 0x27: // U+0027 APOSTROPHE character (')
+ case 0x5C: // U+005C REVERSE SOLIDUS character (\)
+ case 0x2F: // U+002F SOLIDUS character (/)
+ buffer.add(current);
+ mode = _TokenizerMode.quote;
+ break;
+ case 0x62: // U+0062 LATIN SMALL LETTER B character
+ buffer.add(0x08);
+ mode = _TokenizerMode.quote;
+ break;
+ case 0x66: // U+0066 LATIN SMALL LETTER F character
+ buffer.add(0x0C);
+ mode = _TokenizerMode.quote;
+ break;
+ case 0x6E: // U+006E LATIN SMALL LETTER N character
+ buffer.add(0x0A);
+ mode = _TokenizerMode.quote;
+ break;
+ case 0x72: // U+0072 LATIN SMALL LETTER R character
+ buffer.add(0x0D);
+ mode = _TokenizerMode.quote;
+ break;
+ case 0x74: // U+0074 LATIN SMALL LETTER T character
+ buffer.add(0x09);
+ mode = _TokenizerMode.quote;
+ break;
+ case 0x75: // U+0075 LATIN SMALL LETTER U character
+ assert(buffer2.isEmpty);
+ mode = _TokenizerMode.quoteEscapeUnicode1;
+ break;
+ default:
+ throw ParserException('Unexpected character ${_describeRune(current)} after backslash in string', line, column);
+ }
+ break;
+
+ case _TokenizerMode.quoteEscapeUnicode1:
+ switch (current) {
+ case -1:
+ throw ParserException('Unexpected end of file inside Unicode escape', line, column);
+ case 0x30: // U+0030 DIGIT ZERO character (0)
+ case 0x31: // U+0031 DIGIT ONE character (1)
+ case 0x32: // U+0032 DIGIT TWO character (2)
+ case 0x33: // U+0033 DIGIT THREE character (3)
+ case 0x34: // U+0034 DIGIT FOUR character (4)
+ case 0x35: // U+0035 DIGIT FIVE character (5)
+ case 0x36: // U+0036 DIGIT SIX character (6)
+ case 0x37: // U+0037 DIGIT SEVEN character (7)
+ case 0x38: // U+0038 DIGIT EIGHT character (8)
+ case 0x39: // U+0039 DIGIT NINE character (9)
+ case 0x41: // U+0041 LATIN CAPITAL LETTER A character
+ case 0x42: // U+0042 LATIN CAPITAL LETTER B character
+ case 0x43: // U+0043 LATIN CAPITAL LETTER C character
+ case 0x44: // U+0044 LATIN CAPITAL LETTER D character
+ case 0x45: // U+0045 LATIN CAPITAL LETTER E character
+ case 0x46: // U+0046 LATIN CAPITAL LETTER F character
+ case 0x61: // U+0061 LATIN SMALL LETTER A character
+ case 0x62: // U+0062 LATIN SMALL LETTER B character
+ case 0x63: // U+0063 LATIN SMALL LETTER C character
+ case 0x64: // U+0064 LATIN SMALL LETTER D character
+ case 0x65: // U+0065 LATIN SMALL LETTER E character
+ case 0x66: // U+0066 LATIN SMALL LETTER F character
+ buffer2.add(current);
+ mode = _TokenizerMode.quoteEscapeUnicode2;
+ break;
+ default:
+ throw ParserException('Unexpected character ${_describeRune(current)} in Unicode escape', line, column);
+ }
+ break;
+
+ case _TokenizerMode.quoteEscapeUnicode2:
+ switch (current) {
+ case -1:
+ throw ParserException('Unexpected end of file inside Unicode escape', line, column);
+ case 0x30: // U+0030 DIGIT ZERO character (0)
+ case 0x31: // U+0031 DIGIT ONE character (1)
+ case 0x32: // U+0032 DIGIT TWO character (2)
+ case 0x33: // U+0033 DIGIT THREE character (3)
+ case 0x34: // U+0034 DIGIT FOUR character (4)
+ case 0x35: // U+0035 DIGIT FIVE character (5)
+ case 0x36: // U+0036 DIGIT SIX character (6)
+ case 0x37: // U+0037 DIGIT SEVEN character (7)
+ case 0x38: // U+0038 DIGIT EIGHT character (8)
+ case 0x39: // U+0039 DIGIT NINE character (9)
+ case 0x41: // U+0041 LATIN CAPITAL LETTER A character
+ case 0x42: // U+0042 LATIN CAPITAL LETTER B character
+ case 0x43: // U+0043 LATIN CAPITAL LETTER C character
+ case 0x44: // U+0044 LATIN CAPITAL LETTER D character
+ case 0x45: // U+0045 LATIN CAPITAL LETTER E character
+ case 0x46: // U+0046 LATIN CAPITAL LETTER F character
+ case 0x61: // U+0061 LATIN SMALL LETTER A character
+ case 0x62: // U+0062 LATIN SMALL LETTER B character
+ case 0x63: // U+0063 LATIN SMALL LETTER C character
+ case 0x64: // U+0064 LATIN SMALL LETTER D character
+ case 0x65: // U+0065 LATIN SMALL LETTER E character
+ case 0x66: // U+0066 LATIN SMALL LETTER F character
+ buffer2.add(current);
+ mode = _TokenizerMode.quoteEscapeUnicode3;
+ break;
+ default:
+ throw ParserException('Unexpected character ${_describeRune(current)} in Unicode escape', line, column);
+ }
+ break;
+
+ case _TokenizerMode.quoteEscapeUnicode3:
+ switch (current) {
+ case -1:
+ throw ParserException('Unexpected end of file inside Unicode escape', line, column);
+ case 0x30: // U+0030 DIGIT ZERO character (0)
+ case 0x31: // U+0031 DIGIT ONE character (1)
+ case 0x32: // U+0032 DIGIT TWO character (2)
+ case 0x33: // U+0033 DIGIT THREE character (3)
+ case 0x34: // U+0034 DIGIT FOUR character (4)
+ case 0x35: // U+0035 DIGIT FIVE character (5)
+ case 0x36: // U+0036 DIGIT SIX character (6)
+ case 0x37: // U+0037 DIGIT SEVEN character (7)
+ case 0x38: // U+0038 DIGIT EIGHT character (8)
+ case 0x39: // U+0039 DIGIT NINE character (9)
+ case 0x41: // U+0041 LATIN CAPITAL LETTER A character
+ case 0x42: // U+0042 LATIN CAPITAL LETTER B character
+ case 0x43: // U+0043 LATIN CAPITAL LETTER C character
+ case 0x44: // U+0044 LATIN CAPITAL LETTER D character
+ case 0x45: // U+0045 LATIN CAPITAL LETTER E character
+ case 0x46: // U+0046 LATIN CAPITAL LETTER F character
+ case 0x61: // U+0061 LATIN SMALL LETTER A character
+ case 0x62: // U+0062 LATIN SMALL LETTER B character
+ case 0x63: // U+0063 LATIN SMALL LETTER C character
+ case 0x64: // U+0064 LATIN SMALL LETTER D character
+ case 0x65: // U+0065 LATIN SMALL LETTER E character
+ case 0x66: // U+0066 LATIN SMALL LETTER F character
+ buffer2.add(current);
+ mode = _TokenizerMode.quoteEscapeUnicode4;
+ break;
+ default:
+ throw ParserException('Unexpected character ${_describeRune(current)} in Unicode escape', line, column);
+ }
+ break;
+
+ case _TokenizerMode.quoteEscapeUnicode4:
+ switch (current) {
+ case -1:
+ throw ParserException('Unexpected end of file inside Unicode escape', line, column);
+ case 0x30: // U+0030 DIGIT ZERO character (0)
+ case 0x31: // U+0031 DIGIT ONE character (1)
+ case 0x32: // U+0032 DIGIT TWO character (2)
+ case 0x33: // U+0033 DIGIT THREE character (3)
+ case 0x34: // U+0034 DIGIT FOUR character (4)
+ case 0x35: // U+0035 DIGIT FIVE character (5)
+ case 0x36: // U+0036 DIGIT SIX character (6)
+ case 0x37: // U+0037 DIGIT SEVEN character (7)
+ case 0x38: // U+0038 DIGIT EIGHT character (8)
+ case 0x39: // U+0039 DIGIT NINE character (9)
+ case 0x41: // U+0041 LATIN CAPITAL LETTER A character
+ case 0x42: // U+0042 LATIN CAPITAL LETTER B character
+ case 0x43: // U+0043 LATIN CAPITAL LETTER C character
+ case 0x44: // U+0044 LATIN CAPITAL LETTER D character
+ case 0x45: // U+0045 LATIN CAPITAL LETTER E character
+ case 0x46: // U+0046 LATIN CAPITAL LETTER F character
+ case 0x61: // U+0061 LATIN SMALL LETTER A character
+ case 0x62: // U+0062 LATIN SMALL LETTER B character
+ case 0x63: // U+0063 LATIN SMALL LETTER C character
+ case 0x64: // U+0064 LATIN SMALL LETTER D character
+ case 0x65: // U+0065 LATIN SMALL LETTER E character
+ case 0x66: // U+0066 LATIN SMALL LETTER F character
+ buffer2.add(current);
+ buffer.add(int.parse(String.fromCharCodes(buffer2), radix: 16));
+ buffer2.clear();
+ mode = _TokenizerMode.quote;
+ break;
+ default:
+ throw ParserException('Unexpected character ${_describeRune(current)} in Unicode escape', line, column);
+ }
+ break;
+
+ case _TokenizerMode.endQuote:
+ switch (current) {
+ case -1:
+ yield _EofToken(line, column);
+ return;
+ case 0x0A: // U+000A LINE FEED (LF)
+ case 0x20: // U+0020 SPACE character
+ mode = _TokenizerMode.main;
+ break;
+ case 0x28: // U+0028 LEFT PARENTHESIS character (()
+ case 0x29: // U+0029 RIGHT PARENTHESIS character ())
+ case 0x2C: // U+002C COMMA character (,)
+ case 0x3A: // U+003A COLON character (:)
+ case 0x3B: // U+003B SEMICOLON character (;)
+ case 0x3D: // U+003D EQUALS SIGN character (=)
+ case 0x5B: // U+005B LEFT SQUARE BRACKET character ([)
+ case 0x5D: // U+005D RIGHT SQUARE BRACKET character (])
+ case 0x7B: // U+007B LEFT CURLY BRACKET character ({)
+ case 0x7D: // U+007D RIGHT CURLY BRACKET character (})
+ yield _SymbolToken(current, line, column);
+ mode = _TokenizerMode.main;
+ break;
+ case 0x2E: // U+002E FULL STOP character (.)
+ mode = _TokenizerMode.dot1;
+ break;
+ default:
+ throw ParserException('Unexpected character ${_describeRune(current)} after end quote', line, column);
+ }
+ break;
+
+ case _TokenizerMode.doubleQuote:
+ switch (current) {
+ case -1:
+ throw ParserException('Unexpected end of file inside string', line, column);
+ case 0x0A: // U+000A LINE FEED (LF)
+ throw ParserException('Unexpected end of line inside string', line, column);
+ case 0x22: // U+0022 QUOTATION MARK character (")
+ yield _StringToken(String.fromCharCodes(buffer), line, column);
+ buffer.clear();
+ mode = _TokenizerMode.endQuote;
+ break;
+ case 0x5C: // U+005C REVERSE SOLIDUS character (\)
+ mode = _TokenizerMode.doubleQuoteEscape;
+ break;
+ default:
+ buffer.add(current);
+ }
+ break;
+
+ case _TokenizerMode.doubleQuoteEscape:
+ switch (current) {
+ case -1:
+ throw ParserException('Unexpected end of file inside string', line, column);
+ case 0x22: // U+0022 QUOTATION MARK character (")
+ case 0x27: // U+0027 APOSTROPHE character (')
+ case 0x5C: // U+005C REVERSE SOLIDUS character (\)
+ case 0x2F: // U+002F SOLIDUS character (/)
+ buffer.add(current);
+ mode = _TokenizerMode.doubleQuote;
+ break;
+ case 0x62: // U+0062 LATIN SMALL LETTER B character
+ buffer.add(0x08);
+ mode = _TokenizerMode.doubleQuote;
+ break;
+ case 0x66: // U+0066 LATIN SMALL LETTER F character
+ buffer.add(0x0C);
+ mode = _TokenizerMode.doubleQuote;
+ break;
+ case 0x6E: // U+006E LATIN SMALL LETTER N character
+ buffer.add(0x0A);
+ mode = _TokenizerMode.doubleQuote;
+ break;
+ case 0x72: // U+0072 LATIN SMALL LETTER R character
+ buffer.add(0x0D);
+ mode = _TokenizerMode.doubleQuote;
+ break;
+ case 0x74: // U+0074 LATIN SMALL LETTER T character
+ buffer.add(0x09);
+ mode = _TokenizerMode.doubleQuote;
+ break;
+ case 0x75: // U+0075 LATIN SMALL LETTER U character
+ assert(buffer2.isEmpty);
+ mode = _TokenizerMode.doubleQuoteEscapeUnicode1;
+ break;
+ default:
+ throw ParserException('Unexpected character ${_describeRune(current)} after backslash in string', line, column);
+ }
+ break;
+
+ case _TokenizerMode.doubleQuoteEscapeUnicode1:
+ switch (current) {
+ case -1:
+ throw ParserException('Unexpected end of file inside Unicode escape', line, column);
+ case 0x30: // U+0030 DIGIT ZERO character (0)
+ case 0x31: // U+0031 DIGIT ONE character (1)
+ case 0x32: // U+0032 DIGIT TWO character (2)
+ case 0x33: // U+0033 DIGIT THREE character (3)
+ case 0x34: // U+0034 DIGIT FOUR character (4)
+ case 0x35: // U+0035 DIGIT FIVE character (5)
+ case 0x36: // U+0036 DIGIT SIX character (6)
+ case 0x37: // U+0037 DIGIT SEVEN character (7)
+ case 0x38: // U+0038 DIGIT EIGHT character (8)
+ case 0x39: // U+0039 DIGIT NINE character (9)
+ case 0x41: // U+0041 LATIN CAPITAL LETTER A character
+ case 0x42: // U+0042 LATIN CAPITAL LETTER B character
+ case 0x43: // U+0043 LATIN CAPITAL LETTER C character
+ case 0x44: // U+0044 LATIN CAPITAL LETTER D character
+ case 0x45: // U+0045 LATIN CAPITAL LETTER E character
+ case 0x46: // U+0046 LATIN CAPITAL LETTER F character
+ case 0x61: // U+0061 LATIN SMALL LETTER A character
+ case 0x62: // U+0062 LATIN SMALL LETTER B character
+ case 0x63: // U+0063 LATIN SMALL LETTER C character
+ case 0x64: // U+0064 LATIN SMALL LETTER D character
+ case 0x65: // U+0065 LATIN SMALL LETTER E character
+ case 0x66: // U+0066 LATIN SMALL LETTER F character
+ buffer2.add(current);
+ mode = _TokenizerMode.doubleQuoteEscapeUnicode2;
+ break;
+ default:
+ throw ParserException('Unexpected character ${_describeRune(current)} in Unicode escape', line, column);
+ }
+ break;
+
+ case _TokenizerMode.doubleQuoteEscapeUnicode2:
+ switch (current) {
+ case -1:
+ throw ParserException('Unexpected end of file inside Unicode escape', line, column);
+ case 0x30: // U+0030 DIGIT ZERO character (0)
+ case 0x31: // U+0031 DIGIT ONE character (1)
+ case 0x32: // U+0032 DIGIT TWO character (2)
+ case 0x33: // U+0033 DIGIT THREE character (3)
+ case 0x34: // U+0034 DIGIT FOUR character (4)
+ case 0x35: // U+0035 DIGIT FIVE character (5)
+ case 0x36: // U+0036 DIGIT SIX character (6)
+ case 0x37: // U+0037 DIGIT SEVEN character (7)
+ case 0x38: // U+0038 DIGIT EIGHT character (8)
+ case 0x39: // U+0039 DIGIT NINE character (9)
+ case 0x41: // U+0041 LATIN CAPITAL LETTER A character
+ case 0x42: // U+0042 LATIN CAPITAL LETTER B character
+ case 0x43: // U+0043 LATIN CAPITAL LETTER C character
+ case 0x44: // U+0044 LATIN CAPITAL LETTER D character
+ case 0x45: // U+0045 LATIN CAPITAL LETTER E character
+ case 0x46: // U+0046 LATIN CAPITAL LETTER F character
+ case 0x61: // U+0061 LATIN SMALL LETTER A character
+ case 0x62: // U+0062 LATIN SMALL LETTER B character
+ case 0x63: // U+0063 LATIN SMALL LETTER C character
+ case 0x64: // U+0064 LATIN SMALL LETTER D character
+ case 0x65: // U+0065 LATIN SMALL LETTER E character
+ case 0x66: // U+0066 LATIN SMALL LETTER F character
+ buffer2.add(current);
+ mode = _TokenizerMode.doubleQuoteEscapeUnicode3;
+ break;
+ default:
+ throw ParserException('Unexpected character ${_describeRune(current)} in Unicode escape', line, column);
+ }
+ break;
+
+ case _TokenizerMode.doubleQuoteEscapeUnicode3:
+ switch (current) {
+ case -1:
+ throw ParserException('Unexpected end of file inside Unicode escape', line, column);
+ case 0x30: // U+0030 DIGIT ZERO character (0)
+ case 0x31: // U+0031 DIGIT ONE character (1)
+ case 0x32: // U+0032 DIGIT TWO character (2)
+ case 0x33: // U+0033 DIGIT THREE character (3)
+ case 0x34: // U+0034 DIGIT FOUR character (4)
+ case 0x35: // U+0035 DIGIT FIVE character (5)
+ case 0x36: // U+0036 DIGIT SIX character (6)
+ case 0x37: // U+0037 DIGIT SEVEN character (7)
+ case 0x38: // U+0038 DIGIT EIGHT character (8)
+ case 0x39: // U+0039 DIGIT NINE character (9)
+ case 0x41: // U+0041 LATIN CAPITAL LETTER A character
+ case 0x42: // U+0042 LATIN CAPITAL LETTER B character
+ case 0x43: // U+0043 LATIN CAPITAL LETTER C character
+ case 0x44: // U+0044 LATIN CAPITAL LETTER D character
+ case 0x45: // U+0045 LATIN CAPITAL LETTER E character
+ case 0x46: // U+0046 LATIN CAPITAL LETTER F character
+ case 0x61: // U+0061 LATIN SMALL LETTER A character
+ case 0x62: // U+0062 LATIN SMALL LETTER B character
+ case 0x63: // U+0063 LATIN SMALL LETTER C character
+ case 0x64: // U+0064 LATIN SMALL LETTER D character
+ case 0x65: // U+0065 LATIN SMALL LETTER E character
+ case 0x66: // U+0066 LATIN SMALL LETTER F character
+ buffer2.add(current);
+ mode = _TokenizerMode.doubleQuoteEscapeUnicode4;
+ break;
+ default:
+ throw ParserException('Unexpected character ${_describeRune(current)} in Unicode escape', line, column);
+ }
+ break;
+
+ case _TokenizerMode.doubleQuoteEscapeUnicode4:
+ switch (current) {
+ case -1:
+ throw ParserException('Unexpected end of file inside Unicode escape', line, column);
+ case 0x30: // U+0030 DIGIT ZERO character (0)
+ case 0x31: // U+0031 DIGIT ONE character (1)
+ case 0x32: // U+0032 DIGIT TWO character (2)
+ case 0x33: // U+0033 DIGIT THREE character (3)
+ case 0x34: // U+0034 DIGIT FOUR character (4)
+ case 0x35: // U+0035 DIGIT FIVE character (5)
+ case 0x36: // U+0036 DIGIT SIX character (6)
+ case 0x37: // U+0037 DIGIT SEVEN character (7)
+ case 0x38: // U+0038 DIGIT EIGHT character (8)
+ case 0x39: // U+0039 DIGIT NINE character (9)
+ case 0x41: // U+0041 LATIN CAPITAL LETTER A character
+ case 0x42: // U+0042 LATIN CAPITAL LETTER B character
+ case 0x43: // U+0043 LATIN CAPITAL LETTER C character
+ case 0x44: // U+0044 LATIN CAPITAL LETTER D character
+ case 0x45: // U+0045 LATIN CAPITAL LETTER E character
+ case 0x46: // U+0046 LATIN CAPITAL LETTER F character
+ case 0x61: // U+0061 LATIN SMALL LETTER A character
+ case 0x62: // U+0062 LATIN SMALL LETTER B character
+ case 0x63: // U+0063 LATIN SMALL LETTER C character
+ case 0x64: // U+0064 LATIN SMALL LETTER D character
+ case 0x65: // U+0065 LATIN SMALL LETTER E character
+ case 0x66: // U+0066 LATIN SMALL LETTER F character
+ buffer2.add(current);
+ buffer.add(int.parse(String.fromCharCodes(buffer2), radix: 16));
+ buffer2.clear();
+ mode = _TokenizerMode.doubleQuote;
+ break;
+ default:
+ throw ParserException('Unexpected character ${_describeRune(current)} in Unicode escape', line, column);
+ }
+ break;
+
+ case _TokenizerMode.endDoubleQuote:
+ switch (current) {
+ case -1:
+ yield _EofToken(line, column);
+ return;
+ case 0x0A: // U+000A LINE FEED (LF)
+ case 0x20: // U+0020 SPACE character
+ mode = _TokenizerMode.main;
+ break;
+ case 0x28: // U+0028 LEFT PARENTHESIS character (()
+ case 0x29: // U+0029 RIGHT PARENTHESIS character ())
+ case 0x2C: // U+002C COMMA character (,)
+ case 0x3A: // U+003A COLON character (:)
+ case 0x3B: // U+003B SEMICOLON character (;)
+ case 0x3D: // U+003D EQUALS SIGN character (=)
+ case 0x5B: // U+005B LEFT SQUARE BRACKET character ([)
+ case 0x5D: // U+005D RIGHT SQUARE BRACKET character (])
+ case 0x7B: // U+007B LEFT CURLY BRACKET character ({)
+ case 0x7D: // U+007D RIGHT CURLY BRACKET character (})
+ yield _SymbolToken(current, line, column);
+ mode = _TokenizerMode.main;
+ break;
+ case 0x2E: // U+002E FULL STOP character (.)
+ mode = _TokenizerMode.dot1;
+ break;
+ default:
+ throw ParserException('Unexpected character ${_describeRune(current)} after end doublequote', line, column);
+ }
+ break;
+
+ case _TokenizerMode.slash:
+ switch (current) {
+ case -1:
+ throw ParserException('Unexpected end of file inside comment delimiter', line, column);
+ case 0x2F: // U+002F SOLIDUS character (/)
+ mode = _TokenizerMode.comment;
+ break;
+ default:
+ throw ParserException('Unexpected character ${_describeRune(current)} inside comment delimiter', line, column);
+ }
+ break;
+
+ case _TokenizerMode.comment:
+ switch (current) {
+ case -1:
+ yield _EofToken(line, column);
+ return;
+ case 0x0A: // U+000A LINE FEED (LF)
+ mode = _TokenizerMode.main;
+ break;
+ default:
+ // ignored, comment
+ break;
+ }
+ break;
+ }
+ index += 1;
+ }
+}
+
+/// API for parsing Remote Flutter Widgets text files.
+///
+/// Text data files can be parsed by using [readDataFile]. (Unlike the binary
+/// variant of this format, the root of a text data file is always a map.)
+///
+/// Test library files can be parsed by using [readLibraryFile].
+class _Parser {
+ _Parser(Iterable<_Token> source) : _source = source.iterator..moveNext();
+
+ final Iterator<_Token> _source;
+
+ void _advance() {
+ assert(_source.current is! _EofToken);
+ final bool advanced = _source.moveNext();
+ assert(advanced == true); // see https://github.com/dart-lang/sdk/issues/47017
+ }
+
+ bool _foundIdentifier(String identifier) {
+ return (_source.current is _IdentifierToken)
+ && ((_source.current as _IdentifierToken).value == identifier);
+ }
+
+ void _expectIdentifier(String value) {
+ if (_source.current is! _IdentifierToken) {
+ throw ParserException._expected('identifier', _source.current);
+ }
+ if ((_source.current as _IdentifierToken).value != value) {
+ throw ParserException._expected(value, _source.current);
+ }
+ _advance();
+ }
+
+ String _readIdentifier() {
+ if (_source.current is! _IdentifierToken) {
+ throw ParserException._expected('identifier', _source.current);
+ }
+ final String result = (_source.current as _IdentifierToken).value;
+ _advance();
+ return result;
+ }
+
+ String _readString() {
+ if (_source.current is! _StringToken) {
+ throw ParserException._expected('string', _source.current);
+ }
+ final String result = (_source.current as _StringToken).value;
+ _advance();
+ return result;
+ }
+
+ bool _foundSymbol(int symbol) {
+ return (_source.current is _SymbolToken)
+ && ((_source.current as _SymbolToken).symbol == symbol);
+ }
+
+ bool _maybeReadSymbol(int symbol) {
+ if (_foundSymbol(symbol)) {
+ _advance();
+ return true;
+ }
+ return false;
+ }
+
+ void _expectSymbol(int symbol) {
+ if (_source.current is! _SymbolToken) {
+ throw ParserException._expected('symbol "${String.fromCharCode(symbol)}"', _source.current);
+ }
+ if ((_source.current as _SymbolToken).symbol != symbol) {
+ throw ParserException._expected('symbol "${String.fromCharCode(symbol)}"', _source.current);
+ }
+ _advance();
+ }
+
+ String _readKey() {
+ if (_source.current is _IdentifierToken) {
+ return _readIdentifier();
+ }
+ return _readString();
+ }
+
+ DynamicMap _readMap({ required bool extended }) {
+ _expectSymbol(_SymbolToken.openBrace);
+ final DynamicMap results = _readMapBody(extended: extended);
+ _expectSymbol(_SymbolToken.closeBrace);
+ return results;
+ }
+
+ DynamicMap _readMapBody({ required bool extended }) {
+ final DynamicMap results = DynamicMap(); // ignore: prefer_collection_literals
+ while (_source.current is! _SymbolToken) {
+ final String key = _readKey();
+ if (results.containsKey(key)) {
+ throw ParserException._fromToken('Duplicate key "$key" in map', _source.current);
+ }
+ _expectSymbol(_SymbolToken.colon);
+ final Object value = _readValue(extended: extended, nullOk: true);
+ if (value != missing) {
+ results[key] = value;
+ }
+ if (_foundSymbol(_SymbolToken.comma)) {
+ _advance();
+ } else {
+ break;
+ }
+ }
+ return results;
+ }
+
+ final List<String> _loopIdentifiers = <String>[];
+
+ DynamicList _readList({ required bool extended }) {
+ final DynamicList results = DynamicList.empty(growable: true);
+ _expectSymbol(_SymbolToken.openBracket);
+ while (!_foundSymbol(_SymbolToken.closeBracket)) {
+ if (extended && _foundSymbol(_SymbolToken.tripleDot)) {
+ _advance();
+ _expectIdentifier('for');
+ final _Token loopIdentifierToken = _source.current;
+ final String loopIdentifier = _readIdentifier();
+ if (_reservedWords.contains(loopIdentifier)) {
+ throw ParserException._fromToken('$loopIdentifier is a reserved word', loopIdentifierToken);
+ }
+ _expectIdentifier('in');
+ final Object collection = _readValue(extended: true);
+ _expectSymbol(_SymbolToken.colon);
+ _loopIdentifiers.add(loopIdentifier);
+ final Object template = _readValue(extended: extended);
+ assert(_loopIdentifiers.last == loopIdentifier);
+ _loopIdentifiers.removeLast();
+ results.add(Loop(collection, template));
+ } else {
+ final Object value = _readValue(extended: extended);
+ results.add(value);
+ }
+ if (_foundSymbol(_SymbolToken.comma)) {
+ _advance();
+ } else if (!_foundSymbol(_SymbolToken.closeBracket)) {
+ throw ParserException._expected('comma', _source.current);
+ }
+ }
+ _expectSymbol(_SymbolToken.closeBracket);
+ return results;
+ }
+
+ Switch _readSwitch() {
+ final Object value = _readValue(extended: true);
+ final Map<Object?, Object> cases = <Object?, Object>{};
+ _expectSymbol(_SymbolToken.openBrace);
+ while (_source.current is! _SymbolToken) {
+ final Object? key;
+ if (_foundIdentifier('default')) {
+ if (cases.containsKey(null)) {
+ throw ParserException._fromToken('Switch has multiple default cases', _source.current);
+ }
+ key = null;
+ _advance();
+ } else {
+ key = _readValue(extended: true);
+ if (cases.containsKey(key)) {
+ throw ParserException._fromToken('Switch has duplicate cases for key $key', _source.current);
+ }
+ }
+ _expectSymbol(_SymbolToken.colon);
+ final Object value = _readValue(extended: true);
+ cases[key] = value;
+ if (_foundSymbol(_SymbolToken.comma)) {
+ _advance();
+ } else {
+ break;
+ }
+ }
+ _expectSymbol(_SymbolToken.closeBrace);
+ return Switch(value, cases);
+ }
+
+ List<Object> _readParts({ bool optional = false }) {
+ if (optional && !_foundSymbol(_SymbolToken.dot)) {
+ return const <Object>[];
+ }
+ final List<Object> results = <Object>[];
+ do {
+ _expectSymbol(_SymbolToken.dot);
+ if (_source.current is _IntegerToken) {
+ results.add((_source.current as _IntegerToken).value);
+ } else if (_source.current is _StringToken) {
+ results.add((_source.current as _StringToken).value);
+ } else if (_source.current is _IdentifierToken) {
+ results.add((_source.current as _IdentifierToken).value);
+ } else {
+ throw ParserException._unexpected(_source.current);
+ }
+ _advance();
+ } while (_foundSymbol(_SymbolToken.dot));
+ return results;
+ }
+
+ Object _readValue({ required bool extended, bool nullOk = false }) {
+ if (_source.current is _SymbolToken) {
+ switch ((_source.current as _SymbolToken).symbol) {
+ case _SymbolToken.openBracket:
+ return _readList(extended: extended);
+ case _SymbolToken.openBrace:
+ return _readMap(extended: extended);
+ }
+ } else if (_source.current is _IntegerToken) {
+ final Object result = (_source.current as _IntegerToken).value;
+ _advance();
+ return result;
+ } else if (_source.current is _DoubleToken) {
+ final Object result = (_source.current as _DoubleToken).value;
+ _advance();
+ return result;
+ } else if (_source.current is _StringToken) {
+ final Object result = (_source.current as _StringToken).value;
+ _advance();
+ return result;
+ } else if (_source.current is _IdentifierToken) {
+ final String identifier = (_source.current as _IdentifierToken).value;
+ if (identifier == 'true') {
+ _advance();
+ return true;
+ }
+ if (identifier == 'false') {
+ _advance();
+ return false;
+ }
+ if (identifier == 'null' && nullOk) {
+ _advance();
+ return missing;
+ }
+ if (!extended) {
+ throw ParserException._unexpected(_source.current);
+ }
+ if (identifier == 'event') {
+ _advance();
+ return EventHandler(_readString(), _readMap(extended: true));
+ }
+ if (identifier == 'args') {
+ _advance();
+ return ArgsReference(_readParts());
+ }
+ if (identifier == 'data') {
+ _advance();
+ return DataReference(_readParts());
+ }
+ if (identifier == 'state') {
+ _advance();
+ return StateReference(_readParts());
+ }
+ if (identifier == 'switch') {
+ _advance();
+ return _readSwitch();
+ }
+ if (identifier == 'set') {
+ _advance();
+ _expectIdentifier('state');
+ final StateReference stateReference = StateReference(_readParts());
+ _expectSymbol(_SymbolToken.equals);
+ final Object value = _readValue(extended: true);
+ return SetStateHandler(stateReference, value);
+ }
+ final int index = _loopIdentifiers.lastIndexOf(identifier) + 1;
+ if (index > 0) {
+ _advance();
+ return LoopReference(_loopIdentifiers.length - index, _readParts(optional: true));
+ }
+ return _readConstructorCall();
+ }
+ throw ParserException._unexpected(_source.current);
+ }
+
+ ConstructorCall _readConstructorCall() {
+ final String name = _readIdentifier();
+ _expectSymbol(_SymbolToken.openParen);
+ final DynamicMap arguments = _readMapBody(extended: true);
+ _expectSymbol(_SymbolToken.closeParen);
+ return ConstructorCall(name, arguments);
+ }
+
+ WidgetDeclaration _readWidgetDeclaration() {
+ _expectIdentifier('widget');
+ final String name = _readIdentifier();
+ DynamicMap? initialState;
+ if (_foundSymbol(_SymbolToken.openBrace)) {
+ initialState = _readMap(extended: false);
+ }
+ _expectSymbol(_SymbolToken.equals);
+ final BlobNode root;
+ if (_foundIdentifier('switch')) {
+ _advance();
+ root = _readSwitch();
+ } else {
+ root = _readConstructorCall();
+ }
+ _expectSymbol(_SymbolToken.semicolon);
+ return WidgetDeclaration(name, initialState, root);
+ }
+
+ Iterable<WidgetDeclaration> _readWidgetDeclarations() sync* {
+ while (_foundIdentifier('widget')) {
+ yield _readWidgetDeclaration();
+ }
+ }
+
+ Import _readImport() {
+ _expectIdentifier('import');
+ final List<String> parts = <String>[];
+ do {
+ parts.add(_readKey());
+ } while (_maybeReadSymbol(_SymbolToken.dot));
+ _expectSymbol(_SymbolToken.semicolon);
+ return Import(LibraryName(parts));
+ }
+
+ Iterable<Import> _readImports() sync* {
+ while (_foundIdentifier('import')) {
+ yield _readImport();
+ }
+ }
+
+ DynamicMap readDataFile() {
+ final DynamicMap result = _readMap(extended: false);
+ _expectEof('end of file');
+ return result;
+ }
+
+ RemoteWidgetLibrary readLibraryFile() {
+ final RemoteWidgetLibrary result = RemoteWidgetLibrary(
+ _readImports().toList(),
+ _readWidgetDeclarations().toList(),
+ );
+ if (result.widgets.isEmpty) {
+ _expectEof('keywords "import" or "widget", or end of file');
+ } else {
+ _expectEof('keyword "widget" or end of file');
+ }
+ return result;
+ }
+
+ void _expectEof(String expectation) {
+ if (_source.current is! _EofToken) {
+ throw ParserException._expected(expectation, _source.current);
+ }
+ final bool more = _source.moveNext();
+ assert(more == false); // see https://github.com/dart-lang/sdk/issues/47017
+ }
+}
diff --git a/packages/rfw/lib/flutter/argument_decoders.dart b/packages/rfw/lib/flutter/argument_decoders.dart
new file mode 100644
index 0000000..8554413
--- /dev/null
+++ b/packages/rfw/lib/flutter/argument_decoders.dart
@@ -0,0 +1,1422 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// There's a lot of <Object>[] lists in this file so to avoid making this
+// file even less readable we relax our usual stance on verbose typing.
+// ignore_for_file: always_specify_types
+
+// This file is hand-formatted.
+
+import 'dart:math' as math show pi;
+import 'dart:ui' show FontFeature; // TODO(ianh): https://github.com/flutter/flutter/issues/87235
+
+import 'package:flutter/material.dart';
+
+import 'runtime.dart';
+
+/// Default duration and curve for animations in remote flutter widgets.
+///
+/// This inherited widget allows a duration and a curve (defaulting to 200ms and
+/// [Curves.fastOutSlowIn]) to be set as the default to use when local widgets
+/// use the [ArgumentsDecoder.curve] and [ArgumentsDecoder.duration] methods and
+/// find that the [DataSource] has no explicit curve or duration.
+class AnimationDefaults extends InheritedWidget {
+ /// Configures an [AnimanionDefaults] widget.
+ ///
+ /// The [duration] and [curve] are optional, and default to 200ms and
+ /// [Curves.fastOutSlowIn] respectively.
+ const AnimationDefaults({
+ Key? key,
+ this.duration,
+ this.curve,
+ required Widget child,
+ }) : super(key: key, child: child);
+
+ /// The default duration that [ArgumentsDecoder.duration] should use.
+ ///
+ /// Defaults to 200ms when this is null.
+ final Duration? duration;
+
+ /// The default curve that [ArgumentsDecoder.curve] should use.
+ ///
+ /// Defaults to [Curves.fastOutSlowIn] when this is null.
+ final Curve? curve;
+
+ /// Return the ambient [AnimationDefaults.duration], or 200ms if there is no
+ /// ambient [AnimationDefaults] or if the nearest [AnimationDefaults] has a
+ /// null [duration].
+ static Duration durationOf(BuildContext context) {
+ return context.dependOnInheritedWidgetOfExactType<AnimationDefaults>()?.duration ?? const Duration(milliseconds: 200);
+ }
+
+ /// Return the ambient [AnimationDefaults.curve], or [Curves.fastOutSlowIn] if
+ /// there is no ambient [AnimationDefaults] or if the nearest
+ /// [AnimationDefaults] has a null [curve].
+ static Curve curveOf(BuildContext context) {
+ return context.dependOnInheritedWidgetOfExactType<AnimationDefaults>()?.curve ?? Curves.fastOutSlowIn;
+ }
+
+ @override
+ bool updateShouldNotify(AnimationDefaults oldWidget) => duration != oldWidget.duration || curve != oldWidget.curve;
+}
+
+/// Signature for methods that decode structured values from a [DataSource],
+/// such as the static methods of [ArgumentDecoders].
+///
+/// Used to make some of the methods of that class extensible.
+typedef ArgumentDecoder<T> = T Function(DataSource source, List<Object> key);
+
+/// A set of methods for decoding structured values from a [DataSource].
+///
+/// Specifically, these methods decode types that are used by local widgets
+/// (q.v. [createCoreWidgets]).
+///
+/// These methods take a [DataSource] and a `key`. The `key` is a path to the
+/// part of the [DataSource] that the value should be read from. This may
+/// identify a map, a list, or a leaf value, depending on the needs of the
+/// method.
+class ArgumentDecoders {
+ const ArgumentDecoders._();
+
+ /// This is a workaround for https://github.com/dart-lang/sdk/issues/47021
+ static const ArgumentDecoders __ = ArgumentDecoders._(); // ignore: unused_field
+
+ // (in alphabetical order)
+
+ /// Decodes an [AlignmentDirectional] or [Alignment] object out of the
+ /// specified map.
+ ///
+ /// If the map has `start` and `y` keys, then it is interpreted as an
+ /// [AlignmentDirectional] with those values. Otherwise if it has `x` and `y`
+ /// it's an [Alignment] with those values. Otherwise it returns null.
+ static AlignmentGeometry? alignment(DataSource source, List<Object> key) {
+ if (!source.isMap(key)) {
+ return null;
+ }
+ final double? x = source.v<double>([...key, 'x']);
+ final double? start = source.v<double>([...key, 'start']);
+ final double? y = source.v<double>([...key, 'y']);
+ if (x == null && start == null) {
+ return null;
+ }
+ if (y == null) {
+ return null;
+ }
+ if (start != null) {
+ return AlignmentDirectional(start, y);
+ }
+ x!;
+ return Alignment(x, y);
+ }
+
+ /// Decodes the specified map into a [BoxConstraints].
+ ///
+ /// The keys used are `minWidth`, `maxWidth`, `minHeight`, and `maxHeight`.
+ /// Omitted keys are defaulted to 0.0 for minimums and infinity for maximums.
+ static BoxConstraints? boxConstraints(DataSource source, List<Object> key) {
+ if (!source.isMap(key)) {
+ return null;
+ }
+ return BoxConstraints(
+ minWidth: source.v<double>([...key, 'minWidth']) ?? 0.0,
+ maxWidth: source.v<double>([...key, 'maxWidth']) ?? double.infinity,
+ minHeight: source.v<double>([...key, 'minHeight']) ?? 0.0,
+ maxHeight: source.v<double>([...key, 'maxHeight']) ?? double.infinity,
+ );
+ }
+
+ /// Returns a [BorderDirectional] from the specified list.
+ ///
+ /// The list is a list of values as interpreted by [borderSide]. An empty or
+ /// missing list results in a null return value. The list should have one
+ /// through four items. Extra items are ignored.
+ ///
+ /// The values are interpreted as follows:
+ ///
+ /// * start: first value.
+ /// * top: second value, defaulting to same as start.
+ /// * end: third value, defaulting to same as start.
+ /// * bottom: fourth value, defaulting to same as top.
+ static BoxBorder? border(DataSource source, List<Object> key) {
+ final BorderSide? a = borderSide(source, [...key, 0]);
+ if (a == null) {
+ return null;
+ }
+ final BorderSide? b = borderSide(source, [...key, 1]);
+ final BorderSide? c = borderSide(source, [...key, 2]);
+ final BorderSide? d = borderSide(source, [...key, 3]);
+ return BorderDirectional(
+ start: a,
+ top: b ?? a,
+ end: c ?? a,
+ bottom: d ?? b ?? a,
+ );
+ }
+
+ /// Returns a [BorderRadiusDirectional] from the specified list.
+ ///
+ /// The list is a list of values as interpreted by [radius]. An empty or
+ /// missing list results in a null return value. The list should have one
+ /// through four items. Extra items are ignored.
+ ///
+ /// The values are interpreted as follows:
+ ///
+ /// * topStart: first value.
+ /// * topEnd: second value, defaulting to same as topStart.
+ /// * bottomStart: third value, defaulting to same as topStart.
+ /// * bottomEnd: fourth value, defaulting to same as topEnd.
+ static BorderRadiusGeometry? borderRadius(DataSource source, List<Object> key) {
+ final Radius? a = radius(source, [...key, 0]);
+ if (a == null) {
+ return null;
+ }
+ final Radius? b = radius(source, [...key, 1]);
+ final Radius? c = radius(source, [...key, 2]);
+ final Radius? d = radius(source, [...key, 3]);
+ return BorderRadiusDirectional.only(
+ topStart: a,
+ topEnd: b ?? a,
+ bottomStart: c ?? a,
+ bottomEnd: d ?? b ?? a,
+ );
+ }
+
+ /// Returns a [BorderSide] from the specified map.
+ ///
+ /// If the map is absent, returns null.
+ ///
+ /// Otherwise (even if it has no keys), the [BorderSide] is created from the
+ /// keys `color` (see [color], defaults to black), `width` (a double, defaults
+ /// to 1.0), and `style` (see [enumValue] for [BorderStyle], defaults to
+ /// [BorderStyle.solid]).
+ static BorderSide? borderSide(DataSource source, List<Object> key) {
+ if (!source.isMap(key)) {
+ return null;
+ }
+ return BorderSide(
+ color: color(source, [...key, 'color']) ?? const Color(0xFF000000),
+ width: source.v<double>([...key, 'width']) ?? 1.0,
+ style: enumValue<BorderStyle>(BorderStyle.values, source, [...key, 'style']) ?? BorderStyle.solid,
+ );
+ }
+
+ /// Returns a [BoxShadow] from the specified map.
+ ///
+ /// If the map is absent, returns null.
+ ///
+ /// Otherwise (even if it has no keys), the [BoxShadow] is created from the
+ /// keys `color` (see [color], defaults to black), `offset` (see [offset],
+ /// defaults to [Offset.zero]), `blurRadius` (double, defaults to zero), and
+ /// `spreadRadius` (double, defaults to zero).
+ static BoxShadow boxShadow(DataSource source, List<Object> key) {
+ if (!source.isMap(key)) {
+ return const BoxShadow();
+ }
+ return BoxShadow(
+ color: color(source, [...key, 'color']) ?? const Color(0xFF000000),
+ offset: offset(source, [...key, 'offset']) ?? Offset.zero,
+ blurRadius: source.v<double>([...key, 'blurRadius']) ?? 0.0,
+ spreadRadius: source.v<double>([...key, 'spreadRadius']) ?? 0.0,
+ );
+ }
+
+ /// Returns a [Color] from the specified integer.
+ ///
+ /// Returns null if it's not an integer; otherwise, passes it to the [new
+ /// Color] constructor.
+ static Color? color(DataSource source, List<Object> key) {
+ final int? value = source.v<int>(key);
+ if (value == null) {
+ return null;
+ }
+ return Color(value);
+ }
+
+ /// Returns a [ColorFilter] from the specified map.
+ ///
+ /// The `type` key specifies the kind of filter.
+ ///
+ /// A type of `linearToSrgbGamma` creates a [ColorFilter.linearToSrgbGamma].
+ ///
+ /// A type of `matrix` creates a [ColorFilter.matrix], parsing the `matrix`
+ /// key as per [colorMatrix]). If there is no `matrix` key, returns null.
+ ///
+ /// A type of `mode` creates a [ColorFilter.mode], using the `color` key
+ /// (see[color], defaults to black) and the `blendMode` key (see [enumValue] for
+ /// [BlendMdoe], defaults to [BlendMode.srcOver])
+ ///
+ /// A type of `srgbToLinearGamma` creates a [ColorFilter.srgbToLinearGamma].
+ ///
+ /// If the type is none of these, but is not null, then the type is looked up
+ /// in [colorFilterDecoders], and if an entry is found, this method defers to
+ /// that callback.
+ ///
+ /// Otherwise, returns null.
+ static ColorFilter? colorFilter(DataSource source, List<Object> key) {
+ final String? type = source.v<String>([...key, 'type']);
+ switch (type) {
+ case null:
+ return null;
+ case 'linearToSrgbGamma':
+ return const ColorFilter.linearToSrgbGamma();
+ case 'matrix':
+ final List<double>? matrix = colorMatrix(source, [...key, 'matrix']);
+ if (matrix == null) {
+ return null;
+ }
+ return ColorFilter.matrix(matrix);
+ case 'mode':
+ return ColorFilter.mode(
+ color(source, [...key, 'color']) ?? const Color(0xFF000000),
+ enumValue<BlendMode>(BlendMode.values, source, [...key, 'blendMode']) ?? BlendMode.srcOver,
+ );
+ case 'srgbToLinearGamma':
+ return const ColorFilter.srgbToLinearGamma();
+ default:
+ final ArgumentDecoder<ColorFilter?>? decoder = colorFilterDecoders[type];
+ if (decoder == null) {
+ return null;
+ }
+ return decoder(source, key);
+ }
+ }
+
+ /// Extension mechanism for [colorFilter].
+ static final Map<String, ArgumentDecoder<ColorFilter?>> colorFilterDecoders = <String, ArgumentDecoder<ColorFilter?>>{};
+
+ /// Returns a list of 20 doubles from the specified list.
+ ///
+ /// If the specified key does not identify a list, returns null instead.
+ ///
+ /// If the list has fewer than 20 entries or if any of the entries are not
+ /// doubles, any entries that could not be obtained are replaced by zero.
+ ///
+ /// Used by [colorFilter] in the `matrix` mode.
+ static List<double>? colorMatrix(DataSource source, List<Object> key) {
+ if (!source.isList(key)) {
+ return null;
+ }
+ return <double>[
+ source.v<double>([...key, 0]) ?? 0.0,
+ source.v<double>([...key, 1]) ?? 0.0,
+ source.v<double>([...key, 2]) ?? 0.0,
+ source.v<double>([...key, 3]) ?? 0.0,
+ source.v<double>([...key, 4]) ?? 0.0,
+ source.v<double>([...key, 5]) ?? 0.0,
+ source.v<double>([...key, 6]) ?? 0.0,
+ source.v<double>([...key, 7]) ?? 0.0,
+ source.v<double>([...key, 8]) ?? 0.0,
+ source.v<double>([...key, 9]) ?? 0.0,
+ source.v<double>([...key, 10]) ?? 0.0,
+ source.v<double>([...key, 11]) ?? 0.0,
+ source.v<double>([...key, 12]) ?? 0.0,
+ source.v<double>([...key, 13]) ?? 0.0,
+ source.v<double>([...key, 14]) ?? 0.0,
+ source.v<double>([...key, 15]) ?? 0.0,
+ source.v<double>([...key, 16]) ?? 0.0,
+ source.v<double>([...key, 17]) ?? 0.0,
+ source.v<double>([...key, 18]) ?? 0.0,
+ source.v<double>([...key, 19]) ?? 0.0,
+ ];
+ }
+
+ /// Returns a [Color] from the specified integer.
+ ///
+ /// Returns black if it's not an integer; otherwise, passes it to the [new
+ /// Color] constructor.
+ ///
+ /// This is useful in situations where null is not acceptable, for example,
+ /// when providing a decoder to [list]. Otherwise, prefer using [DataSource.v]
+ /// directly.
+ static Color colorOrBlack(DataSource source, List<Object> key) {
+ return color(source, key) ?? const Color(0xFF000000);
+ }
+
+ /// Returns a [Curve] from the specified string.
+ ///
+ /// The given key should specify a string. If that string matches one of the
+ /// names of static curves defined in the [Curves] class, then that curve is
+ /// returned. Otherwise, if the string was not null, and is present as a key
+ /// in the [curveDecoders] map, then the matching decoder from that map is
+ /// invoked. Otherwise, the default obtained from [AnimationDefaults.curveOf]
+ /// is used (which is why a [BuildContext] is required).
+ static Curve curve(DataSource source, List<Object> key, BuildContext context) {
+ final String? type = source.v<String>(key);
+ switch (type) {
+ case 'linear':
+ return Curves.linear;
+ case 'decelerate':
+ return Curves.decelerate;
+ case 'fastLinearToSlowEaseIn':
+ return Curves.fastLinearToSlowEaseIn;
+ case 'ease':
+ return Curves.ease;
+ case 'easeIn':
+ return Curves.easeIn;
+ case 'easeInToLinear':
+ return Curves.easeInToLinear;
+ case 'easeInSine':
+ return Curves.easeInSine;
+ case 'easeInQuad':
+ return Curves.easeInQuad;
+ case 'easeInCubic':
+ return Curves.easeInCubic;
+ case 'easeInQuart':
+ return Curves.easeInQuart;
+ case 'easeInQuint':
+ return Curves.easeInQuint;
+ case 'easeInExpo':
+ return Curves.easeInExpo;
+ case 'easeInCirc':
+ return Curves.easeInCirc;
+ case 'easeInBack':
+ return Curves.easeInBack;
+ case 'easeOut':
+ return Curves.easeOut;
+ case 'linearToEaseOut':
+ return Curves.linearToEaseOut;
+ case 'easeOutSine':
+ return Curves.easeOutSine;
+ case 'easeOutQuad':
+ return Curves.easeOutQuad;
+ case 'easeOutCubic':
+ return Curves.easeOutCubic;
+ case 'easeOutQuart':
+ return Curves.easeOutQuart;
+ case 'easeOutQuint':
+ return Curves.easeOutQuint;
+ case 'easeOutExpo':
+ return Curves.easeOutExpo;
+ case 'easeOutCirc':
+ return Curves.easeOutCirc;
+ case 'easeOutBack':
+ return Curves.easeOutBack;
+ case 'easeInOut':
+ return Curves.easeInOut;
+ case 'easeInOutSine':
+ return Curves.easeInOutSine;
+ case 'easeInOutQuad':
+ return Curves.easeInOutQuad;
+ case 'easeInOutCubic':
+ return Curves.easeInOutCubic;
+ case 'easeInOutCubicEmphasized':
+ return Curves.easeInOutCubicEmphasized;
+ case 'easeInOutQuart':
+ return Curves.easeInOutQuart;
+ case 'easeInOutQuint':
+ return Curves.easeInOutQuint;
+ case 'easeInOutExpo':
+ return Curves.easeInOutExpo;
+ case 'easeInOutCirc':
+ return Curves.easeInOutCirc;
+ case 'easeInOutBack':
+ return Curves.easeInOutBack;
+ case 'fastOutSlowIn':
+ return Curves.fastOutSlowIn;
+ case 'slowMiddle':
+ return Curves.slowMiddle;
+ case 'bounceIn':
+ return Curves.bounceIn;
+ case 'bounceOut':
+ return Curves.bounceOut;
+ case 'bounceInOut':
+ return Curves.bounceInOut;
+ case 'elasticIn':
+ return Curves.elasticIn;
+ case 'elasticOut':
+ return Curves.elasticOut;
+ case 'elasticInOut':
+ return Curves.elasticInOut;
+ default:
+ if (type != null) {
+ final ArgumentDecoder<Curve>? decoder = curveDecoders[type];
+ if (decoder != null) {
+ return decoder(source, key);
+ }
+ }
+ return AnimationDefaults.curveOf(context);
+ }
+ }
+
+ /// Extension mechanism for [curve].
+ ///
+ /// The decoders must not return null.
+ ///
+ /// The given key will specify a string, which is known to not match any of
+ /// the values in [Curves].
+ static final Map<String, ArgumentDecoder<Curve>> curveDecoders = <String, ArgumentDecoder<Curve>>{};
+
+ /// Returns a [Decoration] from the specified map.
+ ///
+ /// The `type` key specifies the kind of decoration.
+ ///
+ /// A type of `box` creates a [BoxDecoration] using the keys `color`
+ /// ([color]), `image` ([decorationImage]), `border` ([border]),
+ /// `borderRadius` ([borderRadius]), `boxShadow` (a [list] of [boxShadow]),
+ /// `gradient` ([gradient]), `backgroundBlendMode` (an [enumValue] of [BlendMode]),
+ /// and `shape` (an [enumValue] of [BoxShape]), these keys each corresponding to
+ /// the properties of [BoxDecoration] with the same name.
+ ///
+ /// A type of `flutterLogo` creates a [FlutterLogoDecoration] using the keys
+ /// `color` ([color], corresponds to [FlutterLogoDecoration.textColor]),
+ /// `style` ([enumValue] of [FlutterLogoStyle], defaults to
+ /// [FlutterLogoStyle.markOnly]), and `margin` ([edgeInsets], always with a
+ /// left-to-right direction), the latter two keys corresponding to
+ /// the properties of [FlutterLogoDecoration] with the same name.
+ ///
+ /// A type of `shape` creates a [ShapeDecoration] using the keys `color`
+ /// ([color]), `image` ([decorationImage]), `gradient` ([gradient]), `shadows`
+ /// (a [list] of [boxShadow]), and `shape` ([shapeBorder]), these keys each
+ /// corresponding to the properties of [ShapeDecoration] with the same name.
+ ///
+ /// If the type is none of these, but is not null, then the type is looked up
+ /// in [decorationDecoders], and if an entry is found, this method defers to
+ /// that callback.
+ ///
+ /// Otherwise, returns null.
+ static Decoration? decoration(DataSource source, List<Object> key) {
+ final String? type = source.v<String>([...key, 'type']);
+ switch (type) {
+ case null:
+ return null;
+ case 'box':
+ return BoxDecoration(
+ color: color(source, [...key, 'color']),
+ image: decorationImage(source, [...key, 'image']),
+ border: border(source, [...key, 'border']),
+ borderRadius: borderRadius(source, [...key, 'borderRadius']),
+ boxShadow: list<BoxShadow>(source, [...key, 'boxShadow'], boxShadow),
+ gradient: gradient(source, [...key, 'gradient']),
+ backgroundBlendMode: enumValue<BlendMode>(BlendMode.values, source, [...key, 'backgroundBlendMode']),
+ shape: enumValue<BoxShape>(BoxShape.values, source, [...key, 'shape']) ?? BoxShape.rectangle,
+ );
+ case 'flutterLogo':
+ return FlutterLogoDecoration(
+ textColor: color(source, [...key, 'color']) ?? const Color(0xFF757575),
+ style: enumValue<FlutterLogoStyle>(FlutterLogoStyle.values, source, [...key, 'style']) ?? FlutterLogoStyle.markOnly,
+ margin: (edgeInsets(source, [...key, 'margin']) ?? EdgeInsets.zero).resolve(TextDirection.ltr),
+ );
+ case 'shape':
+ return ShapeDecoration(
+ color: color(source, [...key, 'color']),
+ image: decorationImage(source, [...key, 'image']),
+ gradient: gradient(source, [...key, 'gradient']),
+ shadows: list<BoxShadow>(source, [...key, 'shadows'], boxShadow),
+ shape: shapeBorder(source, [...key, 'shape']) ?? const Border(),
+ );
+ default:
+ final ArgumentDecoder<Decoration?>? decoder = decorationDecoders[type];
+ if (decoder == null) {
+ return null;
+ }
+ return decoder(source, key);
+ }
+ }
+
+ /// Extension mechanism for [decoration].
+ static final Map<String, ArgumentDecoder<Decoration?>> decorationDecoders = <String, ArgumentDecoder<Decoration?>>{};
+
+ /// Returns a [DecorationImage] from the specified map.
+ ///
+ /// The [DecorationImage.image] is determined by interpreting the same key as
+ /// per [imageProvider]. If that method returns null, then this returns null
+ /// also. Otherwise, the return value is used as the provider and additional
+ /// keys map to the identically-named properties of [DecorationImage]:
+ /// `onError` (must be an event handler; the payload map is augmented by an
+ /// `exception` key that contains the text serialization of the exception and
+ /// a `stackTrace` key that contains the stack trace, also as a string),
+ /// `colorFilter` ([colorFilter]), `fit` ([enumValue] of [BoxFit]), `alignment`
+ /// ([alignment], defaults to [Alignment.center]), `centerSlice` ([rect]),
+ /// `repeat` ([enumValue] of [ImageRepeat], defaults to [ImageRepeat.noRepeat]),
+ /// `matchTextDirection` (boolean, defaults to false).
+ static DecorationImage? decorationImage(DataSource source, List<Object> key) {
+ final ImageProvider? provider = imageProvider(source, key);
+ if (provider == null) {
+ return null;
+ }
+ return DecorationImage(
+ image: provider,
+ onError: (Object exception, StackTrace? stackTrace) {
+ final VoidCallback? handler = source.voidHandler([...key, 'onError'], { 'exception': exception.toString(), 'stackTrack': stackTrace.toString() });
+ if (handler != null) {
+ handler();
+ }
+ },
+ colorFilter: colorFilter(source, [...key, 'colorFilter']),
+ fit: enumValue<BoxFit>(BoxFit.values, source, [...key, 'fit']),
+ alignment: alignment(source, [...key, 'alignment']) ?? Alignment.center,
+ centerSlice: rect(source, [...key, 'centerSlice']),
+ repeat: enumValue<ImageRepeat>(ImageRepeat.values, source, [...key, 'repeat']) ?? ImageRepeat.noRepeat,
+ matchTextDirection: source.v<bool>([...key, 'matchTextDirection']) ?? false,
+ );
+ }
+
+ /// Returns a double from the specified double.
+ ///
+ /// Returns 0.0 if it's not a double.
+ ///
+ /// This is useful in situations where null is not acceptable, for example,
+ /// when providing a decoder to [list]. Otherwise, prefer using [DataSource.v]
+ /// directly.
+ static double doubleOrZero(DataSource source, List<Object> key) {
+ return source.v<double>(key) ?? 0.0;
+ }
+
+ /// Returns a [Duration] from the specified integer.
+ ///
+ /// If it's not an integer, the default obtained from
+ /// [AnimationDefaults.durationOf] is used (which is why a [BuildContext] is
+ /// required).
+ static Duration duration(DataSource source, List<Object> key, BuildContext context) {
+ final int? value = source.v<int>(key);
+ if (value == null) {
+ return AnimationDefaults.durationOf(context);
+ }
+ return Duration(milliseconds: value);
+ }
+
+ /// Returns an [EdgeInsetsDirectional] from the specified list.
+ ///
+ /// The list is a list of doubles. An empty or missing list results in a null
+ /// return value. The list should have one through four items. Extra items are
+ /// ignored.
+ ///
+ /// The values are interpreted as follows:
+ ///
+ /// * start: first value.
+ /// * top: second value, defaulting to same as start.
+ /// * end: third value, defaulting to same as start.
+ /// * bottom: fourth value, defaulting to same as top.
+ static EdgeInsetsGeometry? edgeInsets(DataSource source, List<Object> key) {
+ final double? a = source.v<double>([...key, 0]);
+ if (a == null) {
+ return null;
+ }
+ final double? b = source.v<double>([...key, 1]);
+ final double? c = source.v<double>([...key, 2]);
+ final double? d = source.v<double>([...key, 3]);
+ return EdgeInsetsDirectional.fromSTEB(
+ a,
+ b ?? a,
+ c ?? a,
+ d ?? b ?? a,
+ );
+ }
+
+ /// Returns one of the values of the specified enum `T`, from the specified string.
+ ///
+ /// The string must match the name of the enum value, excluding the enum type
+ /// name (the part of its [toString] after the dot).
+ ///
+ /// The first argument must be the `values` list for that enum; this is the
+ /// list of values that is searched.
+ ///
+ /// For example, `enumValue<TileMode>(TileMode.values, source, ['tileMode']) ??
+ /// TileMode.clamp` reads the `tileMode` key of `source`, and looks for the
+ /// first match in [TileMode.values], defaulting to [TileMode.clamp] if
+ /// nothing matches; thus, the string `mirror` would return [TileMode.mirror].
+ static T? enumValue<T>(List<T> values, DataSource source, List<Object> key) {
+ final String? value = source.v<String>(key);
+ if (value == null) {
+ return null;
+ }
+ for (int index = 0; index < values.length; index += 1) {
+ if (value == values[index].toString().split('.').last) {
+ return values[index];
+ }
+ }
+ return null;
+ }
+
+ /// Returns a [FontFeature] from the specified map.
+ ///
+ /// The `feature` key is used as the font feature name (defaulting to the
+ /// probably-useless private value "NONE"), and the `value` key is used as the
+ /// value (defaulting to 1, which typically means "enabled").
+ ///
+ /// As this never returns null, it is possible to use it with [list].
+ static FontFeature fontFeature(DataSource source, List<Object> key) {
+ return FontFeature(source.v<String>([...key, 'feature']) ?? 'NONE', source.v<int>([...key, 'value']) ?? 1);
+ }
+
+ /// Returns a [Gradient] from the specified map.
+ ///
+ /// The `type` key specifies the kind of gradient.
+ ///
+ /// A type of `linear` creates a [LinearGradient] using the keys `begin`
+ /// ([alignment], defaults to [Alignment.centerLeft]), `end` ([alignment],
+ /// defaults to [Alignment.centerRight]), `colors` ([list] of [colorOrBlack],
+ /// defaults to a two-element list with black and white), `stops` ([list] of
+ /// [doubleOrZero]), and `tileMode` ([enumValue] of [TileMode], defaults to
+ /// [TileMode.clamp]), these keys each corresponding to the properties of
+ /// [BoxDecoration] with the same name.
+ ///
+ /// A type of `radial` creates a [RadialGradient] using the keys `center`
+ /// ([alignment], defaults to [Alignment.center]), `radius' (double, defaults
+ /// to 0.5), `colors` ([list] of [colorOrBlack], defaults to a two-element
+ /// list with black and white), `stops` ([list] of [doubleOrZero]), `tileMode`
+ /// ([enumValue] of [TileMode], defaults to [TileMode.clamp]), `focal`
+ /// (([alignment]), and `focalRadius` (double, defaults to zero), these keys
+ /// each corresponding to the properties of [BoxDecoration] with the same
+ /// name.
+ ///
+ /// A type of `linear` creates a [LinearGradient] using the keys `center`
+ /// ([alignment], defaults to [Alignment.center]), `startAngle` (double,
+ /// defaults to 0.0), `endAngle` (double, defaults to 2π), `colors` ([list] of
+ /// [colorOrBlack], defaults to a two-element list with black and white),
+ /// `stops` ([list] of [doubleOrZero]), and `tileMode` ([enumValue] of [TileMode],
+ /// defaults to [TileMode.clamp]), these keys each corresponding to the
+ /// properties of [BoxDecoration] with the same name.
+ ///
+ /// The `transform` property of these gradient classes is not supported.
+ // TODO(ianh): https://github.com/flutter/flutter/issues/87208
+ ///
+ /// If the type is none of these, but is not null, then the type is looked up
+ /// in [gradientDecoders], and if an entry is found, this method defers to
+ /// that callback.
+ ///
+ /// Otherwise, returns null.
+ static Gradient? gradient(DataSource source, List<Object> key) {
+ final String? type = source.v<String>([...key, 'type']);
+ switch (type) {
+ case null:
+ return null;
+ case 'linear':
+ return LinearGradient(
+ begin: alignment(source, [...key, 'begin']) ?? Alignment.centerLeft,
+ end: alignment(source, [...key, 'end']) ?? Alignment.centerRight,
+ colors: list<Color>(source, [...key, 'colors'], colorOrBlack) ?? const <Color>[Color(0xFF000000), Color(0xFFFFFFFF)],
+ stops: list<double>(source, [...key, 'stops'], doubleOrZero),
+ tileMode: enumValue<TileMode>(TileMode.values, source, [...key, 'tileMode']) ?? TileMode.clamp,
+ // transform: GradientTransformMatrix(matrix(source, [...key, 'transform'])), // blocked by https://github.com/flutter/flutter/issues/87208
+ );
+ case 'radial':
+ return RadialGradient(
+ center: alignment(source, [...key, 'center']) ?? Alignment.center,
+ radius: source.v<double>([...key, 'radius']) ?? 0.5,
+ colors: list<Color>(source, [...key, 'colors'], colorOrBlack) ?? const <Color>[Color(0xFF000000), Color(0xFFFFFFFF)],
+ stops: list<double>(source, [...key, 'stops'], doubleOrZero),
+ tileMode: enumValue<TileMode>(TileMode.values, source, [...key, 'tileMode']) ?? TileMode.clamp,
+ focal: alignment(source, [...key, 'focal']),
+ focalRadius: source.v<double>([...key, 'focalRadius']) ?? 0.0,
+ // transform: GradientTransformMatrix(matrix(source, [...key, 'transform'])), // blocked by https://github.com/flutter/flutter/issues/87208
+ );
+ case 'sweep':
+ return SweepGradient(
+ center: alignment(source, [...key, 'center']) ?? Alignment.center,
+ startAngle: source.v<double>([...key, 'startAngle']) ?? 0.0,
+ endAngle: source.v<double>([...key, 'endAngle']) ?? math.pi * 2,
+ colors: list<Color>(source, [...key, 'colors'], colorOrBlack) ?? const <Color>[Color(0xFF000000), Color(0xFFFFFFFF)],
+ stops: list<double>(source, [...key, 'stops'], doubleOrZero),
+ tileMode: enumValue<TileMode>(TileMode.values, source, [...key, 'tileMode']) ?? TileMode.clamp,
+ // transform: GradientTransformMatrix(matrix(source, [...key, 'transform'])), // blocked by https://github.com/flutter/flutter/issues/87208
+ );
+ default:
+ final ArgumentDecoder<Gradient?>? decoder = gradientDecoders[type];
+ if (decoder == null) {
+ return null;
+ }
+ return decoder(source, key);
+ }
+ }
+
+ /// Extension mechanism for [gradient].
+ static final Map<String, ArgumentDecoder<Gradient?>> gradientDecoders = <String, ArgumentDecoder<Gradient?>>{};
+
+ /// Returns a [SliverGridDelegate] from the specified map.
+ ///
+ /// The `type` key specifies the kind of grid delegate.
+ ///
+ /// A type of `fixedCrossAxisCount` creates a
+ /// [SliverGridDelegateWithFixedCrossAxisCount] using the keys
+ /// `crossAxisCount`, `mainAxisSpacing`, `crossAxisSpacing`,
+ /// `childAspectRatio`, and `mainAxisExtent`.
+ ///
+ /// A type of `maxCrossAxisExtent` creates a
+ /// [SliverGridDelegateWithMaxCrossAxisExtent] using the keys
+ /// maxCrossAxisExtent:`, `mainAxisSpacing`, `crossAxisSpacing`,
+ /// `childAspectRatio`, and `mainAxisExtent`.
+ ///
+ /// The types (int or double) and defaults for these keys match the
+ /// identically named arguments to the default constructors of those classes.
+ ///
+ /// If the type is none of these, but is not null, then the type is looked up
+ /// in [gridDelegateDecoders], and if an entry is found, this method defers to
+ /// that callback.
+ ///
+ /// Otherwise, returns null.
+ static SliverGridDelegate? gridDelegate(DataSource source, List<Object> key) {
+ final String? type = source.v<String>([...key, 'type']);
+ switch (type) {
+ case null:
+ return null;
+ case 'fixedCrossAxisCount':
+ return SliverGridDelegateWithFixedCrossAxisCount(
+ crossAxisCount: source.v<int>([...key, 'crossAxisCount']) ?? 2,
+ mainAxisSpacing: source.v<double>([...key, 'mainAxisSpacing']) ?? 0.0,
+ crossAxisSpacing: source.v<double>([...key, 'crossAxisSpacing']) ?? 0.0,
+ childAspectRatio: source.v<double>([...key, 'childAspectRatio']) ?? 1.0,
+ mainAxisExtent: source.v<double>([...key, 'mainAxisExtent']),
+ );
+ case 'maxCrossAxisExtent':
+ return SliverGridDelegateWithMaxCrossAxisExtent(
+ maxCrossAxisExtent: source.v<double>([...key, 'maxCrossAxisExtent']) ?? 100.0,
+ mainAxisSpacing: source.v<double>([...key, 'mainAxisSpacing']) ?? 0.0,
+ crossAxisSpacing: source.v<double>([...key, 'crossAxisSpacing']) ?? 0.0,
+ childAspectRatio: source.v<double>([...key, 'childAspectRatio']) ?? 1.0,
+ mainAxisExtent: source.v<double>([...key, 'mainAxisExtent']),
+ );
+ default:
+ final ArgumentDecoder<SliverGridDelegate?>? decoder = gridDelegateDecoders[type];
+ if (decoder == null) {
+ return null;
+ }
+ return decoder(source, key);
+ }
+ }
+
+ /// Extension mechanism for [gridDelegate].
+ static final Map<String, ArgumentDecoder<SliverGridDelegate?>> gridDelegateDecoders = <String, ArgumentDecoder<SliverGridDelegate?>>{};
+
+ /// Returns an [IconData] from the specified map.
+ ///
+ /// If the map does not have an `icon` key that is an integer, returns null.
+ ///
+ /// Otherwise, returns an [IconData] with the [IconData.codePoint] set to the
+ /// integer from the `icon` key, the [IconData.fontFamily] set to the string
+ /// from the `fontFamily` key, and the [IconData.matchTextDirection] set to
+ /// the boolean from the `matchTextDirection` key (defaulting to false).
+ ///
+ /// For Material Design icons (those from the [Icons] class), the code point
+ /// can be obtained from the documentation for the icon, and the font family
+ /// is `MaterialIcons`. For example, [Icons.chalet] would correspond to
+ /// `{ icon: 0xe14f, fontFamily: 'MaterialIcons' }`.
+ ///
+ /// When building the release build of an application that uses the RFW
+ /// package, because this method creates non-const [IconData] objects
+ /// dynamically, the `--no-tree-shake-icons` option must be used.
+ static IconData? iconData(DataSource source, List<Object> key) {
+ final int? icon = source.v<int>([...key, 'icon']);
+ if (icon == null) {
+ return null;
+ }
+ return IconData(
+ icon,
+ fontFamily: source.v<String>([...key, 'fontFamily']),
+ matchTextDirection: source.v<bool>([...key, 'matchTextDirection']) ?? false,
+ );
+ }
+
+ /// Returns an [IconThemeData] from the specified map.
+ ///
+ /// If the map is absent, returns null.
+ ///
+ /// Otherwise (even if it has no keys), the [IconThemeData] is created from
+ /// the following keys: 'color` ([color]), `opacity` (double), `size`
+ /// (double).
+ static IconThemeData? iconThemeData(DataSource source, List<Object> key) {
+ if (!source.isMap(key)) {
+ return null;
+ }
+ return IconThemeData(
+ color: color(source, [...key, 'color']),
+ opacity: source.v<double>([...key, 'opacity']),
+ size: source.v<double>([...key, 'size']),
+ );
+ }
+
+ /// Returns an [ImageProvider] from the specifed map.
+ ///
+ /// The `source` key of the specified map is controlling. It must be a string.
+ /// If its value is one of the keys in [imageProviderDecoders], then the
+ /// relevant decoder is invoked and its return value is used (even if it is
+ /// null).
+ ///
+ /// Otherwise, if the `source` key gives an absolute URL (one with a scheme),
+ /// then a [NetworkImage] with that URL is returned. Its scale is given by the
+ /// `scale` key (double, defaults to 1.0).
+ ///
+ /// Otherwise, if the `source` key gives a relative URL (i.e. it can be parsed
+ /// as a URL and has no scheme), an [AssetImage] with the name given by the
+ /// `source` key is returned.
+ ///
+ /// Otherwise, if there is no `source` key in the map, or if that cannot be
+ /// parsed as a URL (absolute or relative), null is returned.
+ static ImageProvider? imageProvider(DataSource source, List<Object> key) {
+ final String? image = source.v<String>([...key, 'source']);
+ if (image == null) {
+ return null;
+ }
+ if (imageProviderDecoders.containsKey(image)) {
+ return imageProviderDecoders[image]!(source, key);
+ }
+ final Uri? imageUrl = Uri.tryParse(image);
+ if (imageUrl == null) {
+ return null;
+ }
+ if (!imageUrl.hasScheme) {
+ return AssetImage(image);
+ }
+ return NetworkImage(image, scale: source.v<double>([...key, 'scale']) ?? 1.0);
+ }
+
+ /// Extension mechanism for [imageProvider].
+ static final Map<String, ArgumentDecoder<ImageProvider?>> imageProviderDecoders = <String, ArgumentDecoder<ImageProvider?>>{};
+
+ /// Returns a [List] of `T` values from the specified list, using the given
+ /// `decoder` to parse each value.
+ ///
+ /// If the list is absent _or empty_, returns null (not an empty list).
+ ///
+ /// Otherwise, returns a list with as many items as the specified list, with
+ /// each entry in the list decoded using `decoder`.
+ ///
+ /// If `T` is non-nullable, the decoder must also be non-nullable.
+ static List<T>? list<T>(DataSource source, List<Object> key, ArgumentDecoder<T> decoder) {
+ final int count = source.length(key);
+ if (count == 0) {
+ return null;
+ }
+ return List<T>.generate(count, (int index) {
+ return decoder(source, [...key, index]);
+ });
+ }
+
+ /// Returns a [Locale] from the specified string.
+ ///
+ /// The string is split on hyphens ("-").
+ ///
+ /// If the string is null, returns null.
+ ///
+ /// If there is no hyphen in the list, uses the one-argument form of [new
+ /// Locale], passing the whole string.
+ ///
+ /// If there is one hyphen in the list, uses the two-argument form of [new
+ /// Locale], passing the parts before and after the hyphen respectively.
+ ///
+ /// If there are two or more hyphens, uses the [new Locale.fromSubtags]
+ /// constructor.
+ static Locale? locale(DataSource source, List<Object> key) {
+ final String? value = source.v<String>(key);
+ if (value == null) {
+ return null;
+ }
+ final List<String> subtags = value.split('-');
+ if (subtags.isEmpty) {
+ return null;
+ }
+ if (subtags.length == 1) {
+ return Locale(value);
+ }
+ if (subtags.length == 2) {
+ return Locale(subtags[0], subtags[1]);
+ }
+ // TODO(ianh): verify this is correct (I tried looking up the Unicode spec but it was... confusing)
+ return Locale.fromSubtags(languageCode: subtags[0], scriptCode: subtags[1], countryCode: subtags[2]);
+ }
+
+ /// Returns a list of 16 doubles from the specified list.
+ ///
+ /// If the list is missing or has fewer than 16 entries, returns null.
+ ///
+ /// Otherwise, returns a list of 16 entries, corresponding to the first 16
+ /// entries of the specified list, with any non-double values replaced by 0.0.
+ static Matrix4? matrix(DataSource source, List<Object> key) {
+ final double? arg15 = source.v<double>([...key, 15]);
+ if (arg15 == null) {
+ return null;
+ }
+ return Matrix4(
+ source.v<double>([...key, 0]) ?? 0.0,
+ source.v<double>([...key, 1]) ?? 0.0,
+ source.v<double>([...key, 2]) ?? 0.0,
+ source.v<double>([...key, 3]) ?? 0.0,
+ source.v<double>([...key, 4]) ?? 0.0,
+ source.v<double>([...key, 5]) ?? 0.0,
+ source.v<double>([...key, 6]) ?? 0.0,
+ source.v<double>([...key, 7]) ?? 0.0,
+ source.v<double>([...key, 8]) ?? 0.0,
+ source.v<double>([...key, 9]) ?? 0.0,
+ source.v<double>([...key, 10]) ?? 0.0,
+ source.v<double>([...key, 11]) ?? 0.0,
+ source.v<double>([...key, 12]) ?? 0.0,
+ source.v<double>([...key, 13]) ?? 0.0,
+ source.v<double>([...key, 14]) ?? 0.0,
+ arg15,
+ );
+ }
+
+ /// Returns a [MaskFilter] from the specified map.
+ ///
+ /// The `type` key specifies the kind of mask filter.
+ ///
+ /// A type of `blur` creates a [MaskFilter.blur]. The `style` key ([enumValue] of
+ /// [BlurStyle], defaults to [BlurStyle.normal]) is used as the blur style,
+ /// and the `sigma` key (double, defaults to 1.0) is used as the blur sigma.
+ ///
+ /// If the type is none of these, but is not null, then the type is looked up
+ /// in [maskFilterDecoders], and if an entry is found, this method defers to
+ /// that callback.
+ ///
+ /// Otherwise, returns null.
+ static MaskFilter? maskFilter(DataSource source, List<Object> key) {
+ final String? type = source.v<String>([...key, 'type']);
+ switch (type) {
+ case null:
+ return null;
+ case 'blur':
+ return MaskFilter.blur(
+ enumValue<BlurStyle>(BlurStyle.values, source, [...key, 'style']) ?? BlurStyle.normal,
+ source.v<double>([...key, 'sigma']) ?? 1.0,
+ );
+ default:
+ final ArgumentDecoder<MaskFilter?>? decoder = maskFilterDecoders[type];
+ if (decoder == null) {
+ return null;
+ }
+ return decoder(source, key);
+ }
+ }
+
+ /// Extension mechanism for [maskFilter].
+ static final Map<String, ArgumentDecoder<MaskFilter?>> maskFilterDecoders = <String, ArgumentDecoder<MaskFilter?>>{};
+
+ /// Returns an [Offset] from the specified map.
+ ///
+ /// The map must have an `x` key and a `y` key, doubles.
+ static Offset? offset(DataSource source, List<Object> key) {
+ final double? x = source.v<double>([...key, 'x']);
+ if (x == null) {
+ return null;
+ }
+ final double? y = source.v<double>([...key, 'y']);
+ if (y == null) {
+ return null;
+ }
+ return Offset(x, y);
+ }
+
+ /// Returns a [Paint] from the specified map.
+ ///
+ /// If the map is absent, returns null.
+ ///
+ /// Otherwise (even if it has no keys), a new [Paint] is created and its
+ /// properties are set according to the identically-named properties of the
+ /// map, as follows:
+ ///
+ /// * `blendMode`: [enumValue] of [BlendMode].
+ /// * `color`: [color].
+ /// * `colorFilter`: [colorFilter].
+ /// * `filterQuality`: [enumValue] of [FilterQuality].
+ // * `imageFilter`: [imageFilter].
+ // * `invertColors`: boolean.
+ /// * `isAntiAlias`: boolean.
+ /// * `maskFilter`: [maskFilter].
+ /// * `shader`: [shader].
+ // * `strokeCap`: [enumValue] of [StrokeCap].
+ // * `strokeJoin`: [enumValue] of [StrokeJoin].
+ // * `strokeMiterLimit`: double.
+ // * `strokeWidth`: double.
+ // * `style`: [enumValue] of [PaintingStyle].
+ ///
+ /// (Some values are not supported, because there is no way for them to be
+ /// used currently in RFW contexts.)
+ static Paint? paint(DataSource source, List<Object> key) {
+ if (!source.isMap(key)) {
+ return null;
+ }
+ final Paint result = Paint();
+ final BlendMode? paintBlendMode = enumValue<BlendMode>(BlendMode.values, source, [...key, 'blendMode']);
+ if (paintBlendMode != null) {
+ result.blendMode = paintBlendMode;
+ }
+ final Color? paintColor = color(source, [...key, 'color']);
+ if (paintColor != null) {
+ result.color = paintColor;
+ }
+ final ColorFilter? paintColorFilter = colorFilter(source, [...key, 'colorFilter']);
+ if (paintColorFilter != null) {
+ result.colorFilter = paintColorFilter;
+ }
+ final FilterQuality? paintFilterQuality = enumValue<FilterQuality>(FilterQuality.values, source, [...key, 'filterQuality']);
+ if (paintFilterQuality != null) {
+ result.filterQuality = paintFilterQuality;
+ }
+ // final ImageFilter? paintImageFilter = imageFilter(source, [...key, 'imageFilter']);
+ // if (paintImageFilter != null) {
+ // result.imageFilter = paintImageFilter;
+ // }
+ // final bool? paintInvertColors = source.v<bool>([...key, 'invertColors']);
+ // if (paintInvertColors != null) {
+ // result.invertColors = paintInvertColors;
+ // }
+ final bool? paintIsAntiAlias = source.v<bool>([...key, 'isAntiAlias']);
+ if (paintIsAntiAlias != null) {
+ result.isAntiAlias = paintIsAntiAlias;
+ }
+ final MaskFilter? paintMaskFilter = maskFilter(source, [...key, 'maskFilter']);
+ if (paintMaskFilter != null) {
+ result.maskFilter = paintMaskFilter;
+ }
+ final Shader? paintShader = shader(source, [...key, 'shader']);
+ if (paintShader != null) {
+ result.shader = paintShader;
+ }
+ // final StrokeCap? paintStrokeCap = enumValue<StrokeCap>(StrokeCap.values, source, [...key, 'strokeCap']),
+ // if (paintStrokeCap != null) {
+ // result.strokeCap = paintStrokeCap;
+ // }
+ // final StrokeJoin? paintStrokeJoin = enumValue<StrokeJoin>(StrokeJoin.values, source, [...key, 'strokeJoin']),
+ // if (paintStrokeJoin != null) {
+ // result.strokeJoin = paintStrokeJoin;
+ // }
+ // final double paintStrokeMiterLimit? = source.v<double>([...key, 'strokeMiterLimit']),
+ // if (paintStrokeMiterLimit != null) {
+ // result.strokeMiterLimit = paintStrokeMiterLimit;
+ // }
+ // final double paintStrokeWidth? = source.v<double>([...key, 'strokeWidth']),
+ // if (paintStrokeWidth != null) {
+ // result.strokeWidth = paintStrokeWidth;
+ // }
+ // final PaintingStyle? paintStyle = enumValue<PaintingStyle>(PaintingStyle.values, source, [...key, 'style']),
+ // if (paintStyle != null) {
+ // result.style = paintStyle;
+ // }
+ return result;
+ }
+
+ /// Returns a [Radius] from the specified map.
+ ///
+ /// The map must have an `x` value corresponding to [Radius.x], and may have a
+ /// `y` value corresponding to [Radius.y].
+ ///
+ /// If the map only has an `x` key, the `y` value is assumed to be the same
+ /// (as in [Radius.circular]).
+ ///
+ /// If the `x` key is absent, the returned value is null.
+ static Radius? radius(DataSource source, List<Object> key) {
+ final double? x = source.v<double>([...key, 'x']);
+ if (x == null) {
+ return null;
+ }
+ final double y = source.v<double>([...key, 'y']) ?? x;
+ return Radius.elliptical(x, y);
+ }
+
+ /// Returns a [Rect] from the specified map.
+ ///
+ /// If the map is absent, returns null.
+ ///
+ /// Otherwise, returns a [Rect.fromLTWH] whose x, y, width, and height
+ /// components are determined from the `x`, `y`, `w`, and `h` properties of
+ /// the map, with missing (or non-double) values replaced by zeros.
+ static Rect? rect(DataSource source, List<Object> key) {
+ if (!source.isMap(key)) {
+ return null;
+ }
+ final double x = source.v<double>([...key, 'x']) ?? 0.0;
+ final double y = source.v<double>([...key, 'y']) ?? 0.0;
+ final double w = source.v<double>([...key, 'w']) ?? 0.0;
+ final double h = source.v<double>([...key, 'h']) ?? 0.0;
+ return Rect.fromLTWH(x, y, w, h);
+ }
+
+ /// Returns a [ShapeBorder] from the specified map or list.
+ ///
+ /// If the key identifies a list, then each entry in the list is decoded by
+ /// recursively invoking [shapeBorder], and the result is the combination of
+ /// those [ShapeBorder] values as obtained using the [ShapeBorder.+] operator.
+ ///
+ /// Otherwise, if the key identifies a map with a `type` value, the map is
+ /// interpreted according to the `type` as follows:
+ ///
+ /// * `box`: the map's `sides` key is interpreted as a list by [border] and
+ /// the resulting [BoxBorder] (actually, [BorderDirectional]) is returned.
+ ///
+ /// * `beveled`: a [BeveledRectangleBorder] is returned; the `side` key is
+ /// interpreted by [borderSide] to set the [BeveledRectangleBorder.side]
+ /// (default of [BorderSide.none)), and the `borderRadius` key is
+ /// interpreted by [borderRadius] to set the
+ /// [BeveledRectangleBorder.borderRadius] (default [BorderRadius.zero]).
+ ///
+ /// * `circle`: a [CircleBorder] is returned; the `side` key is interpreted
+ /// by [borderSide] to set the [BeveledRectangleBorder.side] (default of
+ /// [BorderSide.none)).
+ ///
+ /// * `continuous`: a [ContinuousRectangleBorder] is returned; the `side` key
+ /// is interpreted by [borderSide] to set the [BeveledRectangleBorder.side]
+ /// (default of [BorderSide.none)), and the `borderRadius` key is
+ /// interpreted by [borderRadius] to set the
+ /// [BeveledRectangleBorder.borderRadius] (default [BorderRadius.zero]).
+ ///
+ /// * `rounded`: a [RoundedRectangleBorder] is returned; the `side` key is
+ /// interpreted by [borderSide] to set the [BeveledRectangleBorder.side]
+ /// (default of [BorderSide.none)), and the `borderRadius` key is
+ /// interpreted by [borderRadius] to set the
+ /// [BeveledRectangleBorder.borderRadius] (default [BorderRadius.zero]).
+ ///
+ /// * `stadium`: a [StadiumBorder] is returned; the `side` key is interpreted
+ /// by [borderSide] to set the [BeveledRectangleBorder.side] (default of
+ /// [BorderSide.none)).
+ ///
+ /// If the type is none of these, then the type is looked up in
+ /// [shapeBorderDecoders], and if an entry is found, this method defers to
+ /// that callback.
+ ///
+ /// Otherwise, if type is null or is not found in [shapeBorderDecoders], returns null.
+ static ShapeBorder? shapeBorder(DataSource source, List<Object> key) {
+ final List<ShapeBorder?>? shapes = list<ShapeBorder?>(source, key, shapeBorder);
+ if (shapes != null) {
+ return shapes.where((ShapeBorder? a) => a != null).reduce((ShapeBorder? a, ShapeBorder? b) => a! + b!);
+ }
+ final String? type = source.v<String>([...key, 'type']);
+ switch (type) {
+ case null:
+ return null;
+ case 'box':
+ return border(source, [...key, 'sides']) ?? const Border();
+ case 'beveled':
+ return BeveledRectangleBorder(
+ side: borderSide(source, [...key, 'side']) ?? BorderSide.none,
+ borderRadius: borderRadius(source, [...key, 'borderRadius']) ?? BorderRadius.zero,
+ );
+ case 'circle':
+ return CircleBorder(
+ side: borderSide(source, [...key, 'side']) ?? BorderSide.none,
+ );
+ case 'continuous':
+ return ContinuousRectangleBorder(
+ side: borderSide(source, [...key, 'side']) ?? BorderSide.none,
+ borderRadius: borderRadius(source, [...key, 'borderRadius']) ?? BorderRadius.zero,
+ );
+ case 'rounded':
+ return RoundedRectangleBorder(
+ side: borderSide(source, [...key, 'side']) ?? BorderSide.none,
+ borderRadius: borderRadius(source, [...key, 'borderRadius']) ?? BorderRadius.zero,
+ );
+ case 'stadium':
+ return StadiumBorder(
+ side: borderSide(source, [...key, 'side']) ?? BorderSide.none,
+ );
+ default:
+ final ArgumentDecoder<ShapeBorder>? decoder = shapeBorderDecoders[type];
+ if (decoder == null) {
+ return null;
+ }
+ return decoder(source, key);
+ }
+ }
+
+ /// Extension mechanism for [shapeBorder].
+ static final Map<String, ArgumentDecoder<ShapeBorder>> shapeBorderDecoders = <String, ArgumentDecoder<ShapeBorder>>{};
+
+ /// Returns a [Shader] based on the specified map.
+ ///
+ /// The `type` key specifies the kind of shader.
+ ///
+ /// A type of `linear`, `radial`, or `sweep` is interpreted as described by
+ /// [gradient]; then, the gradient is compiled to a shader by applying the
+ /// `rect` (interpreted by [rect]) and `textDirection` (interpreted as an
+ /// [enumValue] of [TextDirection]) using the [Gradient.createShader] method.
+ ///
+ /// If the type is none of these, but is not null, then the type is looked up
+ /// in [shaderDecoders], and if an entry is found, this method defers to
+ /// that callback.
+ ///
+ /// Otherwise, returns null.
+ static Shader? shader(DataSource source, List<Object> key) {
+ final String? type = source.v<String>([...key, 'type']);
+ switch (type) {
+ case null:
+ return null;
+ case 'linear':
+ case 'radial':
+ case 'sweep':
+ return gradient(source, key)!.createShader(
+ rect(source, [...key, 'rect']) ?? Rect.zero,
+ textDirection: enumValue<TextDirection>(TextDirection.values, source, ['textDirection']) ?? TextDirection.ltr,
+ );
+ default:
+ final ArgumentDecoder<Shader?>? decoder = shaderDecoders[type];
+ if (decoder == null) {
+ return null;
+ }
+ return decoder(source, key);
+ }
+ }
+
+ /// Extension mechanism for [shader].
+ static final Map<String, ArgumentDecoder<Shader?>> shaderDecoders = <String, ArgumentDecoder<Shader?>>{};
+
+ /// Returns a string from the specified string.
+ ///
+ /// Returns the empty string if it's not a string.
+ ///
+ /// This is useful in situations where null is not acceptable, for example,
+ /// when providing a decoder to [list]. Otherwise, prefer using [DataSource.v]
+ /// directly.
+ static String string(DataSource source, List<Object> key) {
+ return source.v<String>(key) ?? '';
+ }
+
+ /// Returns a [StrutStyle] from the specified map.
+ ///
+ /// If the map is absent, returns null.
+ ///
+ /// Otherwise (even if it has no keys), the [StrutStyle] is created from the
+ /// following keys: 'fontFamily` (string), `fontFamilyFallback` ([list] of
+ /// [string]), `fontSize` (double), `height` (double), `leadingDistribution`
+ /// ([enumValue] of [TextLeadingDistribution]), `leading` (double),
+ /// `fontWeight` ([enumValue] of [FontWeight]), `fontStyle` ([enumValue] of
+ /// [FontStyle]), `forceStrutHeight` (boolean).
+ static StrutStyle? strutStyle(DataSource source, List<Object> key) {
+ if (!source.isMap(key)) {
+ return null;
+ }
+ return StrutStyle(
+ fontFamily: source.v<String>([...key, 'fontFamily']),
+ fontFamilyFallback: list<String>(source, [...key, 'fontFamilyFallback'], string),
+ fontSize: source.v<double>([...key, 'fontSize']),
+ height: source.v<double>([...key, 'height']),
+ leadingDistribution: enumValue<TextLeadingDistribution>(TextLeadingDistribution.values, source, [...key, 'leadingDistribution']),
+ leading: source.v<double>([...key, 'leading']),
+ fontWeight: enumValue<FontWeight>(FontWeight.values, source, [...key, 'fontWeight']),
+ fontStyle: enumValue<FontStyle>(FontStyle.values, source, [...key, 'fontStyle']),
+ forceStrutHeight: source.v<bool>([...key, 'forceStrutHeight']),
+ );
+ }
+
+ /// Returns a [TextHeightBehavior] from the specified map.
+ ///
+ /// If the map is absent, returns null.
+ ///
+ /// Otherwise (even if it has no keys), the [TextHeightBehavior] is created
+ /// from the following keys: 'applyHeightToFirstAscent` (boolean; defaults to
+ /// true), `applyHeightToLastDescent` (boolean, defaults to true), and
+ /// `leadingDistribution` ([enumValue] of [TextLeadingDistribution], deafults
+ /// to [TextLeadingDistribution.proportional]).
+ static TextHeightBehavior? textHeightBehavior(DataSource source, List<Object> key) {
+ if (!source.isMap(key)) {
+ return null;
+ }
+ return TextHeightBehavior(
+ applyHeightToFirstAscent: source.v<bool>([...key, 'applyHeightToFirstAscent']) ?? true,
+ applyHeightToLastDescent: source.v<bool>([...key, 'applyHeightToLastDescent']) ?? true,
+ leadingDistribution: enumValue<TextLeadingDistribution>(TextLeadingDistribution.values, source, [...key, 'leadingDistribution']) ?? TextLeadingDistribution.proportional,
+ );
+ }
+
+ /// Returns a [TextDecoration] from the specified list or string.
+ ///
+ /// If the key identifies a list, then each entry in the list is decoded by
+ /// recursively invoking [textDecoration], and the result is the combination
+ /// of those [TextDecoration] values as obtained using
+ /// [TextDecoration.combine].
+ ///
+ /// Otherwise, if the key identifies a string, then the value `lineThrough` is
+ /// mapped to [TextDecoration.lineThrough], `overline` to
+ /// [TextDecoration.overline], and `underline` to [TextDecoration.underline].
+ /// Other values (and the abscence of a value) are interpreted as
+ /// [TextDecoration.none].
+ static TextDecoration textDecoration(DataSource source, List<Object> key) {
+ final List<TextDecoration>? decorations = list<TextDecoration>(source, key, textDecoration);
+ if (decorations != null) {
+ return TextDecoration.combine(decorations);
+ }
+ switch (source.v<String>([...key])) {
+ case 'lineThrough':
+ return TextDecoration.lineThrough;
+ case 'overline':
+ return TextDecoration.overline;
+ case 'underline':
+ return TextDecoration.underline;
+ default:
+ return TextDecoration.none;
+ }
+ }
+
+ /// Returns a [TextStyle] from the specified map.
+ ///
+ /// If the map is absent, returns null.
+ ///
+ /// Otherwise (even if it has no keys), the [TextStyle] is created from the
+ /// following keys: `color` ([color]), `backgroundColor` ([color]), `fontSize`
+ /// (double), `fontWeight` ([enumValue] of [FontWeight]), `fontStyle`
+ /// ([enumValue] of [FontStyle]), `letterSpacing` (double), `wordSpacing`
+ /// (double), `textBaseline` ([enumValue] of [TextBaseline]), `height`
+ /// (double), `leadingDistribution` ([enumValue] of
+ /// [TextLeadingDistribution]), `locale` ([locale]), `foreground` ([paint]),
+ /// `background` ([paint]), `shadows` ([list] of [boxShadow]s), `fontFeatures`
+ /// ([list] of [fontFeature]s), `decoration` ([textDecoration]),
+ /// `decorationColor` ([color]), `decorationStyle` ([enumValue] of
+ /// [TextDecorationStyle]), `decorationThickness` (double), 'fontFamily`
+ /// (string), `fontFamilyFallback` ([list] of [string]), and `overflow`
+ /// ([enumValue] of [TextOverflow]).
+ static TextStyle? textStyle(DataSource source, List<Object> key) {
+ if (!source.isMap(key)) {
+ return null;
+ }
+ return TextStyle(
+ color: color(source, [...key, 'color']),
+ backgroundColor: color(source, [...key, 'backgroundColor']),
+ fontSize: source.v<double>([...key, 'fontSize']),
+ fontWeight: enumValue<FontWeight>(FontWeight.values, source, [...key, 'fontWeight']),
+ fontStyle: enumValue<FontStyle>(FontStyle.values, source, [...key, 'fontStyle']),
+ letterSpacing: source.v<double>([...key, 'letterSpacing']),
+ wordSpacing: source.v<double>([...key, 'wordSpacing']),
+ textBaseline: enumValue<TextBaseline>(TextBaseline.values, source, ['textBaseline']),
+ height: source.v<double>([...key, 'height']),
+ leadingDistribution: enumValue<TextLeadingDistribution>(TextLeadingDistribution.values, source, [...key, 'leadingDistribution']),
+ locale: locale(source, [...key, 'locale']),
+ foreground: paint(source, [...key, 'foreground']),
+ background: paint(source, [...key, 'background']),
+ shadows: list<BoxShadow>(source, [...key, 'shadows'], boxShadow),
+ fontFeatures: list<FontFeature>(source, [...key, 'fontFeatures'], fontFeature),
+ decoration: textDecoration(source, [...key, 'decoration']),
+ decorationColor: color(source, [...key, 'decorationColor']),
+ decorationStyle: enumValue<TextDecorationStyle>(TextDecorationStyle.values, source, [...key, 'decorationStyle']),
+ decorationThickness: source.v<double>([...key, 'decorationThickness']),
+ fontFamily: source.v<String>([...key, 'fontFamily']),
+ fontFamilyFallback: list<String>(source, [...key, 'fontFamilyFallback'], string),
+ overflow: enumValue<TextOverflow>(TextOverflow.values, source, ['overflow']),
+ );
+ }
+
+ /// Returns a [VisualDensity] from the specified string or map.
+ ///
+ /// If the specified value is a string, then it is interpreted as follows:
+ ///
+ /// * `adaptivePlatformDensity`: returns
+ /// [VisualDensity.adaptivePlatformDensity] (which varies by platform).
+ /// * `comfortable`: returns [VisualDensity.comfortable].
+ /// * `compact`: returns [VisualDensity.compact].
+ /// * `standard`: returns [VisualDensity.standard].
+ ///
+ /// Otherwise, if the specified value is a map, then the keys `horizontal` and
+ /// `vertical` (doubles) are used to create a custom [VisualDensity]. The
+ /// specified values must be in the range given by
+ /// [VisualDensity.minimumDensity] to [VisualDensity.maximumDensity]. Missing
+ /// values are interpreted as zero.
+ static VisualDensity? visualDensity(DataSource source, List<Object> key) {
+ final String? type = source.v<String>(key);
+ switch (type) {
+ case 'adaptivePlatformDensity':
+ return VisualDensity.adaptivePlatformDensity;
+ case 'comfortable':
+ return VisualDensity.comfortable;
+ case 'compact':
+ return VisualDensity.compact;
+ case 'standard':
+ return VisualDensity.standard;
+ default:
+ if (!source.isMap(key)) {
+ return null;
+ }
+ return VisualDensity(
+ horizontal: source.v<double>([...key, 'horizontal']) ?? 0.0,
+ vertical: source.v<double>([...key, 'vertical']) ?? 0.0,
+ );
+ }
+ }
+}
diff --git a/packages/rfw/lib/flutter/content.dart b/packages/rfw/lib/flutter/content.dart
new file mode 100644
index 0000000..e043ba6
--- /dev/null
+++ b/packages/rfw/lib/flutter/content.dart
@@ -0,0 +1,350 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is hand-formatted.
+
+import 'dart:async';
+import 'dart:convert';
+
+import '../dart/model.dart';
+
+/// Signature for the callback passed to [DynamicContent.subscribe].
+typedef SubscriptionCallback = void Function(Object value);
+
+/// Returns a copy of a data structure if it consists of only [DynamicMap]s,
+/// [DynamicList]s, [int]s, [double]s, [bool]s, and [String]s.
+///
+/// This is relatively expensive as the entire data structure must be walked and
+/// new objects created.
+Object? deepClone(Object? template) {
+ if (template == null) {
+ return null;
+ } else if (template is DynamicMap) {
+ return template.map((String key, Object? value) => MapEntry<String, Object?>(key, deepClone(value)));
+ } else if (template is DynamicList) {
+ return template.map((Object? value) => deepClone(value)).toList();
+ } else {
+ assert(template is int || template is double || template is bool || template is String, 'unexpected state object type: ${template.runtimeType} ($template)');
+ return template;
+ }
+}
+
+/// Configuration data from the remote widgets.
+///
+/// Typically this represents the data model, and is updated frequently (or at
+/// least, more frequently than the remote widget definitions) by the server
+/// (or, indeed, by local code, in response to events or other activity).
+///
+/// ## Structure
+///
+/// A [DynamicContent] object represents a tree. A consumer (the remote widgets)
+/// can subscribe to a node to obtain its value.
+///
+/// The root of a [DynamicContent] tree is a map of string-value pairs. The
+/// values are:
+///
+/// * Other maps of string-value pairs ([DynamicMap]).
+/// * Lists of values ([DynamicList]).
+/// * Booleans ([bool]).
+/// * Integers ([int]).
+/// * Doubles ([double]).
+/// * Strings ([String]).
+///
+/// The keys in the root map are independently updated. Typically each
+/// represents a different category of data from the server that the server
+/// updates independently, e.g. theming information and the app state might be
+/// provided separately.
+///
+/// ## Updates
+///
+/// Data is updated using [update] and [updateAll]. The objects passed to those
+/// methods are of the types described above.
+///
+/// Objects for [update] can be obtained in several ways:
+///
+/// 1. Dart maps, lists, and literals of the types given above ("raw data") can
+/// be created directly in code. This is useful for configuring remote
+/// widgets with local client information such as the current time, GPS
+/// coordinates, system settings like dark mode, window dimensions, etc,
+/// where the data was never encoded in the first place.
+///
+/// 2. A Remote Flutter Widgets binary data blob can be parsed using
+/// [decodeDataBlob]. This is the preferred method for decoding data obtained
+/// from the network. See [encodeDataBlob] for a function that generates data
+/// in this format.
+///
+/// 3. A Remote Flutter Widgets text data file can be parsed using
+/// [parseTextDataFile]. Decoding this text format is about ten times slower
+/// than decoding the binary format and about five times slower than decoding
+/// JSON, so it is discouraged in production applications. This text
+/// representation of the Remote Flutter Widgets binary data blob format is
+/// similar to JSON. This form is typically not used in applications; it is
+/// more common for this format to be used on the server side, parsed and
+/// then encoded in binary form for transmission to the client.
+///
+/// 4. Data in JSON form can be decoded using [JsonCodec.decode] (typically
+/// using the [json] object); the JSON decoder creates the same types of data
+/// structures as expected by [update]. This is not generally recommended but
+/// may be appropriate if the data was obtained from a third-party source in
+/// JSON form and could not be preprocessed by a server to convert the data
+/// to the binary form described above. Numbers in JSON are interpreted as
+/// doubles if they contain a decimal point or an `e` in their source
+/// representation, and as integers otherwise. This can cause issues as the
+/// [DynamicContent] and [DataSource] are strongly typed and distinguish
+/// [int] and [double]. Explicit nulls in the JSON are an error (the data
+/// format supported by [DynamicContent] does not support nulls). Decoding
+/// JSON is about 1.5x slower than the binary format.
+///
+/// Subscribers are notified immediately after an update if their data changed.
+///
+/// ## References
+///
+/// To subscribe to a node, the [subscribe] method is used. The method returns
+/// the current value. When the value later changes, the provided callback is
+/// invoked with the new value.
+///
+/// The [unsubscribe] method must be called when the client no longer needs
+/// updates (e.g. when the widget goes away).
+///
+/// To identify a node, a list of keys is used, giving the path from the root to
+/// the node. Each key is either a string (to index into maps) or an integer (to
+/// index into lists). If no node is identified, the [missing] value is
+/// returned. Similarly, if a node goes away, subscribers are given the value
+/// [missing] as the new value. It is not an error to subscribe to missing data.
+/// It _is_ an error to add [missing] values to the data model, however.
+///
+/// The [LocalWidgetBuilder]s passed to a [LocalWidgetLibrary] use a
+/// [DataSource] as their interface into the [DynamicContent]. To ensure the
+/// integrity of the update mechanism, that interface only allows access to
+/// leaves, not intermediate nodes (maps and lists).
+///
+/// It is an error to subscribe to the same key multiple times with the same
+/// callback.
+class DynamicContent {
+ /// Create a fresh [DynamicContent] object.
+ ///
+ /// The `initialData` argument, if provided, is used to update all the keys
+ /// in the [DynamicContent], as if [updateAll] had been called.
+ DynamicContent([ DynamicMap? initialData ]) {
+ if (initialData != null) {
+ updateAll(initialData);
+ }
+ }
+
+ final _DynamicNode _root = _DynamicNode.root();
+
+ /// Update all the keys in the [DynamicContent].
+ ///
+ /// Each key in the provided map is added to [DynamicContent], replacing any
+ /// data that was there previously, as if [update] had been called for each
+ /// key.
+ ///
+ /// Existing keys that are not present in the given map are left unmodified.
+ void updateAll(DynamicMap initialData) {
+ for (final String key in initialData.keys) {
+ final Object value = initialData[key] ?? missing;
+ update(key, value);
+ }
+ }
+
+ /// Updates the content with the specified data.
+ ///
+ /// The given `rootKey` is updated with the data `value`.
+ ///
+ /// The `value` must consist exclusively of [DynamicMap], [DynamicList], [int],
+ /// [double], [bool], and [String] objects.
+ void update(String rootKey, Object value) {
+ _root.updateKey(rootKey, deepClone(value)!);
+ _scheduleCleanup();
+ }
+
+ /// Obtain the value at location `key`, and subscribe `callback` to that key
+ /// so that future [update]s will invoke the callback with the new value.
+ ///
+ /// The value is always non-null; if the value is missing, the [missing]
+ /// object is used instead.
+ ///
+ /// Use [unsubscribe] when the subscription is no longer needed.
+ Object subscribe(List<Object> key, SubscriptionCallback callback) {
+ return _root.subscribe(key, 0, callback);
+ }
+
+ /// Removes a subscription created by [subscribe].
+ void unsubscribe(List<Object> key, SubscriptionCallback callback) {
+ _root.unsubscribe(key, 0, callback);
+ }
+
+ bool _cleanupPending = false;
+
+ void _scheduleCleanup() {
+ if (!_cleanupPending) {
+ _cleanupPending = true;
+ scheduleMicrotask(() {
+ _cleanupPending = false;
+ _DynamicNode.cleanup();
+ });
+ }
+ }
+
+ @override
+ String toString() => '$runtimeType($_root)';
+}
+
+// Node in the [DynamicContent] tree. This should contain no [BlobNode]s.
+class _DynamicNode {
+ _DynamicNode(this._key, this._parent, this._value) : assert(_value == missing || _hasValidType(_value));
+
+ _DynamicNode.root() : _key = missing, _parent = null, _value = DynamicMap(); // ignore: prefer_collection_literals
+
+ final Object _key;
+ final _DynamicNode? _parent;
+ Object _value;
+
+ final Set<SubscriptionCallback> _callbacks = <SubscriptionCallback>{};
+ final Map<Object, _DynamicNode> _children = <Object, _DynamicNode>{};
+
+ bool get isObsolete => _callbacks.isEmpty && _children.isEmpty;
+
+ static final Set<_DynamicNode> _obsoleteNodes = <_DynamicNode>{};
+
+ /// Allow garbage collection to collect unused nodes.
+ ///
+ /// When a node has no subscribers, it is no longer needed (it can be
+ /// recreated if necessary from the raw data). In that situation, the node
+ /// adds itself to a list of "obsolete nodes", but the parent still references
+ /// it and therefore garbage collection would not notice that it is no longer
+ /// used.
+ ///
+ /// This method solves this problem by disconnecting obsolete nodes from the
+ /// tree.
+ static void cleanup() {
+ while (_obsoleteNodes.isNotEmpty) {
+ final _DynamicNode node = _obsoleteNodes.first;
+ _obsoleteNodes.remove(node);
+ if (node.isObsolete) {
+ node._parent?._forget(node._key, node);
+ }
+ }
+ }
+
+ void _forget(Object childKey, _DynamicNode child) {
+ assert(_children[childKey] == child);
+ _children.remove(childKey);
+ if (isObsolete) {
+ _obsoleteNodes.add(this);
+ }
+ }
+
+ static bool _hasValidType(Object? value) {
+ if (value is DynamicMap) {
+ return value.values.every(_hasValidType);
+ }
+ if (value is DynamicList) {
+ return value.every(_hasValidType);
+ }
+ return value is int
+ || value is double
+ || value is bool
+ || value is String;
+ }
+
+ _DynamicNode _prepare(Object childKey) {
+ assert(childKey is String || childKey is int);
+ if (!_children.containsKey(childKey)) {
+ Object value;
+ if (_value is DynamicMap) {
+ if (childKey is String && (_value as DynamicMap).containsKey(childKey)) {
+ value = (_value as DynamicMap)[childKey]!;
+ } else {
+ value = missing;
+ }
+ } else if (_value is DynamicList) {
+ if (childKey is int && childKey >= 0 && childKey < (_value as DynamicList).length) {
+ value = (_value as DynamicList)[childKey]!;
+ } else {
+ value = missing;
+ }
+ } else {
+ value = _value;
+ }
+ _children[childKey] = _DynamicNode(childKey, this, value);
+ }
+ return _children[childKey]!;
+ }
+
+ Object subscribe(List<Object> key, int index, SubscriptionCallback callback) {
+ _obsoleteNodes.remove(this);
+ if (index == key.length) {
+ assert(!_callbacks.contains(callback));
+ _callbacks.add(callback);
+ return _value;
+ }
+ final _DynamicNode child = _prepare(key[index]);
+ return child.subscribe(key, index + 1, callback);
+ }
+
+ void unsubscribe(List<Object> key, int index, SubscriptionCallback callback) {
+ if (index == key.length) {
+ assert(_callbacks.contains(callback));
+ _callbacks.remove(callback);
+ if (_callbacks.isEmpty) {
+ _obsoleteNodes.add(this);
+ }
+ } else {
+ assert(_children.containsKey(key[index]));
+ _children[key[index]]!.unsubscribe(key, index + 1, callback);
+ }
+ }
+
+ void update(Object value) {
+ assert(value == missing || _hasValidType(value), 'cannot update $this using $value');
+ if (value == _value) {
+ return;
+ }
+ _value = value;
+ if (value is DynamicMap) {
+ for (final Object childKey in _children.keys) {
+ if (childKey is String && value.containsKey(childKey)) {
+ _children[childKey]!.update(value[childKey]!);
+ } else {
+ _children[childKey]!.update(missing);
+ }
+ }
+ } else if (value is DynamicList) {
+ for (final Object childKey in _children.keys) {
+ if (childKey is int && childKey >= 0 && childKey < value.length) {
+ _children[childKey]!.update(value[childKey]!);
+ } else {
+ _children[childKey]!.update(missing);
+ }
+ }
+ } else {
+ for (final _DynamicNode child in _children.values) {
+ child.update(missing);
+ }
+ }
+ _sendUpdates(value);
+ }
+
+ void _sendUpdates(Object value) {
+ for (final SubscriptionCallback callback in _callbacks) {
+ callback(value);
+ }
+ }
+
+ void updateKey(String rootKey, Object value) {
+ assert(_value is DynamicMap);
+ assert(_hasValidType(value));
+ if ((_value as DynamicMap)[rootKey] == value) {
+ return;
+ }
+ (_value as DynamicMap)[rootKey] = value;
+ if (_children.containsKey(rootKey)) {
+ _children[rootKey]!.update(value);
+ }
+ }
+
+ @override
+ String toString() => '$_value';
+}
diff --git a/packages/rfw/lib/flutter/core_widgets.dart b/packages/rfw/lib/flutter/core_widgets.dart
new file mode 100644
index 0000000..4863323
--- /dev/null
+++ b/packages/rfw/lib/flutter/core_widgets.dart
@@ -0,0 +1,659 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// There's a lot of <Object>[] lists in this file so to avoid making this
+// file even less readable we relax our usual stance on verbose typing.
+// ignore_for_file: always_specify_types
+
+// This file is hand-formatted.
+
+import 'dart:ui' show FontFeature;
+
+import 'package:flutter/gestures.dart' show DragStartBehavior;
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+
+import 'argument_decoders.dart';
+import 'runtime.dart';
+
+/// A widget library for Remote Flutter Widgets that defines widgets that are
+/// implemented on the client in terms of Flutter widgets from the `widgets`
+/// Dart library.
+///
+/// The following widgets are implemented:
+///
+/// * [Align]
+/// * [AspectRatio]
+/// * [Center]
+/// * [ColoredBox]
+/// * [Column]
+/// * [Container] (actually uses [AnimatedContainer])
+/// * [DefaultTextStyle]
+/// * [Directionality]
+/// * [Expanded]
+/// * [FittedBox]
+/// * [GestureDetector]
+/// * [GridView] (actually uses [GridView.builder])
+/// * [Icon]
+/// * [IconTheme]
+/// * [IntrinsicHeight]
+/// * [IntrinsicWidth]
+/// * [Image] (see below)
+/// * [ListBody]
+/// * [ListView] (actually uses [ListView.builder])
+/// * [Opacity] (actually uses [AnimatedOpacity])
+/// * [Padding] (actually uses [AnimatedPadding])
+/// * [Placeholder]
+/// * [Positioned] (actually uses [AnimatedPositionedDirectional])
+/// * [Rotation] (actually uses [AnimatedRotation])
+/// * [Row]
+/// * [SafeArea]
+/// * [Scale] (actually uses [AnimatedScale])
+/// * [SingleChildScrollView]
+/// * [SizedBox]
+/// * `SizedBoxExpand` (actually [SizedBox.expand])
+/// * `SizedBoxShrink` (actually [SizedBox.shrink])
+/// * [Spacer]
+/// * [Stack]
+/// * [Text]
+/// * [Wrap]
+///
+/// For each, every parameter is implemented using the same name. Parameters
+/// that take structured types are represented using maps, with each named
+/// parameter of that type's default constructor represented by a key, with the
+/// following notable caveats and exceptions:
+///
+/// * Enums are represented as strings with the unqualified name of the value.
+/// For example, [MainAxisAlignment.start] is represented as the string
+/// `"start"`.
+///
+/// * Types that have multiple subclasses (or multiple very unrelated
+/// constructors, like [ColorFilter]) are represented as maps where the `type`
+/// key specifies the type. Typically these have an extension mechanism.
+///
+/// * Matrices are represented as **column-major** flattened arrays. [Matrix4]
+/// values must have exactly 16 doubles in the array.
+///
+/// * [AlignmentGeometry] values can be represented either as `{x: ..., y:
+/// ...}` for a non-directional variant or `{start: ..., y: ...}` for a
+/// directional variant.
+///
+/// * [BoxBorder] instances are defined as arrays of [BorderSide] maps. If the
+/// array has length 1, then that value is used for all four sides. Two
+/// values become the horizontal and vertical sides respectively. Three
+/// values become the start, top-and-bottom, and end respectively. Four
+/// values become the start, top, end, and bottom respectively.
+///
+/// * [BorderRadiusGeometry] values work similarly to [BoxBorder], as an array
+/// of [Radius] values. If the array has one value, it's used for all corners.
+/// With two values, the first becomes the `topStart` and `bottomStart`
+/// corners and the second the `topEnd` and `bottomEnd`. With three, the
+/// values are used for `topStart`, `topEnd`-and-`bottomEnd`, and
+/// `bottomStart` respectively. Four values map to the `topStart`, `topEnd`,
+/// `bottomStart`, and `bottomEnd` respectively.
+///
+/// * [Color] values are represented as integers. The hex literal values are
+/// most convenient for this, the alpha, red, green, and blue channels map to
+/// the 32 bit hex integer as 0xAARRGGBB.
+///
+/// * [ColorFilter] is represented as a map with a `type` key that matches the
+/// constructor name (e.g. `linearToSrgbGamma`). The `matrix` version uses
+/// the `matrix` key for the matrix, expecting a 20-value array. The `mode`
+/// version expects a `color` key for the color (defaults to black) and a
+/// `blendMode` key for the blend mode (defaults to [BlendMode.srcOver]).
+/// Other types are looked up in [ArgumentDecoders.colorFilterDecoders].
+///
+/// * [Curve] values are represented as a string giving the kind of curve from
+/// the predefined [Curves], e.g. `easeInOutCubicEmphasized`. More types may
+/// be added using [ArgumentDecoders.curveDecoders].
+///
+/// * The types supported for [Decoration] are `box` for [BoxDecoration],
+/// `flutterLogo` for [FlutterLogoDecoration], and `shape` for
+/// [ShapeDecoration]. More types can be added with [decorationDecoders].
+///
+/// * [DecorationImage] expects a `source` key that gives either an absolute
+/// URL (to use a [NetworkImage]) or the name of an asset in the client
+/// binary (to use [AssetImage]). In the case of a URL, the `scale` key gives
+/// the scale to pass to the [NetworkImage] constructor.
+/// [DecorationImage.onError] is supported as an event handler with arguments
+/// giving the stringified exception and stack trace. Values can be added to
+/// [ArgumentDecoders.imageProviderDecoders] to override the behavior described here.
+///
+/// * [Duration] is represented by an integer giving milliseconds.
+///
+/// * [EdgeInsetsGeometry] values work like [BoxBorder], with each value in the
+/// array being a double rather than a map.
+///
+/// * [FontFeature] values are a map with a `feature` key and a `value` key.
+/// The `value` defaults to 1. (Technically the `feature` defaults to `NONE`,
+/// too, but that's hardly useful.)
+///
+/// * The [dart:ui.Gradient] and [painting.Gradient] types are both represented
+/// as a map with a type that is either `linear` (for [LinearGradient]),
+/// `radial` (for [RadialGradient]), or `sweep` (for [SweepGradient]), using
+/// the conventions from the [painting.Gradient] version. The `transform`
+/// property on these objects is not currently supported. New gradient types
+/// can be implemented using [ArgumentDecoders.gradientDecoders].
+///
+/// * The [GridDelegate] type is represented as a map with a `type` key that is
+/// either `fixedCrossAxisCount` for
+/// [SliverGridDelegateWithFixedCrossAxisCount] or `maxCrossAxisExtent` for
+/// [SliverGridDelegateWithMaxCrossAxisExtent]. New delegate types can be
+/// supported using [ArgumentDecoders.gridDelegateDecoders].
+///
+/// * [IconData] is represented as a map with an `icon` key giving the
+/// [IconData.codePoint] (and corresponding keys for the other parameters of
+/// the [IconData] constructor). To determine the values to use for icons in
+/// the MaterialIcons font, see how the icons are defined in [Icons]. For
+/// example, [Icons.flutter_dash] is `IconData(0xe2a0, fontFamily:
+/// 'MaterialIcons')` so it would be represented here as `{ icon: 0xE2A0,
+/// fontFamily: "MaterialIcons" }`. (The client must have the font as a
+/// defined asset.)
+///
+/// * [Locale] values are defined as a string in the form `languageCode`,
+/// `languageCode-countryCode`, or
+/// `languageCode-scriptCode-countryCode-ignoredSubtags`. The string is split
+/// on hyphens.
+///
+/// * [MaskFilter] is represented as a map with a `type` key that must be
+/// `blur`; only [MaskFilter.blur] is supported. (The other keys must be
+/// `style`, the [BlurStyle], and `sigma`.)
+///
+/// * [Offset]s are a map with an `x` key and a `y` key.
+///
+/// * [Paint] objects are represented as maps; each property of [Paint] is a
+/// key as if there was a constructor that could set all of [Paint]'s
+/// properties with named parameters. In principle all properties are
+/// supported, though since [Paint] is only used as part of
+/// [painting.TextStyle.background] and [painting.TextStyle.foreground], in
+/// practice some of the properties are ignored since they would be no-ops
+/// (e.g. `invertColors`).
+///
+/// * [Radius] is represented as a map with an `x` value and optionally a `y`
+/// value; if the `y` value is absent, the `x` value is used for both.
+///
+/// * [Rect] values are represented as an array with four doubles, giving the
+/// x, y, width, and height respectively.
+///
+/// * [ShapeBorder] values are represented as either maps with a `type` _or_ as
+/// an array of [ShapeBorder] values. In the array case, the values are
+/// reduced together using [ShapeBorder.+]. When represented as maps, the
+/// type must be one of `box` ([BoxBorder]), `beveled`
+/// ([BeveledRectangleBorder]), `circle` ([CircleBorder]), `continuous`
+/// ([ContinuousRectangleBorder]), `rounded` ([RoundedRectangleBorder]), or
+/// `stadium` ([StadiumBorder]). In the case of `box`, there must be a
+/// `sides` key whose value is an array that is interpreted as per
+/// [BoxBorder] above. Support for new types can be added using the
+/// [ArgumentDecoders.shapeBorderDecoders] map.
+///
+/// * [Shader] values are a map with a `type` that is either `linear`,
+/// `radial`, or `sweep`; in each case, the data is interpreted as per the
+/// [Gradient] case above, except that the gradient is specifically applied
+/// to a [Rect] given by the `rect` key and a [TextDirection] given by the
+/// `textDirection` key. New shader types can be added using
+/// [ArgumentDecoders.shaderDecoders].
+///
+/// * [TextDecoration] is represented either as an array of [TextDecoration]
+/// values (combined via [TextDecoration.combine]) or a string which matches
+/// the name of one of the [TextDecoration] constants (e.g. `underline`).
+///
+/// * [VisualDensity] is either represented as a string which matches one of the
+/// predefined values (`adaptivePlatformDensity`, `comfortable`, etc), or as
+/// a map with keys `horizontal` and `vertical` to define a custom density.
+///
+/// Some of the widgets have special considerations:
+///
+/// * [Image] does not support the builder callbacks or the [Image.opacity]
+/// parameter (because builders are code and code can't be represented in RFW
+/// arguments). The map should have a `source` key that is interpreted as
+/// described above for [DecorationImage]. If the `source` is omitted, an
+/// [AssetImage] with the name `error.png` is used instead (which will likely
+/// fail unless such an asset is declared in the client).
+///
+/// * Parameters of type [ScrollController] and [ScrollPhysics] are not
+/// supported, because they can't really be exposed to declarative code (they
+/// expect to be configured using code that implements delegates or that
+/// interacts with controllers).
+///
+/// * The [Text] widget's first argument, the string, is represented using the
+/// key `text`, which must be either a string or an array of strings to be
+/// concatenated.
+///
+/// One additional widget is defined, [AnimationDefaults]. It has a `duration`
+/// argument and `curve` argument. It sets the default animation duration and
+/// curve for widgets in the library that use the animated variants. If absent,
+/// a default of 200ms and [Curves.fastOutSlowIn] is used.
+LocalWidgetLibrary createCoreWidgets() => LocalWidgetLibrary(_coreWidgetsDefinitions);
+
+// In these widgets we make an effort to expose every single argument available.
+Map<String, LocalWidgetBuilder> get _coreWidgetsDefinitions => <String, LocalWidgetBuilder>{
+
+ // Keep these in alphabetical order.
+
+ 'AnimationDefaults': (BuildContext context, DataSource source) {
+ return AnimationDefaults(
+ duration: ArgumentDecoders.duration(source, ['duration'], context),
+ curve: ArgumentDecoders.curve(source, ['curve'], context),
+ child: source.child(['child']),
+ );
+ },
+
+ 'Align': (BuildContext context, DataSource source) {
+ return AnimatedAlign(
+ duration: ArgumentDecoders.duration(source, ['duration'], context),
+ curve: ArgumentDecoders.curve(source, ['curve'], context),
+ alignment: ArgumentDecoders.alignment(source, ['alignment']) ?? Alignment.center,
+ widthFactor: source.v<double>(['widthFactor']),
+ heightFactor: source.v<double>(['heightFactor']),
+ child: source.optionalChild(['child']),
+ onEnd: source.voidHandler(['onEnd']),
+ );
+ },
+
+ 'AspectRatio': (BuildContext context, DataSource source) {
+ return AspectRatio(
+ aspectRatio: source.v<double>(['aspectRatio']) ?? 1.0,
+ child: source.optionalChild(['child']),
+ );
+ },
+
+ 'Center': (BuildContext context, DataSource source) {
+ return Center(
+ widthFactor: source.v<double>(['widthFactor']),
+ heightFactor: source.v<double>(['heightFactor']),
+ child: source.optionalChild(['child']),
+ );
+ },
+
+ 'ColoredBox': (BuildContext context, DataSource source) {
+ return ColoredBox(
+ color: ArgumentDecoders.color(source, ['color']) ?? const Color(0xFF000000),
+ child: source.optionalChild(['child']),
+ );
+ },
+
+ 'Column': (BuildContext context, DataSource source) {
+ return Column(
+ mainAxisAlignment: ArgumentDecoders.enumValue<MainAxisAlignment>(MainAxisAlignment.values, source, ['mainAxisAlignment']) ?? MainAxisAlignment.start,
+ mainAxisSize: ArgumentDecoders.enumValue<MainAxisSize>(MainAxisSize.values, source, ['mainAxisSize']) ?? MainAxisSize.max,
+ crossAxisAlignment: ArgumentDecoders.enumValue<CrossAxisAlignment>(CrossAxisAlignment.values, source, ['crossAxisAlignment']) ?? CrossAxisAlignment.center,
+ textDirection: ArgumentDecoders.enumValue<TextDirection>(TextDirection.values, source, ['textDirection']),
+ verticalDirection: ArgumentDecoders.enumValue<VerticalDirection>(VerticalDirection.values, source, ['verticalDirection']) ?? VerticalDirection.down,
+ textBaseline: ArgumentDecoders.enumValue<TextBaseline>(TextBaseline.values, source, ['textBaseline']),
+ children: source.childList(['children']),
+ );
+ },
+
+ 'Container': (BuildContext context, DataSource source) {
+ return AnimatedContainer(
+ duration: ArgumentDecoders.duration(source, ['duration'], context),
+ curve: ArgumentDecoders.curve(source, ['curve'], context),
+ alignment: ArgumentDecoders.alignment(source, ['alignment']),
+ padding: ArgumentDecoders.edgeInsets(source, ['padding']),
+ color: ArgumentDecoders.color(source, ['color']),
+ decoration: ArgumentDecoders.decoration(source, ['decoration']),
+ foregroundDecoration: ArgumentDecoders.decoration(source, ['foregroundDecoration']),
+ width: source.v<double>(['width']),
+ height: source.v<double>(['height']),
+ constraints: ArgumentDecoders.boxConstraints(source, ['constraints']),
+ margin: ArgumentDecoders.edgeInsets(source, ['margin']),
+ transform: ArgumentDecoders.matrix(source, ['transform']),
+ transformAlignment: ArgumentDecoders.alignment(source, ['transformAlignment']),
+ child: source.optionalChild(['child']),
+ clipBehavior: ArgumentDecoders.enumValue<Clip>(Clip.values, source, ['clipBehavior']) ?? Clip.none,
+ onEnd: source.voidHandler(['onEnd']),
+ );
+ },
+
+ 'DefaultTextStyle': (BuildContext context, DataSource source) {
+ return AnimatedDefaultTextStyle(
+ duration: ArgumentDecoders.duration(source, ['duration'], context),
+ curve: ArgumentDecoders.curve(source, ['curve'], context),
+ style: ArgumentDecoders.textStyle(source, ['style']) ?? const TextStyle(),
+ textAlign: ArgumentDecoders.enumValue<TextAlign>(TextAlign.values, source, ['textAlign']),
+ softWrap: source.v<bool>(['softWrap']) ?? true,
+ overflow: ArgumentDecoders.enumValue<TextOverflow>(TextOverflow.values, source, ['overflow']) ?? TextOverflow.clip,
+ maxLines: source.v<int>(['maxLines']),
+ textWidthBasis: ArgumentDecoders.enumValue<TextWidthBasis>(TextWidthBasis.values, source, ['textWidthBasis']) ?? TextWidthBasis.parent,
+ textHeightBehavior: ArgumentDecoders.textHeightBehavior(source, ['textHeightBehavior']),
+ child: source.child(['child']),
+ onEnd: source.voidHandler(['onEnd']),
+ );
+ },
+
+ 'Directionality': (BuildContext context, DataSource source) {
+ return Directionality(
+ textDirection: ArgumentDecoders.enumValue<TextDirection>(TextDirection.values, source, ['textDirection']) ?? TextDirection.ltr,
+ child: source.child(['child']),
+ );
+ },
+
+ 'Expanded': (BuildContext context, DataSource source) {
+ return Expanded(
+ flex: source.v<int>(['flex']) ?? 1,
+ child: source.child(['child']),
+ );
+ },
+
+ 'FittedBox': (BuildContext context, DataSource source) {
+ return FittedBox(
+ fit: ArgumentDecoders.enumValue<BoxFit>(BoxFit.values, source, ['fit']) ?? BoxFit.contain,
+ alignment: ArgumentDecoders.alignment(source, ['alignment']) ?? Alignment.center,
+ clipBehavior: ArgumentDecoders.enumValue<Clip>(Clip.values, source, ['clipBehavior']) ?? Clip.none,
+ child: source.optionalChild(['child']),
+ );
+ },
+
+ 'GestureDetector': (BuildContext context, DataSource source) {
+ return GestureDetector(
+ onTap: source.voidHandler(['onTap']),
+ onTapDown: source.handler(['onTapDown'], (VoidCallback trigger) => (TapDownDetails details) => trigger()),
+ onTapUp: source.handler(['onTapUp'], (VoidCallback trigger) => (TapUpDetails details) => trigger()),
+ onTapCancel: source.voidHandler(['onTapCancel']),
+ onDoubleTap: source.voidHandler(['onDoubleTap']),
+ onLongPress: source.voidHandler(['onLongPress']),
+ behavior: ArgumentDecoders.enumValue<HitTestBehavior>(HitTestBehavior.values, source, ['behavior']),
+ child: source.optionalChild(['child']),
+ );
+ },
+
+ 'GridView': (BuildContext context, DataSource source) {
+ return GridView.builder(
+ scrollDirection: ArgumentDecoders.enumValue<Axis>(Axis.values, source, ['scrollDirection']) ?? Axis.vertical,
+ reverse: source.v<bool>(['reverse']) ?? false,
+ // controller,
+ primary: source.v<bool>(['primary']),
+ // physics,
+ shrinkWrap: source.v<bool>(['shrinkWrap']) ?? false,
+ padding: ArgumentDecoders.edgeInsets(source, ['padding']),
+ gridDelegate: ArgumentDecoders.gridDelegate(source, ['gridDelegate']) ?? const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
+ itemBuilder: (BuildContext context, int index) => source.child(['children', index]),
+ itemCount: source.length(['children']),
+ addAutomaticKeepAlives: source.v<bool>(['addAutomaticKeepAlives']) ?? true,
+ addRepaintBoundaries: source.v<bool>(['addRepaintBoundaries']) ?? true,
+ addSemanticIndexes: source.v<bool>(['addSemanticIndexes']) ?? true,
+ cacheExtent: source.v<double>(['cacheExtent']),
+ semanticChildCount: source.v<int>(['semanticChildCount']),
+ dragStartBehavior: ArgumentDecoders.enumValue<DragStartBehavior>(DragStartBehavior.values, source, ['dragStartBehavior']) ?? DragStartBehavior.start,
+ keyboardDismissBehavior: ArgumentDecoders.enumValue<ScrollViewKeyboardDismissBehavior>(ScrollViewKeyboardDismissBehavior.values, source, ['keyboardDismissBehavior']) ?? ScrollViewKeyboardDismissBehavior.manual,
+ restorationId: source.v<String>(['restorationId']),
+ clipBehavior: ArgumentDecoders.enumValue<Clip>(Clip.values, source, ['clipBehavior']) ?? Clip.hardEdge,
+ );
+ },
+
+ 'Icon': (BuildContext context, DataSource source) {
+ return Icon(
+ ArgumentDecoders.iconData(source, []) ?? Icons.flutter_dash,
+ size: source.v<double>(['size']),
+ color: ArgumentDecoders.color(source, ['color']),
+ semanticLabel: source.v<String>(['semanticLabel']),
+ textDirection: ArgumentDecoders.enumValue<TextDirection>(TextDirection.values, source, ['textDirection']),
+ );
+ },
+
+ 'IconTheme': (BuildContext context, DataSource source) {
+ return IconTheme(
+ data: ArgumentDecoders.iconThemeData(source, []) ?? const IconThemeData(),
+ child: source.child(['child']),
+ );
+ },
+
+ 'IntrinsicHeight': (BuildContext context, DataSource source) {
+ return IntrinsicHeight(
+ child: source.optionalChild(['child']),
+ );
+ },
+
+ 'IntrinsicWidth': (BuildContext context, DataSource source) {
+ return IntrinsicWidth(
+ stepWidth: source.v<double>(['width']),
+ stepHeight: source.v<double>(['height']),
+ child: source.optionalChild(['child']),
+ );
+ },
+
+ 'Image': (BuildContext context, DataSource source) {
+ return Image(
+ image: ArgumentDecoders.imageProvider(source, []) ?? const AssetImage('error.png'),
+ // ImageFrameBuilder? frameBuilder,
+ // ImageLoadingBuilder? loadingBuilder,
+ // ImageErrorWidgetBuilder? errorBuilder,
+ semanticLabel: source.v<String>(['semanticLabel']),
+ excludeFromSemantics: source.v<bool>(['excludeFromSemantics']) ?? false,
+ width: source.v<double>(['width']),
+ height: source.v<double>(['height']),
+ color: ArgumentDecoders.color(source, ['color']),
+ // Animation<double>? opacity,
+ colorBlendMode: ArgumentDecoders.enumValue<BlendMode>(BlendMode.values, source, ['blendMode']),
+ fit: ArgumentDecoders.enumValue<BoxFit>(BoxFit.values, source, ['fit']),
+ alignment: ArgumentDecoders.alignment(source, ['alignment']) ?? Alignment.center,
+ repeat: ArgumentDecoders.enumValue<ImageRepeat>(ImageRepeat.values, source, ['repeat']) ?? ImageRepeat.noRepeat,
+ centerSlice: ArgumentDecoders.rect(source, ['centerSlice']),
+ matchTextDirection: source.v<bool>(['matchTextDirection']) ?? false,
+ gaplessPlayback: source.v<bool>(['gaplessPlayback']) ?? false,
+ isAntiAlias: source.v<bool>(['isAntiAlias']) ?? false,
+ filterQuality: ArgumentDecoders.enumValue<FilterQuality>(FilterQuality.values, source, ['filterQuality']) ?? FilterQuality.low,
+ );
+ },
+
+ 'ListBody': (BuildContext context, DataSource source) {
+ return ListBody(
+ mainAxis: ArgumentDecoders.enumValue<Axis>(Axis.values, source, ['mainAxis']) ?? Axis.vertical,
+ reverse: source.v<bool>(['reverse']) ?? false,
+ children: source.childList(['children']),
+ );
+ },
+
+ 'ListView': (BuildContext context, DataSource source) {
+ return ListView.builder(
+ scrollDirection: ArgumentDecoders.enumValue<Axis>(Axis.values, source, ['scrollDirection']) ?? Axis.vertical,
+ reverse: source.v<bool>(['reverse']) ?? false,
+ // ScrollController? controller,
+ primary: source.v<bool>(['primary']),
+ // ScrollPhysics? physics,
+ shrinkWrap: source.v<bool>(['shrinkWrap']) ?? false,
+ padding: ArgumentDecoders.edgeInsets(source, ['padding']),
+ itemExtent: source.v<double>(['itemExtent']),
+ prototypeItem: source.optionalChild(['prototypeItem']),
+ itemCount: source.length(['children']),
+ itemBuilder: (BuildContext context, int index) => source.child(['children', index]),
+ clipBehavior: ArgumentDecoders.enumValue<Clip>(Clip.values, source, ['clipBehavior']) ?? Clip.hardEdge,
+ addAutomaticKeepAlives: source.v<bool>(['addAutomaticKeepAlives']) ?? true,
+ addRepaintBoundaries: source.v<bool>(['addRepaintBoundaries']) ?? true,
+ addSemanticIndexes: source.v<bool>(['addSemanticIndexes']) ?? true,
+ cacheExtent: source.v<double>(['cacheExtent']),
+ semanticChildCount: source.v<int>(['semanticChildCount']),
+ dragStartBehavior: ArgumentDecoders.enumValue<DragStartBehavior>(DragStartBehavior.values, source, ['dragStartBehavior']) ?? DragStartBehavior.start,
+ keyboardDismissBehavior: ArgumentDecoders.enumValue<ScrollViewKeyboardDismissBehavior>(ScrollViewKeyboardDismissBehavior.values, source, ['keyboardDismissBehavior']) ?? ScrollViewKeyboardDismissBehavior.manual,
+ restorationId: source.v<String>(['restorationId']),
+ );
+ },
+
+ 'Opacity': (BuildContext context, DataSource source) {
+ return AnimatedOpacity(
+ duration: ArgumentDecoders.duration(source, ['duration'], context),
+ curve: ArgumentDecoders.curve(source, ['curve'], context),
+ opacity: source.v<double>(['opacity']) ?? 0.0,
+ onEnd: source.voidHandler(['onEnd']),
+ alwaysIncludeSemantics: source.v<bool>(['alwaysIncludeSemantics']) ?? true,
+ );
+ },
+
+ 'Padding': (BuildContext context, DataSource source) {
+ return AnimatedPadding(
+ duration: ArgumentDecoders.duration(source, ['duration'], context),
+ curve: ArgumentDecoders.curve(source, ['curve'], context),
+ padding: ArgumentDecoders.edgeInsets(source, ['padding']) ?? EdgeInsets.zero,
+ child: source.optionalChild(['child']),
+ onEnd: source.voidHandler(['onEnd']),
+ );
+ },
+
+ 'Placeholder': (BuildContext context, DataSource source) {
+ return Placeholder(
+ color: ArgumentDecoders.color(source, ['color']) ?? const Color(0xFF455A64),
+ strokeWidth: source.v<double>(['strokeWidth']) ?? 2.0,
+ fallbackWidth: source.v<double>(['placeholderWidth']) ?? 400.0,
+ fallbackHeight: source.v<double>(['placeholderHeight']) ?? 400.0,
+ );
+ },
+
+ 'Positioned': (BuildContext context, DataSource source) {
+ return AnimatedPositionedDirectional(
+ duration: ArgumentDecoders.duration(source, ['duration'], context),
+ curve: ArgumentDecoders.curve(source, ['curve'], context),
+ start: source.v<double>(['start']),
+ top: source.v<double>(['top']),
+ end: source.v<double>(['end']),
+ bottom: source.v<double>(['bottom']),
+ width: source.v<double>(['width']),
+ height: source.v<double>(['height']),
+ child: source.child(['child']),
+ onEnd: source.voidHandler(['onEnd']),
+ );
+ },
+
+ 'Rotation': (BuildContext context, DataSource source) {
+ return AnimatedRotation(
+ duration: ArgumentDecoders.duration(source, ['duration'], context),
+ curve: ArgumentDecoders.curve(source, ['curve'], context),
+ turns: source.v<double>(['turns']) ?? 0.0,
+ alignment: (ArgumentDecoders.alignment(source, ['alignment']) ?? Alignment.center).resolve(Directionality.of(context)),
+ filterQuality: ArgumentDecoders.enumValue<FilterQuality>(FilterQuality.values, source, ['filterQuality']),
+ child: source.optionalChild(['child']),
+ onEnd: source.voidHandler(['onEnd']),
+ );
+ },
+
+ 'Row': (BuildContext context, DataSource source) {
+ return Row(
+ mainAxisAlignment: ArgumentDecoders.enumValue<MainAxisAlignment>(MainAxisAlignment.values, source, ['mainAxisAlignment']) ?? MainAxisAlignment.start,
+ mainAxisSize: ArgumentDecoders.enumValue<MainAxisSize>(MainAxisSize.values, source, ['mainAxisSize']) ?? MainAxisSize.max,
+ crossAxisAlignment: ArgumentDecoders.enumValue<CrossAxisAlignment>(CrossAxisAlignment.values, source, ['crossAxisAlignment']) ?? CrossAxisAlignment.center,
+ textDirection: ArgumentDecoders.enumValue<TextDirection>(TextDirection.values, source, ['textDirection']),
+ verticalDirection: ArgumentDecoders.enumValue<VerticalDirection>(VerticalDirection.values, source, ['verticalDirection']) ?? VerticalDirection.down,
+ textBaseline: ArgumentDecoders.enumValue<TextBaseline>(TextBaseline.values, source, ['textBaseline']),
+ children: source.childList(['children']),
+ );
+ },
+
+ 'SafeArea': (BuildContext context, DataSource source) {
+ return SafeArea(
+ left: source.v<bool>(['left']) ?? true,
+ top: source.v<bool>(['top']) ?? true,
+ right: source.v<bool>(['right']) ?? true,
+ bottom: source.v<bool>(['bottom']) ?? true,
+ minimum: (ArgumentDecoders.edgeInsets(source, ['minimum']) ?? EdgeInsets.zero).resolve(Directionality.of(context)),
+ maintainBottomViewPadding: source.v<bool>(['maintainBottomViewPadding']) ?? false,
+ child: source.child(['child']),
+ );
+ },
+
+ 'Scale': (BuildContext context, DataSource source) {
+ return AnimatedScale(
+ duration: ArgumentDecoders.duration(source, ['duration'], context),
+ curve: ArgumentDecoders.curve(source, ['curve'], context),
+ scale: source.v<double>(['scale']) ?? 1.0,
+ alignment: (ArgumentDecoders.alignment(source, ['alignment']) ?? Alignment.center).resolve(Directionality.of(context)),
+ filterQuality: ArgumentDecoders.enumValue<FilterQuality>(FilterQuality.values, source, ['filterQuality']),
+ child: source.optionalChild(['child']),
+ onEnd: source.voidHandler(['onEnd']),
+ );
+ },
+
+ 'SingleChildScrollView': (BuildContext context, DataSource source) {
+ return SingleChildScrollView(
+ scrollDirection: ArgumentDecoders.enumValue<Axis>(Axis.values, source, ['scrollDirection']) ?? Axis.vertical,
+ reverse: source.v<bool>(['reverse']) ?? false,
+ padding: ArgumentDecoders.edgeInsets(source, ['padding']),
+ primary: source.v<bool>(['primary']) ?? true,
+ // ScrollPhysics? physics,
+ // ScrollController? controller,
+ child: source.optionalChild(['child']),
+ dragStartBehavior: ArgumentDecoders.enumValue<DragStartBehavior>(DragStartBehavior.values, source, ['dragStartBehavior']) ?? DragStartBehavior.start,
+ clipBehavior: ArgumentDecoders.enumValue<Clip>(Clip.values, source, ['clipBehavior']) ?? Clip.hardEdge,
+ restorationId: source.v<String>(['restorationId']),
+ keyboardDismissBehavior: ArgumentDecoders.enumValue<ScrollViewKeyboardDismissBehavior>(ScrollViewKeyboardDismissBehavior.values, source, ['keyboardDismissBehavior']) ?? ScrollViewKeyboardDismissBehavior.manual,
+ );
+ },
+
+ 'SizedBox': (BuildContext context, DataSource source) {
+ return SizedBox(
+ width: source.v<double>(['width']),
+ height: source.v<double>(['height']),
+ child: source.optionalChild(['child']),
+ );
+ },
+
+ 'SizedBoxExpand': (BuildContext context, DataSource source) {
+ return SizedBox.expand(
+ child: source.optionalChild(['child']),
+ );
+ },
+
+ 'SizedBoxShrink': (BuildContext context, DataSource source) {
+ return SizedBox.shrink(
+ child: source.optionalChild(['child']),
+ );
+ },
+
+ 'Spacer': (BuildContext context, DataSource source) {
+ return Spacer(
+ flex: source.v<int>(['flex']) ?? 1,
+ );
+ },
+
+ 'Stack': (BuildContext context, DataSource source) {
+ return Stack(
+ alignment: ArgumentDecoders.alignment(source, ['alignment']) ?? AlignmentDirectional.topStart,
+ textDirection: ArgumentDecoders.enumValue<TextDirection>(TextDirection.values, source, ['textDirection']),
+ fit: ArgumentDecoders.enumValue<StackFit>(StackFit.values, source, ['fit']) ?? StackFit.loose,
+ clipBehavior: ArgumentDecoders.enumValue<Clip>(Clip.values, source, ['clipBehavior']) ?? Clip.hardEdge,
+ children: source.childList(['children']),
+ );
+ },
+
+ 'Text': (BuildContext context, DataSource source) {
+ String? text = source.v<String>(['text']);
+ if (text == null) {
+ final StringBuffer builder = StringBuffer();
+ final int count = source.length(['text']);
+ for (int index = 0; index < count; index += 1) {
+ builder.write(source.v<String>(['text', index]) ?? '');
+ }
+ text = builder.toString();
+ }
+ return Text(
+ text,
+ style: ArgumentDecoders.textStyle(source, ['style']),
+ strutStyle: ArgumentDecoders.strutStyle(source, ['strutStyle']),
+ textAlign: ArgumentDecoders.enumValue<TextAlign>(TextAlign.values, source, ['textAlign']),
+ textDirection: ArgumentDecoders.enumValue<TextDirection>(TextDirection.values, source, ['textDirection']),
+ locale: ArgumentDecoders.locale(source, ['locale']),
+ softWrap: source.v<bool>(['softWrap']),
+ overflow: ArgumentDecoders.enumValue<TextOverflow>(TextOverflow.values, source, ['overflow']),
+ textScaleFactor: source.v<double>(['textScaleFactor']),
+ maxLines: source.v<int>(['maxLines']),
+ semanticsLabel: source.v<String>(['semanticsLabel']),
+ textWidthBasis: ArgumentDecoders.enumValue<TextWidthBasis>(TextWidthBasis.values, source, ['textWidthBasis']),
+ textHeightBehavior: ArgumentDecoders.textHeightBehavior(source, ['textHeightBehavior']),
+ );
+ },
+
+ 'Wrap': (BuildContext context, DataSource source) {
+ return Wrap(
+ direction: ArgumentDecoders.enumValue<Axis>(Axis.values, source, ['direction']) ?? Axis.horizontal,
+ alignment: ArgumentDecoders.enumValue<WrapAlignment>(WrapAlignment.values, source, ['alignment']) ?? WrapAlignment.start,
+ spacing: source.v<double>(['spacing']) ?? 0.0,
+ runAlignment: ArgumentDecoders.enumValue<WrapAlignment>(WrapAlignment.values, source, ['runAlignment']) ?? WrapAlignment.start,
+ runSpacing: source.v<double>(['runSpacing']) ?? 0.0,
+ crossAxisAlignment: ArgumentDecoders.enumValue<WrapCrossAlignment>(WrapCrossAlignment.values, source, ['crossAxisAlignment']) ?? WrapCrossAlignment.start,
+ textDirection: ArgumentDecoders.enumValue<TextDirection>(TextDirection.values, source, ['textDirection']),
+ verticalDirection: ArgumentDecoders.enumValue<VerticalDirection>(VerticalDirection.values, source, ['verticalDirection']) ?? VerticalDirection.down,
+ clipBehavior: ArgumentDecoders.enumValue<Clip>(Clip.values, source, ['clipBehavior']) ?? Clip.none,
+ children: source.childList(['children']),
+ );
+ },
+
+};
diff --git a/packages/rfw/lib/flutter/material_widgets.dart b/packages/rfw/lib/flutter/material_widgets.dart
new file mode 100644
index 0000000..c10d9f3
--- /dev/null
+++ b/packages/rfw/lib/flutter/material_widgets.dart
@@ -0,0 +1,350 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// There's a lot of <Object>[] lists in this file so to avoid making this
+// file even less readable we relax our usual stance on verbose typing.
+// ignore_for_file: always_specify_types
+
+// This file is hand-formatted.
+
+import 'package:flutter/gestures.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+
+import 'argument_decoders.dart';
+import 'runtime.dart';
+
+/// A widget library for Remote Flutter Widgets that defines widgets that are
+/// implemented on the client in terms of Flutter widgets from the `material`
+/// Dart library.
+///
+/// The following widgets are implemented:
+///
+/// * [AboutListTile]
+/// * [AppBar]
+/// * [ButtonBar]
+/// * [Card]
+/// * [CircularProgressIndicator]
+/// * [Divider]
+/// * [DrawerHeader]
+/// * [ElevatedButton]
+/// * [FloatingActionButton]
+/// * [InkWell]
+/// * [LinearProgressIndicator]
+/// * [ListTile]
+/// * [OutlinedButton]
+/// * [Scaffold]
+/// * [TextButton]
+/// * [VerticalDivider]
+///
+/// For each, every parameter is implemented using the same name. Parameters
+/// that take structured types are represented using maps, with each named
+/// parameter of that type's default constructor represented by a key. The
+/// conventions edscribed for [createCoreWidgets] are reused here.
+///
+/// In addition, the following conventions are introduced:
+///
+/// * Hero tags are always strings.
+///
+/// * [VisualDensity] is represented in the manner described in the documentation
+/// of the [ArgumentDecoders.visualDensity] method.
+///
+/// Some features are not supported:
+///
+/// * [AppBar]s do not support [AppBar.bottom], [AppBar.flexibleSpace], and
+/// related properties. Also, [AppBar.systemOverlayStyle] is not suported.
+///
+/// * Theming in general is not currently supported.
+///
+/// * Properties whose values are [Animation]s or based on
+/// [MaterialStateProperty] are not supported.
+///
+/// * Features related to focus or configuring mouse support are not
+/// implemented.
+///
+/// * Callbacks such as [Scafford.onDrawerChanged] are not exposed.
+///
+/// * The [Scaffold]'s floating action button position and animation features
+/// are not supported.
+///
+/// In general, the trend will all of these unsupported features is that this
+/// library doesn't support features that can't be trivially expressed using the
+/// JSON-like structures of RFW. For example, [MaterialStateProperty] is
+/// designed to be used with code to select the values, which doesn't work well
+/// in the RFW structure.
+LocalWidgetLibrary createMaterialWidgets() => LocalWidgetLibrary(_materialWidgetsDefinitions);
+
+Map<String, LocalWidgetBuilder> get _materialWidgetsDefinitions => <String, LocalWidgetBuilder>{
+
+ // Keep these in alphabetical order.
+
+ 'AboutListTile': (BuildContext context, DataSource source) {
+ return AboutListTile(
+ icon: source.optionalChild(['icon']),
+ child: source.optionalChild(['child']),
+ applicationName: source.v<String>(['applicationName']),
+ applicationVersion: source.v<String>(['applicationVersion']),
+ applicationIcon: source.optionalChild(['applicationIcon']),
+ applicationLegalese: source.v<String>(['applicationLegalese']),
+ aboutBoxChildren: source.childList(['aboutBoxChildren']),
+ dense: source.v<bool>(['dense']),
+ );
+ },
+
+ 'AppBar': (BuildContext context, DataSource source) {
+ // not implemented: bottom (and bottomOpacity), flexibleSpace; systemOverlayStyle
+ return AppBar(
+ leading: source.optionalChild(['leading']),
+ automaticallyImplyLeading: source.v<bool>(['automaticallyImplyLeading']) ?? true,
+ title: source.optionalChild(['title']),
+ actions: source.childList(['actions']),
+ elevation: source.v<double>(['elevation']),
+ shadowColor: ArgumentDecoders.color(source, ['shadowColor']),
+ shape: ArgumentDecoders.shapeBorder(source, ['shape']),
+ backgroundColor: ArgumentDecoders.color(source, ['backgroundColor']),
+ foregroundColor: ArgumentDecoders.color(source, ['foregroundColor']),
+ iconTheme: ArgumentDecoders.iconThemeData(source, ['iconTheme']),
+ actionsIconTheme: ArgumentDecoders.iconThemeData(source, ['actionsIconTheme']),
+ primary: source.v<bool>(['primary']) ?? true,
+ centerTitle: source.v<bool>(['centerTitle']),
+ excludeHeaderSemantics: source.v<bool>(['excludeHeaderSemantics']) ?? false,
+ titleSpacing: source.v<double>(['titleSpacing']),
+ toolbarOpacity: source.v<double>(['toolbarOpacity']) ?? 1.0,
+ toolbarHeight: source.v<double>(['toolbarHeight']),
+ leadingWidth: source.v<double>(['leadingWidth']),
+ toolbarTextStyle: ArgumentDecoders.textStyle(source, ['toolbarTextStyle']),
+ titleTextStyle: ArgumentDecoders.textStyle(source, ['titleTextStyle']),
+ );
+ },
+
+ 'ButtonBar': (BuildContext context, DataSource source) {
+ // not implemented: buttonTextTheme
+ return ButtonBar(
+ alignment: ArgumentDecoders.enumValue<MainAxisAlignment>(MainAxisAlignment.values, source, ['alignment']) ?? MainAxisAlignment.start,
+ mainAxisSize: ArgumentDecoders.enumValue<MainAxisSize>(MainAxisSize.values, source, ['mainAxisSize']) ?? MainAxisSize.max,
+ buttonMinWidth: source.v<double>(['buttonMinWidth']),
+ buttonHeight: source.v<double>(['buttonHeight']),
+ buttonPadding: ArgumentDecoders.edgeInsets(source, ['buttonPadding']),
+ buttonAlignedDropdown: source.v<bool>(['buttonAlignedDropdown']) ?? false,
+ layoutBehavior: ArgumentDecoders.enumValue<ButtonBarLayoutBehavior>(ButtonBarLayoutBehavior.values, source, ['layoutBehavior']),
+ overflowDirection: ArgumentDecoders.enumValue<VerticalDirection>(VerticalDirection.values, source, ['overflowDirection']),
+ overflowButtonSpacing: source.v<double>(['overflowButtonSpacing']),
+ children: source.childList(['children']),
+ );
+ },
+
+ 'Card': (BuildContext context, DataSource source) {
+ return Card(
+ color: ArgumentDecoders.color(source, ['color']),
+ shadowColor: ArgumentDecoders.color(source, ['shadowColor']),
+ elevation: source.v<double>(['elevation']),
+ shape: ArgumentDecoders.shapeBorder(source, ['shape']),
+ borderOnForeground: source.v<bool>(['borderOnForeground']) ?? true,
+ margin: ArgumentDecoders.edgeInsets(source, ['margin']),
+ clipBehavior: ArgumentDecoders.enumValue<Clip>(Clip.values, source, ['clipBehavior']) ?? Clip.none,
+ child: source.optionalChild(['child']),
+ semanticContainer: source.v<bool>(['semanticContainer']) ?? true,
+ );
+ },
+
+ 'CircularProgressIndicator': (BuildContext context, DataSource source) {
+ // not implemented: valueColor
+ return CircularProgressIndicator(
+ value: source.v<double>(['value']),
+ color: ArgumentDecoders.color(source, ['color']),
+ backgroundColor: ArgumentDecoders.color(source, ['backgroundColor']),
+ strokeWidth: source.v<double>(['strokeWidth']) ?? 4.0,
+ semanticsLabel: source.v<String>(['semanticsLabel']),
+ semanticsValue: source.v<String>(['semanticsValue']),
+ );
+ },
+
+ 'Divider': (BuildContext context, DataSource source) {
+ return Divider(
+ height: source.v<double>(['height']),
+ thickness: source.v<double>(['thickness']),
+ indent: source.v<double>(['indent']),
+ endIndent: source.v<double>(['endIndent']),
+ color: ArgumentDecoders.color(source, ['color']),
+ );
+ },
+
+ 'Drawer': (BuildContext context, DataSource source) {
+ return Drawer(
+ elevation: source.v<double>(['elevation']) ?? 16.0,
+ semanticLabel: source.v<String>(['semanticLabel']),
+ child: source.optionalChild(['child']),
+ );
+ },
+
+ 'DrawerHeader': (BuildContext context, DataSource source) {
+ return DrawerHeader(
+ duration: ArgumentDecoders.duration(source, ['duration'], context),
+ curve: ArgumentDecoders.curve(source, ['curve'], context),
+ decoration: ArgumentDecoders.decoration(source, ['decoration']),
+ margin: ArgumentDecoders.edgeInsets(source, ['margin']) ?? const EdgeInsets.only(bottom: 8.0),
+ padding: ArgumentDecoders.edgeInsets(source, ['padding']) ?? const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 8.0),
+ child: source.optionalChild(['child']),
+ );
+ },
+
+ 'ElevatedButton': (BuildContext context, DataSource source) {
+ // not implemented: buttonStyle, focusNode
+ return ElevatedButton(
+ onPressed: source.voidHandler(['onPressed']),
+ onLongPress: source.voidHandler(['onLongPress']),
+ autofocus: source.v<bool>(['autofocus']) ?? false,
+ clipBehavior: ArgumentDecoders.enumValue<Clip>(Clip.values, source, ['clipBehavior']) ?? Clip.none,
+ child: source.child(['child']),
+ );
+ },
+
+ 'FloatingActionButton': (BuildContext context, DataSource source) {
+ // not implemented: mouseCursor, focusNode
+ return FloatingActionButton(
+ child: source.child(['child']),
+ tooltip: source.v<String>(['tooltip']),
+ foregroundColor: ArgumentDecoders.color(source, ['foregroundColor']),
+ backgroundColor: ArgumentDecoders.color(source, ['backgroundColor']),
+ focusColor: ArgumentDecoders.color(source, ['focusColor']),
+ hoverColor: ArgumentDecoders.color(source, ['hoverColor']),
+ splashColor: ArgumentDecoders.color(source, ['splashColor']),
+ heroTag: source.v<String>(['heroTag']),
+ elevation: source.v<double>(['elevation']),
+ focusElevation: source.v<double>(['focusElevation']),
+ hoverElevation: source.v<double>(['hoverElevation']),
+ highlightElevation: source.v<double>(['highlightElevation']),
+ disabledElevation: source.v<double>(['disabledElevation']),
+ onPressed: source.voidHandler(['onPressed']),
+ mini: source.v<bool>(['mini']) ?? false,
+ shape: ArgumentDecoders.shapeBorder(source, ['shape']),
+ clipBehavior: ArgumentDecoders.enumValue<Clip>(Clip.values, source, ['clipBehavior']) ?? Clip.none,
+ autofocus: source.v<bool>(['autofocus']) ?? false,
+ materialTapTargetSize: ArgumentDecoders.enumValue<MaterialTapTargetSize>(MaterialTapTargetSize.values, source, ['materialTapTargetSize']),
+ isExtended: source.v<bool>(['isExtended']) ?? false,
+ enableFeedback: source.v<bool>(['enableFeedback']),
+ );
+ },
+
+ 'InkWell': (BuildContext context, DataSource source) {
+ // not implemented: onHighlightChanged, onHover; mouseCursor; focusColor, hoverColor, highlightColor, overlayColor, splashColor; splashFactory; focusNode, onFocusChange
+ return InkWell(
+ onTap: source.voidHandler(['onTap']),
+ onDoubleTap: source.voidHandler(['onDoubleTap']),
+ onLongPress: source.voidHandler(['onLongPress']),
+ onTapDown: source.handler(['onTapDown'], (VoidCallback trigger) => (TapDownDetails details) => trigger()),
+ onTapCancel: source.voidHandler(['onTapCancel']),
+ radius: source.v<double>(['radius']),
+ borderRadius: ArgumentDecoders.borderRadius(source, ['borderRadius'])?.resolve(Directionality.of(context)),
+ customBorder: ArgumentDecoders.shapeBorder(source, ['customBorder']),
+ enableFeedback: source.v<bool>(['enableFeedback']) ?? true,
+ excludeFromSemantics: source.v<bool>(['excludeFromSemantics']) ?? false,
+ autofocus: source.v<bool>(['autofocus']) ?? false,
+ child: source.optionalChild(['child']),
+ );
+ },
+
+ 'LinearProgressIndicator': (BuildContext context, DataSource source) {
+ // not implemented: valueColor
+ return LinearProgressIndicator(
+ value: source.v<double>(['value']),
+ color: ArgumentDecoders.color(source, ['color']),
+ backgroundColor: ArgumentDecoders.color(source, ['backgroundColor']),
+ minHeight: source.v<double>(['minHeight']),
+ semanticsLabel: source.v<String>(['semanticsLabel']),
+ semanticsValue: source.v<String>(['semanticsValue']),
+ );
+ },
+
+ 'ListTile': (BuildContext context, DataSource source) {
+ // not implemented: mouseCursor, focusNode
+ return ListTile(
+ leading: source.optionalChild(['leading']),
+ title: source.optionalChild(['title']),
+ subtitle: source.optionalChild(['subtitle']),
+ trailing: source.optionalChild(['trailing']),
+ isThreeLine: source.v<bool>(['isThreeLine']) ?? false,
+ dense: source.v<bool>(['dense']),
+ visualDensity: ArgumentDecoders.visualDensity(source, ['visualDensity']),
+ shape: ArgumentDecoders.shapeBorder(source, ['shape']),
+ contentPadding: ArgumentDecoders.edgeInsets(source, ['contentPadding']),
+ enabled: source.v<bool>(['enabled']) ?? true,
+ onTap: source.voidHandler(['onTap']),
+ onLongPress: source.voidHandler(['onLongPress']),
+ selected: source.v<bool>(['selected']) ?? false,
+ focusColor: ArgumentDecoders.color(source, ['focusColor']),
+ hoverColor: ArgumentDecoders.color(source, ['hoverColor']),
+ autofocus: source.v<bool>(['autofocus']) ?? false,
+ tileColor: ArgumentDecoders.color(source, ['tileColor']),
+ selectedTileColor: ArgumentDecoders.color(source, ['selectedTileColor']),
+ enableFeedback: source.v<bool>(['enableFeedback']),
+ horizontalTitleGap: source.v<double>(['horizontalTitleGap']),
+ minVerticalPadding: source.v<double>(['minVerticalPadding']),
+ minLeadingWidth: source.v<double>(['minLeadingWidth']),
+ );
+ },
+
+ 'OutlinedButton': (BuildContext context, DataSource source) {
+ // not implemented: buttonStyle, focusNode
+ return OutlinedButton(
+ onPressed: source.voidHandler(['onPressed']),
+ onLongPress: source.voidHandler(['onLongPress']),
+ autofocus: source.v<bool>(['autofocus']) ?? false,
+ clipBehavior: ArgumentDecoders.enumValue<Clip>(Clip.values, source, ['clipBehavior']) ?? Clip.none,
+ child: source.child(['child']),
+ );
+ },
+
+ 'Scaffold': (BuildContext context, DataSource source) {
+ // not implemented: floatingActionButtonLocation, floatingActionButtonAnimator; onDrawerChanged, onEndDrawerChanged
+ final Widget? appBarWidget = source.optionalChild(['appBar']);
+ final List<Widget> persistentFooterButtons = source.childList(['persistentFooterButtons']);
+ return Scaffold(
+ appBar: appBarWidget == null ? null : PreferredSize(
+ preferredSize: Size.fromHeight(source.v<double>(['bottomHeight']) ?? 56.0),
+ child: appBarWidget,
+ ),
+ body: source.optionalChild(['body']),
+ floatingActionButton: source.optionalChild(['floatingActionButton']),
+ persistentFooterButtons: persistentFooterButtons.isEmpty ? null : persistentFooterButtons,
+ drawer: source.optionalChild(['drawer']),
+ endDrawer: source.optionalChild(['endDrawer']),
+ bottomNavigationBar: source.optionalChild(['bottomNavigationBar']),
+ bottomSheet: source.optionalChild(['bottomSheet']),
+ backgroundColor: ArgumentDecoders.color(source, ['backgroundColor']),
+ resizeToAvoidBottomInset: source.v<bool>(['resizeToAvoidBottomInset']),
+ primary: source.v<bool>(['primary']) ?? true,
+ drawerDragStartBehavior: ArgumentDecoders.enumValue<DragStartBehavior>(DragStartBehavior.values, source, ['drawerDragStartBehavior']) ?? DragStartBehavior.start,
+ extendBody: source.v<bool>(['extendBody']) ?? false,
+ extendBodyBehindAppBar: source.v<bool>(['extendBodyBehindAppBar']) ?? false,
+ drawerScrimColor: ArgumentDecoders.color(source, ['drawerScrimColor']),
+ drawerEdgeDragWidth: source.v<double>(['drawerEdgeDragWidth']),
+ drawerEnableOpenDragGesture: source.v<bool>(['drawerEnableOpenDragGesture']) ?? true,
+ endDrawerEnableOpenDragGesture: source.v<bool>(['endDrawerEnableOpenDragGesture']) ?? true,
+ restorationId: source.v<String>(['restorationId']),
+ );
+ },
+
+ 'TextButton': (BuildContext context, DataSource source) {
+ // not implemented: buttonStyle, focusNode
+ return TextButton(
+ onPressed: source.voidHandler(['onPressed']),
+ onLongPress: source.voidHandler(['onLongPress']),
+ autofocus: source.v<bool>(['autofocus']) ?? false,
+ clipBehavior: ArgumentDecoders.enumValue<Clip>(Clip.values, source, ['clipBehavior']) ?? Clip.none,
+ child: source.child(['child']),
+ );
+ },
+
+ 'VerticalDivider': (BuildContext context, DataSource source) {
+ return VerticalDivider(
+ width: source.v<double>(['width']),
+ thickness: source.v<double>(['thickness']),
+ indent: source.v<double>(['indent']),
+ endIndent: source.v<double>(['endIndent']),
+ color: ArgumentDecoders.color(source, ['color']),
+ );
+ },
+
+};
diff --git a/packages/rfw/lib/flutter/remote_widget.dart b/packages/rfw/lib/flutter/remote_widget.dart
new file mode 100644
index 0000000..34d05fa
--- /dev/null
+++ b/packages/rfw/lib/flutter/remote_widget.dart
@@ -0,0 +1,99 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is hand-formatted.
+
+import 'package:flutter/widgets.dart';
+
+import '../dart/model.dart';
+import 'content.dart';
+import 'runtime.dart';
+
+export '../dart/model.dart' show DynamicMap, LibraryName;
+
+/// Injection point for a remote widget.
+///
+/// This widget combines an RFW [Runtime] and [DynamicData], inserting a
+/// specified [widget] into the tree.
+class RemoteWidget extends StatefulWidget {
+ /// Inserts the specified [widget] into the tree.
+ ///
+ /// The [onEvent] argument is optional. When omitted, events are discarded.
+ const RemoteWidget({ Key? key, required this.runtime, required this.widget, required this.data, this.onEvent }) : super(key: key);
+
+ /// The [Runtime] to use to render the widget specified by [library] and [name].
+ ///
+ /// This should update rarely (doing so is relatively expensive), but it is
+ /// fine to update it. For example, a client could update this on the fly when
+ /// the server deploys a new version of the widget library.
+ ///
+ /// Frequent updates (e.g. animations) should be done by updating [data] instead.
+ final Runtime runtime;
+
+ /// The name of the widget to display, and the library from which to obtain
+ /// in.
+ ///
+ /// The widget must be either declared in the specified library or one of its
+ /// dependencies.
+ ///
+ /// The data to show in the widget is specified using [data].
+ final FullyQualifiedWidgetName widget;
+
+ /// The data to which the widget specified by [name] will be bound.
+ ///
+ /// This includes data that comes from the application, e.g. a description of
+ /// the user's device, the current time, or an animation controller's value,
+ /// and data that comes from the server, e.g. the contents of the user's
+ /// shopping cart.
+ ///
+ /// This can be updated frequently (once per frame) using
+ /// [DynamicContent.update].
+ final DynamicContent data;
+
+ /// Called when there's an event triggered by a remote widget.
+ ///
+ /// If this is null, events are discarded.
+ final RemoteEventHandler? onEvent;
+
+ @override
+ _RemoteWidgetState createState() => _RemoteWidgetState();
+}
+
+class _RemoteWidgetState extends State<RemoteWidget> {
+ @override
+ void initState() {
+ super.initState();
+ widget.runtime.addListener(_runtimeChanged);
+ }
+
+ @override
+ void didUpdateWidget(RemoteWidget oldWidget) {
+ super.didUpdateWidget(oldWidget);
+ if (oldWidget.runtime != widget.runtime) {
+ oldWidget.runtime.removeListener(_runtimeChanged);
+ widget.runtime.addListener(_runtimeChanged);
+ }
+ }
+
+ @override
+ void dispose() {
+ widget.runtime.removeListener(_runtimeChanged);
+ super.dispose();
+ }
+
+ void _runtimeChanged() {
+ setState(() { /* widget probably changed */ });
+ }
+
+ void _eventHandler(String eventName, DynamicMap eventArguments) {
+ if (widget.onEvent != null) {
+ widget.onEvent!(eventName, eventArguments);
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return widget.runtime.build(context, widget.widget, widget.data, _eventHandler);
+ }
+}
diff --git a/packages/rfw/lib/flutter/runtime.dart b/packages/rfw/lib/flutter/runtime.dart
new file mode 100644
index 0000000..a0e594d
--- /dev/null
+++ b/packages/rfw/lib/flutter/runtime.dart
@@ -0,0 +1,1104 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is hand-formatted.
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/widgets.dart';
+
+import '../dart/model.dart';
+import 'content.dart';
+
+/// Signature of builders for local widgets.
+///
+/// The [LocalWidgetLibrary] class wraps a map of widget names to
+/// [LocalWidgetBuilder] callbacks.
+typedef LocalWidgetBuilder = Widget Function(BuildContext context, DataSource source);
+
+/// Signature of the callback passed to a [RemoteWidget].
+///
+/// This is used by [RemoteWidget] and [Runtime.build] as the callback for
+/// events triggered by remote widgets.
+typedef RemoteEventHandler = void Function(String eventName, DynamicMap eventArguments);
+
+/// Signature of the callback passed to [DataSource.handler].
+///
+/// The callback should return a function of type `T`. That function should call
+/// `trigger`.
+///
+/// See [DataSource.handler] for details.
+typedef HandlerGenerator<T extends Function> = T Function(HandlerTrigger trigger);
+
+/// Signature of the callback passed to a [HandlerGenerator].
+///
+/// See [DataSource.handler] for details.
+typedef HandlerTrigger = void Function([DynamicMap? extraArguments]);
+
+/// Used to indicate that there is an error with one of the libraries loaded
+/// into the Remote Flutter Widgets [Runtime].
+///
+/// For example, a reference to a state variable did not match any actual state
+/// values, or a library import loop.
+class RemoteFlutterWidgetsException implements Exception {
+ /// Creates a [RemoteFlutterWidgetsException].
+ ///
+ /// The message should be a complete sentence, starting with a capital letter
+ /// and ending with a period.
+ const RemoteFlutterWidgetsException(this.message);
+
+ /// A description of the problem that was detected.
+ ///
+ /// This will end with a period.
+ final String message;
+
+ @override
+ String toString() => message;
+}
+
+/// Interface for [LocalWidgetBuilder] to obtain data from arguments.
+///
+/// The interface exposes the [v] method, the argument to which is a list of
+/// keys forming a path to a node in the arguments expected by the widget. If
+/// the method's type argument does not match the value obtained, null is
+/// returned instead.
+///
+/// In addition, to fetch widgets specifically, the [child] and [childList]
+/// methods must be used, and to fetch event handlers, the [handler] method must
+/// be used.
+///
+/// The [isList] and [isMap] methods can be used to avoid inspecting keys that
+/// may not be present (e.g. before reading 15 keys in a map that isn't even
+/// present, consider checking if the map is present using [isMap] and
+/// short-circuiting the key lookups if it is not).
+///
+/// To iterate over a list, the [length] method can be used to find the number
+/// of items in the list.
+abstract class DataSource {
+ /// Return the int, double, bool, or String value at the given path of the
+ /// arguments to the widget.
+ ///
+ /// If `T` is not [Object] and `T` does not match the type of the value
+ /// obtained, then the method returns null.
+ T? v<T>(List<Object> argsKey);
+
+ /// Return true if the given key identifies a list, otherwise false.
+ bool isList(List<Object> argsKey);
+
+ /// Return the length of the list at the given path of the arguments to the
+ /// widget.
+ ///
+ /// If the given path does not identify a list, returns zero.
+ int length(List<Object> argsKey);
+
+ /// Return true if the given key identifies a map, otherwise false.
+ bool isMap(List<Object> argsKey);
+
+ /// Build the child at the given key.
+ ///
+ /// If the node specified is not a widget, returns an [ErrorWidget].
+ ///
+ /// See also:
+ ///
+ /// * [optionalChild], which returns null if the widget is missing.
+ Widget child(List<Object> argsKey);
+
+ /// Build the child at the given key.
+ ///
+ /// If the node specified is not a widget, returns null.
+ ///
+ /// See also:
+ ///
+ /// * [child], which returns an [ErrorWidget] instead of null if the widget
+ /// is missing.
+ Widget? optionalChild(List<Object> argsKey);
+
+ /// Builds the children at the given key.
+ ///
+ /// If the node is missing, returns an empty list.
+ ///
+ /// If the node specified is not a list of widgets, returns a list with the
+ /// non-widget nodes replaced by [ErrorWidget].
+ List<Widget> childList(List<Object> argsKey);
+
+ /// Gets a [VoidCallback] event handler at the given key.
+ ///
+ /// If the node specified is an [AnyEventHandler] or a [DynamicList] of
+ /// [AnyEventHandler]s, returns a callback that invokes the specified event
+ /// handler(s), merging the given `extraArguments` into the arguments
+ /// specified in each event handler. In the event of a key conflict (where
+ /// both the arguments specified in the remote widget declaration and the
+ /// argument provided to this method have the same name), the arguments
+ /// specified here take precedence.
+ VoidCallback? voidHandler(List<Object> argsKey, [ DynamicMap? extraArguments ]);
+
+ /// Gets an event handler at the given key.
+ ///
+ /// The event handler can be of any Function type, as specified by the type
+ /// argument `T`.
+ ///
+ /// When this method is called, the second argument, `generator`, is invoked.
+ /// The `generator` callback must return a function, which we will call
+ /// _entrypoint_, that matches the signature of `T`. The `generator` callback
+ /// receives an argument, which we will call `trigger`. The _entrypoint_
+ /// function must call `trigger`, optionally passing it any extra arguments
+ /// that should be merged into the arguments specified in each event handler.
+ ///
+ /// This is admittedly a little confusing. At its core, the problem is that
+ /// this method cannot itself automatically create a function (_entrypoint_)
+ /// of the right type (`T`), and therefore a callback (`generator`) that knows
+ /// how to wrap a function body (`trigger`) in the right signature (`T`) is
+ /// needed to actually build that function (_entrypoint_).
+ T? handler<T extends Function>(List<Object> argsKey, HandlerGenerator<T> generator);
+}
+
+/// Widgets defined by the client application. All remote widgets eventually
+/// bottom out in these widgets.
+class LocalWidgetLibrary extends WidgetLibrary {
+ /// Create a [LocalWidgetLibrary].
+ ///
+ /// The given map must not change once the object is created.
+ LocalWidgetLibrary(this._widgets);
+
+ final Map<String, LocalWidgetBuilder> _widgets;
+
+ /// Returns the builder for the widget of the given name, if any.
+ @protected
+ LocalWidgetBuilder? findConstructor(String name) {
+ return _widgets[name];
+ }
+}
+
+class _ResolvedConstructor {
+ const _ResolvedConstructor(this.fullName, this.constructor);
+ final FullyQualifiedWidgetName fullName;
+ final Object constructor;
+}
+
+/// The logic that builds and maintains Remote Flutter Widgets.
+///
+/// To declare the libraries of widgets, the [update] method is used.
+///
+/// At least one [LocalWidgetLibrary] instance must be declared
+/// so that [RemoteWidgetLibrary] instances can resolve to real widgets.
+///
+/// The [build] method returns a [Widget] generated from one of the libraries of
+/// widgets added in this manner. Generally, it is simpler to use the
+/// [RemoteWidget] widget (which calls [build]).
+class Runtime extends ChangeNotifier {
+ /// Create a [Runtime] object.
+ ///
+ /// This object should be [dispose]d when it is no longer needed.
+ Runtime();
+
+ final Map<LibraryName, WidgetLibrary> _libraries = <LibraryName, WidgetLibrary>{};
+
+ /// Replace the definitions of the specified library (`name`).
+ ///
+ /// References to widgets that are not defined in the available libraries will
+ /// default to using the [ErrorWidget] widget.
+ ///
+ /// [LocalWidgetLibrary] and [RemoteWidgetLibrary] instances are added using
+ /// this method.
+ ///
+ /// [RemoteWidgetLibrary] instances are typically first obtained using
+ /// [decodeLibraryBlob].
+ ///
+ /// To remove a library, the libraries must be cleared using [clearLibraries]
+ /// and then all the libraries must be readded.
+ void update(LibraryName name, WidgetLibrary library) {
+ _libraries[name] = library;
+ _clearCache();
+ }
+
+ /// Remove all the libraries and start afresh.
+ ///
+ /// Calling this notifies the listeners, which typically causes them to
+ /// rebuild their widgets in the next frame (for example, that is how
+ /// [RemoteWidget] is implemented). If no libraries are readded after calling
+ /// [clearLibraries], and there are any listeners, they will fail to rebuild
+ /// any widgets that they were configured to create. For this reason, this
+ /// call should usually be immediately followed by calls to [update].
+ void clearLibraries() {
+ _libraries.clear();
+ _clearCache();
+ }
+
+ final Map<FullyQualifiedWidgetName, _ResolvedConstructor?> _cachedConstructors = <FullyQualifiedWidgetName, _ResolvedConstructor?>{};
+ final Map<FullyQualifiedWidgetName, _CurriedWidget> _widgets = <FullyQualifiedWidgetName, _CurriedWidget>{};
+
+ void _clearCache() {
+ _cachedConstructors.clear();
+ _widgets.clear();
+ notifyListeners();
+ }
+
+ /// Build the root widget of a Remote Widget subtree.
+ ///
+ /// The widget is identified by a [FullyQualifiedWidgetName], which identifies
+ /// a library and a widget name. The widget does not strictly have to be in
+ /// that library, so long as it is in that library's dependencies.
+ ///
+ /// The data for the widget is given by the `data` argument. That object can
+ /// be updated independently, the widget will rebuild appropriately as it
+ /// changes.
+ ///
+ /// The `remoteEventTarget` argument is the callback to invoke whenever a
+ /// remote widget event handler is triggered.
+ Widget build(BuildContext context, FullyQualifiedWidgetName widget, DynamicContent data, RemoteEventHandler remoteEventTarget) {
+ final _CurriedWidget boundWidget;
+ if (_widgets.containsKey(widget)) {
+ boundWidget = _widgets[widget]!;
+ } else {
+ _checkForImportLoops(widget.library);
+ boundWidget = _applyConstructorAndBindArguments(widget, const <String, Object?>{}, -1, <FullyQualifiedWidgetName>{});
+ _widgets[widget] = boundWidget;
+ }
+ return boundWidget.build(context, data, remoteEventTarget, const <_WidgetState>[]);
+ }
+
+ void _checkForImportLoops(LibraryName name, [ Set<LibraryName>? visited ]) {
+ final WidgetLibrary? library = _libraries[name];
+ if (library is RemoteWidgetLibrary) {
+ visited ??= <LibraryName>{};
+ visited.add(name);
+ for (final Import import in library.imports) {
+ final LibraryName dependency = import.name;
+ if (visited.contains(dependency)) {
+ final List<LibraryName> path = <LibraryName>[dependency];
+ for (final LibraryName name in visited.toList().reversed) {
+ if (name == dependency) {
+ break;
+ }
+ path.add(name);
+ }
+ if (path.length == 1) {
+ assert(path.single == dependency);
+ throw RemoteFlutterWidgetsException('Library $dependency depends on itself.');
+ } else {
+ throw RemoteFlutterWidgetsException('Library $dependency indirectly depends on itself via ${path.reversed.join(" which depends on ")}.');
+ }
+ }
+ _checkForImportLoops(dependency, visited.toSet());
+ }
+ }
+ }
+
+ _ResolvedConstructor? _findConstructor(FullyQualifiedWidgetName fullName) {
+ if (_cachedConstructors.containsKey(fullName)) {
+ return _cachedConstructors[fullName];
+ }
+ final WidgetLibrary? library = _libraries[fullName.library];
+ if (library is RemoteWidgetLibrary) {
+ for (final WidgetDeclaration constructor in library.widgets) {
+ if (constructor.name == fullName.widget) {
+ return _cachedConstructors[fullName] = _ResolvedConstructor(fullName, constructor);
+ }
+ }
+ for (final Import import in library.imports) {
+ final LibraryName dependency = import.name;
+ final _ResolvedConstructor? result = _findConstructor(FullyQualifiedWidgetName(dependency, fullName.widget));
+ if (result != null) {
+ return _cachedConstructors[fullName] = result;
+ }
+ }
+ } else if (library is LocalWidgetLibrary) {
+ final LocalWidgetBuilder? constructor = library.findConstructor(fullName.widget);
+ if (constructor != null) {
+ return _cachedConstructors[fullName] = _ResolvedConstructor(fullName, constructor);
+ }
+ } else {
+ assert(library is Null); // ignore: prefer_void_to_null, type_check_with_null, https://github.com/dart-lang/sdk/issues/47017#issuecomment-907562014
+ }
+ _cachedConstructors[fullName] = null;
+ return null;
+ }
+
+ Iterable<LibraryName> _findMissingLibraries(LibraryName library) sync* {
+ final WidgetLibrary? root = _libraries[library];
+ if (root == null) {
+ yield library;
+ return;
+ }
+ if (root is LocalWidgetLibrary) {
+ return;
+ }
+ root as RemoteWidgetLibrary;
+ for (final Import import in root.imports) {
+ yield* _findMissingLibraries(import.name);
+ }
+ }
+
+ /// Resolves argument references ([ArgsReference] objects) in the given
+ /// `node`, and applies [ConstructorCall]s so that all remaining widgets are
+ /// local widgets.
+ _CurriedWidget _applyConstructorAndBindArguments(FullyQualifiedWidgetName fullName, DynamicMap arguments, int stateDepth, Set<FullyQualifiedWidgetName> usedWidgets) {
+ final _ResolvedConstructor? widget = _findConstructor(fullName);
+ if (widget != null) {
+ if (widget.constructor is WidgetDeclaration) {
+ if (usedWidgets.contains(widget.fullName)) {
+ return _CurriedLocalWidget.error(fullName, 'Widget loop: Tried to call ${widget.fullName} constructor reentrantly.');
+ }
+ usedWidgets = usedWidgets.toSet()..add(widget.fullName);
+ final WidgetDeclaration constructor = widget.constructor as WidgetDeclaration;
+ int newDepth;
+ if (constructor.initialState != null) {
+ newDepth = stateDepth + 1;
+ } else {
+ newDepth = stateDepth;
+ }
+ Object result = _bindArguments(widget.fullName, constructor.root, arguments, newDepth, usedWidgets);
+ if (result is Switch) {
+ result = _CurriedSwitch(widget.fullName, result, arguments, constructor.initialState);
+ } else {
+ result as _CurriedWidget;
+ if (constructor.initialState != null) {
+ result = _CurriedRemoteWidget(widget.fullName, result, arguments, constructor.initialState);
+ }
+ }
+ return result as _CurriedWidget;
+ }
+ assert(widget.constructor is LocalWidgetBuilder);
+ return _CurriedLocalWidget(widget.fullName, widget.constructor as LocalWidgetBuilder, arguments);
+ }
+ final Set<LibraryName> missingLibraries = _findMissingLibraries(fullName.library).toSet();
+ if (missingLibraries.isNotEmpty) {
+ return _CurriedLocalWidget.error(
+ fullName,
+ 'Could not find remote widget named ${fullName.widget} in ${fullName.library}, '
+ 'possibly because some dependencies were missing: ${missingLibraries.join(", ")}',
+ );
+ }
+ return _CurriedLocalWidget.error(fullName, 'Could not find remote widget named ${fullName.widget} in ${fullName.library}.');
+ }
+
+ Object _bindArguments(FullyQualifiedWidgetName context, Object node, Object arguments, int stateDepth, Set<FullyQualifiedWidgetName> usedWidgets) {
+ if (node is ConstructorCall) {
+ final DynamicMap subArguments = _bindArguments(context, node.arguments, arguments, stateDepth, usedWidgets) as DynamicMap;
+ return _applyConstructorAndBindArguments(FullyQualifiedWidgetName(context.library, node.name), subArguments, stateDepth, usedWidgets);
+ }
+ if (node is DynamicMap) {
+ return node.map<String, Object?>(
+ (String name, Object? value) => MapEntry<String, Object?>(name, _bindArguments(context, value!, arguments, stateDepth, usedWidgets)),
+ );
+ }
+ if (node is DynamicList) {
+ return node.map<Object>(
+ (Object? value) => _bindArguments(context, value!, arguments, stateDepth, usedWidgets),
+ ).toList();
+ }
+ if (node is Loop) {
+ final Object input = _bindArguments(context, node.input, arguments, stateDepth, usedWidgets);
+ final Object output = _bindArguments(context, node.output, arguments, stateDepth, usedWidgets);
+ return Loop(input, output);
+ }
+ if (node is Switch) {
+ return Switch(
+ _bindArguments(context, node.input, arguments, stateDepth, usedWidgets),
+ node.outputs.map<Object?, Object>(
+ (Object? key, Object value) {
+ return MapEntry<Object?, Object>(
+ key == null ? key : _bindArguments(context, key, arguments, stateDepth, usedWidgets),
+ _bindArguments(context, value, arguments, stateDepth, usedWidgets),
+ );
+ },
+ ),
+ );
+ }
+ if (node is ArgsReference) {
+ return node.bind(arguments);
+ }
+ if (node is StateReference) {
+ return node.bind(stateDepth);
+ }
+ if (node is EventHandler) {
+ return EventHandler(node.eventName, _bindArguments(context, node.eventArguments, arguments, stateDepth, usedWidgets) as DynamicMap);
+ }
+ if (node is SetStateHandler) {
+ assert(node.stateReference is StateReference);
+ final BoundStateReference stateReference = (node.stateReference as StateReference).bind(stateDepth);
+ return SetStateHandler(stateReference, _bindArguments(context, node.value, arguments, stateDepth, usedWidgets));
+ }
+ assert(node is! WidgetDeclaration);
+ return node;
+ }
+}
+
+// Internal structure to represent the result of indexing into a list.
+//
+// There are two ways this can go: either we index in and find a result, in
+// which case [result] is that value and the other fields are null, or we fail
+// to index into the list and we obtain the length as a side-effect, in which
+// case [result] is null, [rawList] is the raw list (might contain [Loop] objects),
+// and [length] is the effective length after expanding all the internal loops.
+class _ResolvedDynamicList {
+ const _ResolvedDynamicList(this.rawList, this.result, this.length);
+ final DynamicList? rawList;
+ final Object? result; // null means out of range
+ final int? length; // might be null if result is not null
+}
+
+typedef _DataResolverCallback = Object Function(List<Object> dataKey);
+typedef _StateResolverCallback = Object Function(List<Object> stateKey, int depth);
+
+abstract class _CurriedWidget extends BlobNode {
+ const _CurriedWidget(this.fullName, this.arguments, this.initialState);
+
+ final FullyQualifiedWidgetName fullName;
+ final DynamicMap arguments;
+ final DynamicMap? initialState;
+
+ static Object _bindLoopVariable(Object node, Object argument, int depth) {
+ if (node is DynamicMap) {
+ return node.map<String, Object?>(
+ (String name, Object? value) => MapEntry<String, Object?>(name, _bindLoopVariable(value!, argument, depth)),
+ );
+ }
+ if (node is DynamicList) {
+ return node.map<Object>(
+ (Object? value) => _bindLoopVariable(value!, argument, depth),
+ ).toList();
+ }
+ if (node is Loop) {
+ return Loop(_bindLoopVariable(node.input, argument, depth), _bindLoopVariable(node.output, argument, depth + 1));
+ }
+ if (node is Switch) {
+ return Switch(
+ _bindLoopVariable(node.input, argument, depth),
+ node.outputs.map<Object?, Object>(
+ (Object? key, Object value) => MapEntry<Object?, Object>(
+ key == null ? null : _bindLoopVariable(key, argument, depth),
+ _bindLoopVariable(value, argument, depth),
+ ),
+ )
+ );
+ }
+ if (node is _CurriedLocalWidget) {
+ return _CurriedLocalWidget(
+ node.fullName,
+ node.child,
+ _bindLoopVariable(node.arguments, argument, depth) as DynamicMap,
+ );
+ }
+ if (node is _CurriedRemoteWidget) {
+ return _CurriedRemoteWidget(
+ node.fullName,
+ _bindLoopVariable(node.child, argument, depth) as _CurriedWidget,
+ _bindLoopVariable(node.arguments, argument, depth) as DynamicMap,
+ node.initialState,
+ );
+ }
+ if (node is _CurriedSwitch) {
+ return _CurriedSwitch(
+ node.fullName,
+ _bindLoopVariable(node.root, argument, depth) as Switch,
+ _bindLoopVariable(node.arguments, argument, depth) as DynamicMap,
+ node.initialState,
+ );
+ }
+ if (node is LoopReference) {
+ if (node.loop == depth) {
+ return node.bind(argument);
+ }
+ return node;
+ }
+ if (node is BoundArgsReference) {
+ return BoundArgsReference(_bindLoopVariable(node.arguments, argument, depth), node.parts);
+ }
+ if (node is EventHandler) {
+ return EventHandler(node.eventName, _bindLoopVariable(node.eventArguments, argument, depth) as DynamicMap);
+ }
+ if (node is SetStateHandler) {
+ return SetStateHandler(node.stateReference, _bindLoopVariable(node.value, argument, depth));
+ }
+ return node;
+ }
+
+ /// Look up the _index_th entry in `list`, expanding any loops in `list`.
+ ///
+ /// If `index` is -1, this evaluates the entire list to ensure the length is available.
+ //
+ // TODO(ianh): This really should have some sort of caching. Right now, evaluating a whole list
+ // ends up being around O(N^2) since we have to walk the list from the start for every entry.
+ static _ResolvedDynamicList _listLookup(DynamicList list, int targetEffectiveIndex, _StateResolverCallback stateResolver, _DataResolverCallback dataResolver) {
+ if (list.any((Object? entry) => entry is Loop)) {
+ int currentIndex = 0; // where we are in `list` (some entries of which might represent multiple values, because they are themselves loops)
+ int effectiveIndex = 0; // where we are in the fully expanded list (the coordinate space in which we're aiming for `targetEffectiveIndex`)
+ while ((effectiveIndex <= targetEffectiveIndex || targetEffectiveIndex < 0) && currentIndex < list.length) {
+ final Object node = list[currentIndex]!;
+ if (node is Loop) {
+ Object inputList = node.input;
+ while (inputList is! DynamicList) {
+ if (inputList is BoundArgsReference) {
+ inputList = _resolveFrom(inputList.arguments, inputList.parts, stateResolver, dataResolver);
+ } else if (inputList is DataReference) {
+ inputList = dataResolver(inputList.parts);
+ } else if (inputList is BoundStateReference) {
+ inputList = stateResolver(inputList.parts, inputList.depth);
+ } else if (inputList is BoundLoopReference) {
+ inputList = _resolveFrom(inputList.value, inputList.parts, stateResolver, dataResolver);
+ } else if (inputList is Switch) {
+ inputList = _resolveFrom(inputList, const <Object>[], stateResolver, dataResolver);
+ } else {
+ // e.g. it's a map or something else that isn't indexable
+ inputList = DynamicList.empty();
+ }
+ assert(inputList is! _ResolvedDynamicList);
+ }
+ final _ResolvedDynamicList entry = _listLookup(inputList, targetEffectiveIndex >= 0 ? targetEffectiveIndex - effectiveIndex : -1, stateResolver, dataResolver);
+ if (entry.result != null) {
+ final Object boundResult = _bindLoopVariable(node.output, entry.result!, 0);
+ return _ResolvedDynamicList(null, boundResult, null);
+ }
+ effectiveIndex += entry.length!;
+ } else { // list[currentIndex] is not a Loop
+ if (effectiveIndex == targetEffectiveIndex) {
+ return _ResolvedDynamicList(null, list[currentIndex], null);
+ }
+ effectiveIndex += 1;
+ }
+ currentIndex += 1;
+ }
+ return _ResolvedDynamicList(list, null, effectiveIndex);
+ } else {
+ if (targetEffectiveIndex < 0 || targetEffectiveIndex >= list.length) {
+ return _ResolvedDynamicList(list, null, list.length);
+ }
+ return _ResolvedDynamicList(list, list[targetEffectiveIndex]!, list.length);
+ }
+ }
+
+ static Object _resolveFrom(Object root, List<Object> parts, _StateResolverCallback stateResolver, _DataResolverCallback dataResolver) {
+ int index = 0;
+ Object current = root;
+ while (true) {
+ if (current is DataReference) {
+ if (index < parts.length) {
+ current = current.constructReference(parts.sublist(index));
+ index = parts.length;
+ }
+ current = dataResolver(current.parts);
+ continue;
+ } else if (current is BoundArgsReference) {
+ List<Object> nextParts = current.parts;
+ if (index < parts.length) {
+ nextParts += parts.sublist(index);
+ }
+ parts = nextParts;
+ current = current.arguments;
+ index = 0;
+ continue;
+ } else if (current is BoundStateReference) {
+ if (index < parts.length) {
+ current = current.constructReference(parts.sublist(index));
+ index = parts.length;
+ }
+ current = stateResolver(current.parts, current.depth);
+ continue;
+ } else if (current is BoundLoopReference) {
+ List<Object> nextParts = current.parts;
+ if (index < parts.length) {
+ nextParts += parts.sublist(index);
+ }
+ parts = nextParts;
+ current = current.value;
+ index = 0;
+ continue;
+ } else if (current is Switch) {
+ final Object key = _resolveFrom(current.input, const <Object>[], stateResolver, dataResolver);
+ Object? value = current.outputs[key];
+ if (value == null) {
+ value = current.outputs[null];
+ if (value == null) {
+ return missing;
+ }
+ }
+ current = value;
+ continue;
+ } else if (index >= parts.length) {
+ if (current is EventHandler) {
+ current = EventHandler(current.eventName, _fix(current.eventArguments, stateResolver, dataResolver) as DynamicMap);
+ } else if (current is SetStateHandler) {
+ current = SetStateHandler(current.stateReference, _fix(current.value, stateResolver, dataResolver));
+ }
+ break;
+ } else if (current is DynamicMap) {
+ if (parts[index] is! String) {
+ return missing;
+ }
+ if (!current.containsKey(parts[index])) {
+ return missing;
+ }
+ current = current[parts[index]]!;
+ } else if (current is DynamicList) {
+ if (parts[index] is! int) {
+ return missing;
+ }
+ current = _listLookup(current, parts[index] as int, stateResolver, dataResolver).result ?? missing;
+ } else {
+ assert(current is! ArgsReference);
+ assert(current is! StateReference);
+ assert(current is! LoopReference);
+ return missing;
+ }
+ index += 1;
+ }
+ assert(current is! Reference, 'Unexpected unbound reference (of type ${current.runtimeType}): $current');
+ assert(current is! Switch);
+ assert(current is! Loop);
+ return current;
+ }
+
+ static Object _fix(Object root, _StateResolverCallback stateResolver, _DataResolverCallback dataResolver) {
+ if (root is DynamicMap) {
+ return root.map((String key, Object? value) => MapEntry<String, Object?>(key, _fix(root[key]!, stateResolver, dataResolver)));
+ } else if (root is DynamicList) {
+ if (root.any((Object? entry) => entry is Loop)) {
+ final int length = _listLookup(root, -1, stateResolver, dataResolver).length!;
+ return DynamicList.generate(length, (int index) => _fix(_listLookup(root, index, stateResolver, dataResolver).result!, stateResolver, dataResolver));
+ } else {
+ return DynamicList.generate(root.length, (int index) => _fix(root[index]!, stateResolver, dataResolver));
+ }
+ } else if (root is BlobNode) {
+ return _resolveFrom(root, const <Object>[], stateResolver, dataResolver);
+ } else {
+ return root;
+ }
+ }
+
+ Object resolve(List<Object> parts, _StateResolverCallback stateResolver, _DataResolverCallback dataResolver, { required bool expandLists }) {
+ Object result = _resolveFrom(arguments, parts, stateResolver, dataResolver);
+ if (result is DynamicList && expandLists) {
+ result = _listLookup(result, -1, stateResolver, dataResolver);
+ }
+ assert(result is! Reference);
+ assert(result is! Switch);
+ assert(result is! Loop);
+ return result;
+ }
+
+ Widget build(BuildContext context, DynamicContent data, RemoteEventHandler remoteEventTarget, List<_WidgetState> states) {
+ return _Widget(curriedWidget: this, data: data, remoteEventTarget: remoteEventTarget, states: states);
+ }
+
+ Widget buildChild(BuildContext context, DataSource source, DynamicContent data, RemoteEventHandler remoteEventTarget, List<_WidgetState> states, _StateResolverCallback stateResolver, _DataResolverCallback dataResolver);
+
+ @override
+ String toString() => '$fullName ${initialState ?? "{}"} $arguments';
+}
+
+class _CurriedLocalWidget extends _CurriedWidget {
+ const _CurriedLocalWidget(FullyQualifiedWidgetName fullName, this.child, DynamicMap arguments) : super(fullName, arguments, null);
+
+ factory _CurriedLocalWidget.error(FullyQualifiedWidgetName fullName, String message) {
+ return _CurriedLocalWidget(fullName, (BuildContext context, DataSource data) => _buildErrorWidget(message), const <String, Object?>{});
+ }
+
+ final LocalWidgetBuilder child;
+
+ @override
+ Widget buildChild(BuildContext context, DataSource source, DynamicContent data, RemoteEventHandler remoteEventTarget, List<_WidgetState> states, _StateResolverCallback stateResolver, _DataResolverCallback dataResolver) {
+ return child(context, source);
+ }
+}
+
+class _CurriedRemoteWidget extends _CurriedWidget {
+ const _CurriedRemoteWidget(FullyQualifiedWidgetName fullName, this.child, DynamicMap arguments, DynamicMap? initialState) : super(fullName, arguments, initialState);
+
+ final _CurriedWidget child;
+
+ @override
+ Widget buildChild(BuildContext context, DataSource source, DynamicContent data, RemoteEventHandler remoteEventTarget, List<_WidgetState> states, _StateResolverCallback stateResolver, _DataResolverCallback dataResolver) {
+ return child.build(context, data, remoteEventTarget, states);
+ }
+
+ @override
+ String toString() => '${super.toString()} = $child';
+}
+
+class _CurriedSwitch extends _CurriedWidget {
+ const _CurriedSwitch(FullyQualifiedWidgetName fullName, this.root, DynamicMap arguments, DynamicMap? initialState) : super(fullName, arguments, initialState);
+
+ final Switch root;
+
+ @override
+ Widget buildChild(BuildContext context, DataSource source, DynamicContent data, RemoteEventHandler remoteEventTarget, List<_WidgetState> states, _StateResolverCallback stateResolver, _DataResolverCallback dataResolver) {
+ Object result = _CurriedWidget._resolveFrom(root, const <Object>[], stateResolver, dataResolver);
+ if (result is _CurriedWidget) {
+ result = result.build(context, data, remoteEventTarget, states);
+ return result as Widget;
+ }
+ return _buildErrorWidget('Switch in $fullName did not resolve to a widget (got $result).');
+ }
+
+ @override
+ String toString() => '${super.toString()} = $root';
+}
+
+class _Widget extends StatefulWidget {
+ const _Widget({ Key? key, required this.curriedWidget, required this.data, required this.remoteEventTarget, required this.states }) : super(key: key);
+
+ final _CurriedWidget curriedWidget;
+
+ final DynamicContent data;
+
+ final RemoteEventHandler remoteEventTarget;
+
+ final List<_WidgetState> states;
+
+ @override
+ State<_Widget> createState() => _WidgetState();
+}
+
+class _WidgetState extends State<_Widget> implements DataSource {
+ DynamicContent? _state;
+ DynamicMap? _stateStore;
+ late List<_WidgetState> _states;
+
+ @override
+ void initState() {
+ super.initState();
+ _updateState();
+ }
+
+ @override
+ void didUpdateWidget(_Widget oldWidget) {
+ super.didUpdateWidget(oldWidget);
+ if (oldWidget.curriedWidget != widget.curriedWidget) {
+ _updateState();
+ }
+ if (oldWidget.data != widget.data || oldWidget.curriedWidget != widget.curriedWidget || oldWidget.states != widget.states) {
+ _unsubscribe();
+ }
+ }
+
+ @override
+ void dispose() {
+ _unsubscribe();
+ super.dispose();
+ }
+
+ void _updateState() {
+ _stateStore = deepClone(widget.curriedWidget.initialState) as DynamicMap?;
+ if (_stateStore != null) {
+ _state ??= DynamicContent();
+ _state!.updateAll(_stateStore!);
+ } else {
+ _state = null;
+ }
+ _states = widget.states;
+ if (_state != null) {
+ _states = _states.toList()..add(this);
+ }
+ }
+
+ void _handleSetState(int depth, List<Object> parts, Object value) {
+ _states[depth].applySetState(parts, value);
+ }
+
+ void applySetState(List<Object> parts, Object value) {
+ assert(parts.isNotEmpty);
+ assert(_stateStore != null);
+ int index = 0;
+ Object current = _stateStore!;
+ while (index < parts.length) {
+ final Object subindex = parts[index];
+ if (current is DynamicMap) {
+ if (subindex is! String) {
+ throw RemoteFlutterWidgetsException('${parts.join(".")} does not identify existing state.');
+ }
+ if (!current.containsKey(subindex)) {
+ throw RemoteFlutterWidgetsException('${parts.join(".")} does not identify existing state.');
+ }
+ if (index == parts.length - 1) {
+ current[subindex] = value;
+ } else {
+ current = current[parts[index]]!;
+ }
+ } else if (current is DynamicList) {
+ if (subindex is! int) {
+ throw RemoteFlutterWidgetsException('${parts.join(".")} does not identify existing state.');
+ }
+ if (subindex < 0 || subindex >= current.length) {
+ throw RemoteFlutterWidgetsException('${parts.join(".")} does not identify existing state.');
+ }
+ if (index == parts.length - 1) {
+ current[subindex] = value;
+ } else {
+ current = current[subindex]!;
+ }
+ } else {
+ throw RemoteFlutterWidgetsException('${parts.join(".")} does not identify existing state.');
+ }
+ index += 1;
+ }
+ _state!.updateAll(_stateStore!);
+ }
+
+ // List of subscriptions into [widget.data].
+ //
+ // Keys are into the [DynamicContent] object.
+ final Map<_Key, _Subscription> _subscriptions = <_Key, _Subscription>{};
+
+ void _unsubscribe() {
+ for (final _Subscription value in _subscriptions.values) {
+ value.dispose();
+ }
+ _subscriptions.clear();
+ _argsCache.clear();
+ }
+
+ @override
+ T? v<T>(List<Object> argsKey) {
+ assert(T == Object || T == int || T == double || T == bool || T == String);
+ final Object value = _fetch(argsKey, expandLists: false);
+ return value is T ? value as T : null;
+ }
+
+ @override
+ bool isList(List<Object> argsKey) {
+ final Object value = _fetch(argsKey, expandLists: false);
+ return value is _ResolvedDynamicList
+ || value is DynamicList;
+ }
+
+ @override
+ int length(List<Object> argsKey) {
+ final Object value = _fetch(argsKey, expandLists: true);
+ if (value is _ResolvedDynamicList) {
+ if (value.rawList != null) {
+ assert(value.length != null);
+ return value.length!;
+ }
+ }
+ assert(value is! DynamicList);
+ return 0;
+ }
+
+ @override
+ bool isMap(List<Object> argsKey) {
+ final Object value = _fetch(argsKey, expandLists: false);
+ return value is DynamicMap;
+ }
+
+ @override
+ Widget child(List<Object> argsKey) {
+ final Object value = _fetch(argsKey, expandLists: false);
+ if (value is _CurriedWidget) {
+ return value.build(context, widget.data, widget.remoteEventTarget, widget.states);
+ }
+ return _buildErrorWidget('Not a widget at $argsKey (got $value) for ${widget.curriedWidget.fullName}.');
+ }
+
+ @override
+ Widget? optionalChild(List<Object> argsKey) {
+ final Object value = _fetch(argsKey, expandLists: false);
+ if (value is _CurriedWidget) {
+ return value.build(context, widget.data, widget.remoteEventTarget, widget.states);
+ }
+ return null;
+ }
+
+ @override
+ List<Widget> childList(List<Object> argsKey) {
+ final Object value = _fetch(argsKey, expandLists: true);
+ if (value is _ResolvedDynamicList) {
+ assert(value.length != null);
+ final DynamicList fullList = _fetchList(argsKey, value.length!);
+ return fullList.map<Widget>((Object? node) {
+ if (node is _CurriedWidget) {
+ return node.build(context, widget.data, widget.remoteEventTarget, _states);
+ }
+ return _buildErrorWidget('Not a widget at $argsKey (got $node) for ${widget.curriedWidget.fullName}.');
+ }).toList();
+ }
+ if (value == missing) {
+ return const <Widget>[];
+ }
+ return <Widget>[
+ _buildErrorWidget('Not a widget list at $argsKey (got $value) for ${widget.curriedWidget.fullName}.'),
+ ];
+ }
+
+ @override
+ VoidCallback? voidHandler(List<Object> argsKey, [ DynamicMap? extraArguments ]) {
+ return handler<VoidCallback>(argsKey, (HandlerTrigger callback) => () => callback(extraArguments));
+ }
+
+ @override
+ T? handler<T extends Function>(List<Object> argsKey, HandlerGenerator<T> generator) {
+ Object value = _fetch(argsKey, expandLists: true);
+ if (value is AnyEventHandler) {
+ value = <Object>[ value ];
+ } else if (value is _ResolvedDynamicList) {
+ value = _fetchList(argsKey, value.length!);
+ }
+ if (value is DynamicList) {
+ final List<AnyEventHandler> handlers = value.whereType<AnyEventHandler>().toList();
+ if (handlers.isNotEmpty) {
+ return generator(([DynamicMap? extraArguments]) {
+ for (final AnyEventHandler entry in handlers) {
+ if (entry is EventHandler) {
+ DynamicMap arguments = entry.eventArguments;
+ if (extraArguments != null) {
+ arguments = DynamicMap.fromEntries(arguments.entries.followedBy(extraArguments.entries));
+ }
+ widget.remoteEventTarget(entry.eventName, arguments);
+ } else if (entry is SetStateHandler) {
+ assert(entry.stateReference is BoundStateReference);
+ _handleSetState((entry.stateReference as BoundStateReference).depth, entry.stateReference.parts, entry.value);
+ }
+ }
+ });
+ }
+ }
+ return null;
+ }
+
+ // null values means the data is not in the cache
+ final Map<_Key, Object?> _argsCache = <_Key, Object?>{};
+
+ bool _debugFetching = false;
+ final List<_Subscription> _dependencies = <_Subscription>[];
+
+ Object _fetch(List<Object> argsKey, { required bool expandLists }) {
+ final _Key key = _Key(_kArgsSection, argsKey);
+ final Object? value = _argsCache[key];
+ if (value != null && (value is! DynamicList || !expandLists)) {
+ return value;
+ }
+ assert(!_debugFetching);
+ try {
+ _debugFetching = true;
+ final Object result = widget.curriedWidget.resolve(argsKey, _stateResolver, _dataResolver, expandLists: expandLists);
+ for (final _Subscription subscription in _dependencies) {
+ subscription.addClient(key);
+ }
+ _argsCache[key] = result;
+ return result;
+ } finally {
+ _dependencies.clear();
+ _debugFetching = false;
+ }
+ }
+
+ DynamicList _fetchList(List<Object> argsKey, int length) {
+ return DynamicList.generate(length, (int index) {
+ return _fetch(<Object>[...argsKey, index], expandLists: false);
+ });
+ }
+
+ Object _dataResolver(List<Object> rawDataKey) {
+ final _Key dataKey = _Key(_kDataSection, rawDataKey);
+ final _Subscription subscription;
+ if (!_subscriptions.containsKey(dataKey)) {
+ subscription = _Subscription(widget.data, this, rawDataKey);
+ _subscriptions[dataKey] = subscription;
+ } else {
+ subscription = _subscriptions[dataKey]!;
+ }
+ _dependencies.add(subscription);
+ return subscription.value;
+ }
+
+ Object _stateResolver(List<Object> rawStateKey, int depth) {
+ final _Key stateKey = _Key(depth, rawStateKey);
+ final _Subscription subscription;
+ if (!_subscriptions.containsKey(stateKey)) {
+ if (depth >= _states.length) {
+ throw const RemoteFlutterWidgetsException('Reference to state value did not correspond to any stateful remote widget.');
+ }
+ final DynamicContent? state = _states[depth]._state;
+ if (state == null) {
+ return missing;
+ }
+ subscription = _Subscription(state, this, rawStateKey);
+ _subscriptions[stateKey] = subscription;
+ } else {
+ subscription = _subscriptions[stateKey]!;
+ }
+ _dependencies.add(subscription);
+ return subscription.value;
+ }
+
+ void updateData(Set<_Key> affectedArgs) {
+ setState(() {
+ for (final _Key key in affectedArgs) {
+ _argsCache[key] = null;
+ }
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ // TODO(ianh): what if this creates some _dependencies?
+ return widget.curriedWidget.buildChild(context, this, widget.data, widget.remoteEventTarget, _states, _stateResolver, _dataResolver);
+ }
+
+ @override
+ void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+ super.debugFillProperties(properties);
+ properties.add(StringProperty('name', '${widget.curriedWidget.fullName}'));
+ }
+}
+
+const int _kDataSection = -1;
+const int _kArgsSection = -2;
+
+@immutable
+class _Key {
+ _Key(this.section, this.parts) : assert(_isValidKey(parts), '$parts is not a valid key');
+
+ static bool _isValidKey(List<Object> parts) {
+ return parts.every((Object segment) => segment is int || segment is String);
+ }
+
+ final int section;
+ final List<Object> parts;
+
+ @override
+ bool operator ==(Object other) {
+ return other is _Key // _Key has no subclasses, don't need to check runtimeType
+ && section == other.section
+ && listEquals(parts, other.parts);
+ }
+
+ @override
+ int get hashCode => hashValues(section, hashList(parts));
+}
+
+class _Subscription {
+ _Subscription(this._data, this._state, this._dataKey) {
+ _update(_data.subscribe(_dataKey, _update));
+ }
+
+ final DynamicContent _data;
+ final _WidgetState _state;
+ final List<Object> _dataKey;
+ final Set<_Key> _clients = <_Key>{};
+
+ Object get value => _value;
+ late Object _value;
+
+ void _update(Object value) {
+ _state.updateData(_clients);
+ _value = value;
+ }
+
+ void addClient(_Key key) {
+ _clients.add(key);
+ }
+
+ void dispose() {
+ _data.unsubscribe(_dataKey, _update);
+ }
+}
+
+ErrorWidget _buildErrorWidget(String message) {
+ FlutterError.reportError(FlutterErrorDetails(
+ exception: message,
+ stack: StackTrace.current,
+ library: 'Remote Flutter Widgets',
+ ));
+ return ErrorWidget(message);
+}
diff --git a/packages/rfw/lib/formats.dart b/packages/rfw/lib/formats.dart
new file mode 100644
index 0000000..ae327e3
--- /dev/null
+++ b/packages/rfw/lib/formats.dart
@@ -0,0 +1,32 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/// # Remote Flutter Widgets - formats only import
+///
+/// This is a subset of the [rfw] library that only exposes features that
+/// do not depend on Flutter.
+///
+/// Specifically, the following APIs are exposed by this library:
+///
+/// * [parseLibraryFile] and [parseDataFile], for parsing Remote Flutter
+/// Widgets text library and data files respectively. (These are not exposed
+/// by the [rfw] library since they are not intended for use in client-side
+/// code.)
+///
+/// * [encodeLibraryBlob] and [encodeDataBlob], for encoding the output of the
+/// previous methods into binary form.
+///
+/// * [decodeLibraryBlob] and [decodeDataBlob], which decode those binary
+/// forms.
+///
+/// * The [DynamicMap], [DynamicList], and [BlobNode] types (and subclasses),
+/// which are used to represent the data model and remote widget libraries in
+/// memory.
+///
+/// For client-side code, import `package:rfw/rfw.dart` instead.
+library formats;
+
+export 'dart/binary.dart';
+export 'dart/model.dart';
+export 'dart/text.dart';
diff --git a/packages/rfw/lib/rfw.dart b/packages/rfw/lib/rfw.dart
new file mode 100644
index 0000000..23cfab7
--- /dev/null
+++ b/packages/rfw/lib/rfw.dart
@@ -0,0 +1,58 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/// # Remote Flutter Widgets
+///
+/// Renders a widget tree described in a file, which you can change at runtime.
+///
+/// There are many ways to use a package such as this one. In general, the
+/// approach looks something like this:
+///
+/// 
+///
+/// The network aspects of this design are out of scope for this package. Remote
+/// widget libraries and data should be cached locally, to avoid network issues
+/// causing interface failures.
+///
+/// In the extreme, this package can be combined with a local scripting runtime
+/// (e.g. [https://pub.dev/packages/hetu_script](hetu_script)) to run
+/// remotely-fetched logic locally:
+///
+/// 
+///
+///
+/// ## Using the [RemoteWidget] widget
+///
+/// To render a remote widget, the [RemoteWidget] widget can be used. It takes a
+/// [DynamicContent] instance and a [Runtime] instance, which must be configured
+/// ahead of time.
+///
+/// ## Best practices for handling remote data
+///
+/// The most efficient way to parse remote data is [decodeDataBlob] and the most
+/// efficient way to parse remote widget libraries is [decodeLibraryBlob].
+///
+/// The methods for parsing the text format are not exported by
+/// `package:rfw/rfw.dart` to discourage their use in client-side code.
+///
+/// ## Server-side dart
+///
+/// This package can be used in non-Flutter environments by importing
+/// `package:rfw/formats.dart` rather than `package:rfw/rfw.dart`. In the
+/// `formats` mode, the [Runtime] and [DynamicContent] objects, as well as the
+/// [RemoteWidget] widget, are not available, but the [parseDataFile] and
+/// [parseLibraryFile] methods are. They can be used in conjunction with
+/// [encodeDataBlob] and [encodeLibraryBlob] (respectively) to generate the
+/// binary files used by client-side code.
+library rfw;
+
+export 'dart/binary.dart';
+export 'dart/model.dart';
+export 'dart/text.dart' hide parseDataFile, parseLibraryFile;
+export 'flutter/argument_decoders.dart';
+export 'flutter/content.dart';
+export 'flutter/core_widgets.dart';
+export 'flutter/material_widgets.dart';
+export 'flutter/remote_widget.dart';
+export 'flutter/runtime.dart';
diff --git a/packages/rfw/pubspec.yaml b/packages/rfw/pubspec.yaml
new file mode 100644
index 0000000..37dfdbf
--- /dev/null
+++ b/packages/rfw/pubspec.yaml
@@ -0,0 +1,18 @@
+name: rfw
+description: Remote Flutter widgets
+repository: https://github.com/flutter/packages/tree/master/packages/rfw
+issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+rfw%22
+version: 1.0.0
+
+environment:
+ sdk: ">=2.13.0 <3.0.0"
+ flutter: ">=1.17.0"
+
+dependencies:
+ flutter:
+ sdk: flutter
+ meta: ^1.7.0
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
diff --git a/packages/rfw/run_tests.sh b/packages/rfw/run_tests.sh
new file mode 100755
index 0000000..abf2398
--- /dev/null
+++ b/packages/rfw/run_tests.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+# Copyright 2013 The Flutter Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Please update these targets when you update this package.
+# Please ensure that test coverage continues to be 100%.
+
+TARGET_LINES=2125
+TARGET_PERCENT=100
+LAST_UPDATE="2021-08-30"
+
+# ----------------------------------------------------------------------
+
+# This script is mentioned in the README.md file.
+
+set -e
+
+if [ "$CHANNEL" == "stable" ]; then
+ # For now these are disabled because this package has never been supported
+ # on the stable channel and requires newer language features that have not
+ # yet shipped to a stable build. -Hixie, 2021-08-30
+ echo "Skipping tests on stable channel."
+ exit 0
+fi
+
+rm -rf coverage
+# We run with --update-goldens because the goal here is not to verify the tests
+# pass but to verify the coverage, and the goldens are not always going to pass
+# when run on different platforms (e.g. on Cirrus we run this on a mac but the
+# goldens expect a linux box).
+flutter test --coverage --update-goldens
+ACTUAL=`lcov -l coverage/lcov.info | tail -1 | cut -d '|' -f 2 | cut -d '%' -f 2`
+# We only check the TARGET_LINES matches, not the TARGET_PERCENT,
+# because we expect the percentage to drop over time as Dart fixes
+# various bugs in how it determines what lines are coverable.
+if [ $ACTUAL -lt $TARGET_LINES ]; then
+ echo
+ echo " ╭──────────────────────────────╮"
+ echo " │ COVERAGE REGRESSION DETECTED │"
+ echo " ╰──────────────────────────────╯"
+ echo
+ lcov --quiet --list coverage/lcov.info
+ echo
+ echo "Coverage has reduced to only" $ACTUAL "lines. This is lower than it was"
+ echo "as of $LAST_UPDATE, when coverage was $TARGET_PERCENT%, covering $TARGET_LINES lines."
+ echo "Please add sufficient tests to get coverage back to 100%, and update"
+ echo "run_tests.sh to have the appropriate targets."
+ echo
+ echo "When in doubt, ask @Hixie for advice. Thanks!"
+ exit 1
+fi
+rm -rf coverage
diff --git a/packages/rfw/test/argument_decoders_test.dart b/packages/rfw/test/argument_decoders_test.dart
new file mode 100644
index 0000000..dfe0abb
--- /dev/null
+++ b/packages/rfw/test/argument_decoders_test.dart
@@ -0,0 +1,498 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is hand-formatted.
+
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:rfw/formats.dart' show parseLibraryFile;
+import 'package:rfw/rfw.dart';
+
+void main() {
+ testWidgets('String example', (WidgetTester tester) async {
+ Duration? duration;
+ Curve? curve;
+ int buildCount = 0;
+ final Widget builder = Builder(
+ builder: (BuildContext context) {
+ buildCount += 1;
+ duration = AnimationDefaults.durationOf(context);
+ curve = AnimationDefaults.curveOf(context);
+ return const SizedBox.shrink();
+ },
+ );
+ await tester.pumpWidget(
+ AnimationDefaults(
+ duration: const Duration(milliseconds: 500),
+ curve: Curves.easeIn,
+ child: builder,
+ ),
+ );
+ expect(duration, const Duration(milliseconds: 500));
+ expect(curve, Curves.easeIn);
+ expect(buildCount, 1);
+ await tester.pumpWidget(
+ AnimationDefaults(
+ duration: const Duration(milliseconds: 500),
+ curve: Curves.easeIn,
+ child: builder,
+ ),
+ );
+ expect(buildCount, 1);
+ await tester.pumpWidget(
+ AnimationDefaults(
+ duration: const Duration(milliseconds: 501),
+ curve: Curves.easeIn,
+ child: builder,
+ ),
+ );
+ expect(buildCount, 2);
+ });
+
+ testWidgets('spot checks', (WidgetTester tester) async {
+ Duration? duration;
+ Curve? curve;
+ int buildCount = 0;
+ final Runtime runtime = Runtime()
+ ..update(const LibraryName(<String>['core']), createCoreWidgets())
+ ..update(const LibraryName(<String>['builder']), LocalWidgetLibrary(<String, LocalWidgetBuilder>{
+ 'Test': (BuildContext context, DataSource source) {
+ buildCount += 1;
+ duration = AnimationDefaults.durationOf(context);
+ curve = AnimationDefaults.curveOf(context);
+ return const SizedBox.shrink();
+ },
+ }))
+ ..update(const LibraryName(<String>['test']), parseLibraryFile('import core; widget root = SizedBox();'));
+ final DynamicContent data = DynamicContent();
+ final List<String> eventLog = <String>[];
+ await tester.pumpWidget(
+ RemoteWidget(
+ runtime: runtime,
+ data: data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
+ onEvent: (String eventName, DynamicMap eventArguments) {
+ eventLog.add(eventName);
+ expect(eventArguments, const <String, Object?>{ 'argument': true });
+ },
+ ),
+ );
+ expect(find.byType(SizedBox), findsOneWidget);
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = Align(alignment: { x: 0.25, y: 0.75 });
+ '''));
+ await tester.pump();
+ expect(tester.widget<Align>(find.byType(Align)).alignment, const Alignment(0.25, 0.75));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = Align(alignment: { start: 0.25, y: 0.75 });
+ '''));
+ await tester.pump();
+ expect(tester.widget<Align>(find.byType(Align)).alignment, const Alignment(0.25, 0.75));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ import builder;
+ widget root = AnimationDefaults(curve: "easeOut", duration: 5000, child: Test());
+ '''));
+ await tester.pump();
+ expect(buildCount, 1);
+ expect(duration, const Duration(seconds: 5));
+ expect(curve, Curves.easeOut);
+
+ ArgumentDecoders.curveDecoders['saw3'] = (DataSource source, List<Object> key) => const SawTooth(3);
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ import builder;
+ widget root = AnimationDefaults(curve: "saw3", child: Test());
+ '''));
+ await tester.pump();
+ expect(curve, isA<SawTooth>());
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = AspectRatio(aspectRatio: 0.5);
+ '''));
+ await tester.pump();
+ expect(tester.widget<AspectRatio>(find.byType(AspectRatio)).aspectRatio, 0.5);
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = Center(widthFactor: 0.25);
+ '''));
+ await tester.pump();
+ expect(tester.widget<Center>(find.byType(Center)).widthFactor, 0.25);
+ expect(tester.widget<Center>(find.byType(Center)).heightFactor, null);
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = ColoredBox(color: 0xFF112233);
+ '''));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0xFF112233));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = Column(
+ mainAxisAlignment: "center",
+ children: [ ColoredBox(color: 1), ColoredBox(color: 2) ],
+ );
+ '''));
+ await tester.pump();
+ expect(tester.widget<Column>(find.byType(Column)).mainAxisAlignment, MainAxisAlignment.center);
+ expect(tester.widget<Column>(find.byType(Column)).crossAxisAlignment, CrossAxisAlignment.center);
+ expect(tester.widget<Column>(find.byType(Column)).verticalDirection, VerticalDirection.down);
+ expect(tester.widget<Column>(find.byType(Column)).children, hasLength(2));
+ expect(tester.widgetList<ColoredBox>(find.byType(ColoredBox)).toList()[0].color, const Color(0x00000001));
+ expect(tester.widgetList<ColoredBox>(find.byType(ColoredBox)).toList()[1].color, const Color(0x00000002));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = ColoredBox(color: 0xFF112233);
+ '''));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0xFF112233));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = DefaultTextStyle(
+ textHeightBehavior: { applyHeightToLastDescent: false },
+ child: SizedBoxShrink(),
+ );
+ '''));
+ await tester.pump();
+ expect(
+ tester.widget<DefaultTextStyle>(find.byType(DefaultTextStyle)).textHeightBehavior,
+ const TextHeightBehavior(applyHeightToLastDescent: false),
+ );
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = Directionality(
+ textDirection: "ltr",
+ child: SizedBoxShrink(),
+ );
+ '''));
+ await tester.pump();
+ expect(tester.widget<Directionality>(find.byType(Directionality)).textDirection, TextDirection.ltr);
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = FittedBox(
+ fit: "cover",
+ );
+ '''));
+ await tester.pump();
+ expect(tester.widget<FittedBox>(find.byType(FittedBox)).fit, BoxFit.cover);
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = GestureDetector(
+ onTap: event 'tap' { argument: true },
+ child: ColoredBox(),
+ );
+ '''));
+ await tester.pump();
+ await tester.tap(find.byType(ColoredBox));
+ expect(eventLog, <String>['tap']);
+ eventLog.clear();
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = Directionality(
+ textDirection: "ltr",
+ child: Icon(
+ icon: 0x0001,
+ fontFamily: 'FONT',
+ ),
+ );
+ '''));
+ await tester.pump();
+ expect(tester.widget<Icon>(find.byType(Icon)).icon!.codePoint, 1);
+ expect(tester.widget<Icon>(find.byType(Icon)).icon!.fontFamily, 'FONT');
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = IconTheme(
+ color: 0x12345678,
+ child: SizedBoxShrink(),
+ );
+ '''));
+ await tester.pump();
+ expect(tester.widget<IconTheme>(find.byType(IconTheme)).data.color, const Color(0x12345678));
+ });
+
+ testWidgets('golden checks', (WidgetTester tester) async {
+ final Runtime runtime = Runtime()
+ ..update(const LibraryName(<String>['core']), createCoreWidgets())
+ ..update(const LibraryName(<String>['test']), parseLibraryFile('import core; widget root = SizedBox();'));
+ final DynamicContent data = DynamicContent();
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.rtl,
+ child: RemoteWidget(
+ runtime: runtime,
+ data: data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
+ ),
+ ),
+ );
+ expect(find.byType(RemoteWidget), findsOneWidget);
+
+ ArgumentDecoders.decorationDecoders['tab'] = (DataSource source, List<Object> key) {
+ return UnderlineTabIndicator(
+ borderSide: ArgumentDecoders.borderSide(source, <Object>[...key, 'side']) ?? const BorderSide(width: 2.0, color: Color(0xFFFFFFFF)),
+ insets: ArgumentDecoders.edgeInsets(source, <Object>['insets']) ?? EdgeInsets.zero,
+ );
+ };
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = Container(
+ margin: [20.0, 10.0, 30.0, 5.0],
+ padding: [10.0],
+ decoration: {
+ type: 'box',
+ borderRadius: [ { x: 120.0 }, { x: 130.0, y: 40.0 } ],
+ image: {
+ // this image doesn't exist so nothing much happens here
+ // we check the results of this parse in a separate expect
+ source: 'asset',
+ color: 0xFF00BBCC,
+ centerSlice: { x: 5.0, y: 8.0, w: 100.0, h: 70.0 },
+ colorFilter: {
+ type: 'matrix', matrix: [
+ 1.0, 1.0, 1.0, 1.0, 1.0,
+ 1.0, 1.0, 1.0, 1.0, 1.0,
+ 1.0, 1.0, 1.0, 1.0, 1.0,
+ 1.0, 1.0, 1.0, 1.0, 1.0,
+ ],
+ },
+ },
+ gradient: {
+ type: 'sweep',
+ },
+ },
+ foregroundDecoration: {
+ type: 'box',
+ border: [ { width: 10.0, color: 0xFFFFFF00 }, { width: 3.0, color: 0xFF00FFFF } ],
+ boxShadow: [ { offset: { x: 25.0, y: 25.0 }, color: 0x5F000000, } ],
+ image: {
+ // this image also doesn't exist
+ // we check the results of this parse in a separate expect
+ source: 'x-invalid://',
+ colorFilter: {
+ type: 'mode',
+ color: 0xFF8811FF,
+ blendMode: "xor",
+ },
+ },
+ gradient: {
+ type: 'linear',
+ colors: [ 0x1F009900, 0x1F33CC33, 0x7F777700 ],
+ stops: [ 0.0, 0.75, 1.0 ],
+ },
+ },
+ alignment: { x: 0.0, y: -0.5, },
+ transform: [
+ 0.9, 0.2, 0.1, 0.0,
+ -0.1, 1.1, 0.0, 0.0,
+ 0.0, 0.0, 1.0, 0.0,
+ 50.0, -20.0, 0.0, 1.0,
+ ],
+ child: Container(
+ constraints: { maxWidth: 400.0, maxHeight: 350.0 },
+ margin: [5.0, 25.0, 10.0, 20.0],
+ decoration: {
+ type: 'box',
+ color: 0xFF9911CC,
+ gradient: { type: 'custom' },
+ },
+ foregroundDecoration: {
+ type: 'flutterLogo',
+ margin: [ 100.0 ],
+ },
+ child: Container(
+ margin: [5.0],
+ decoration: {
+ type: 'tab',
+ side: { width: 20.0, color: 0xFFFFFFFF },
+ },
+ foregroundDecoration: {
+ type: 'shape',
+ shape: [
+ { type: 'box', border: { width: 10.0, color: 0xFF0000FF } },
+ { type: 'beveled', borderRadius: [ { x: 60.0 } ], side: { width: 10.0, color: 0xFF0033FF } },
+ { type: 'circle', side: { width: 10.0, color: 0xFF0066FF } },
+ { type: 'continuous', borderRadius: [ { x: 60.0 }, { x: 80.0 }, { x: 0.0 }, { x: 20.0, y: 50.0 } ], side: { width: 10.0, color: 0xFFEEFF33 } },
+ { type: 'rounded', borderRadius: [ { x: 20.0 } ], side: { width: 10.0, color: 0xFF00CCFF } },
+ { type: 'stadium', side: { width: 10.0, color: 0xFF00FFFF } },
+ { type: 'custom', side: { width: 100.0, color: 0xFFFF0000 } }, // should not render
+ ],
+ gradient: {
+ type: 'radial',
+ },
+ },
+ ),
+ ),
+ );
+ '''));
+ await tester.pump();
+ await expectLater(
+ find.byType(RemoteWidget),
+ matchesGoldenFile('goldens/argument_decoders_test.containers.png'),
+ );
+ expect(find.byType(DecoratedBox), findsNWidgets(6));
+ expect(
+ (tester.widgetList<DecoratedBox>(find.byType(DecoratedBox)).toList()[1].decoration as BoxDecoration).image.toString(),
+ 'DecorationImage(AssetImage(bundle: null, name: "asset"), ' // this just seemed like the easiest way to check all this...
+ 'ColorFilter.matrix([1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), '
+ 'Alignment.center, centerSlice: Rect.fromLTRB(5.0, 8.0, 105.0, 78.0), scale 1.0, opacity 1.0, FilterQuality.low)',
+ );
+ expect(
+ (tester.widgetList<DecoratedBox>(find.byType(DecoratedBox)).toList()[0].decoration as BoxDecoration).image.toString(),
+ 'DecorationImage(NetworkImage("x-invalid://", scale: 1.0), '
+ 'ColorFilter.mode(Color(0xff8811ff), BlendMode.xor), Alignment.center, scale 1.0, '
+ 'opacity 1.0, FilterQuality.low)',
+ );
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = Column(
+ children: [
+ Text(
+ text: [
+ 'Hello World Hello World Hello World Hello World Hello World Hello World Hello World',
+ 'Hello World Hello World Hello World Hello World Hello World Hello World Hello World',
+ ],
+ locale: "en-US",
+ style: {
+ fontFamilyFallback: [ "a", "b" ],
+ fontSize: 30.0,
+ },
+ strutStyle: {
+ fontSize: 50.0,
+ },
+ ),
+ Expanded(
+ flex: 2,
+ child: Text(
+ text: 'Aaaa Aaaaaaa Aaaaa',
+ locale: "en",
+ style: {
+ decoration: [ "underline", "overline" ],
+ decorationColor: 0xFF00FF00,
+ fontFeatures: [ { feature: 'sups' } ],
+ foreground: {
+ blendMode: 'color',
+ color: 0xFFEEDDCC,
+ colorFilter: { type: 'srgbToLinearGamma' },
+ filterQuality: "high",
+ isAntiAlias: true,
+ maskFilter: { type: 'blur' },
+ shader: { type: 'linear', rect: { x: 0.0, y: 0.0, w: 300.0, h: 200.0, } }
+ },
+ background: {
+ colorFilter: { type: 'custom' },
+ maskFilter: { type: 'custom' },
+ shader: { type: 'custom' },
+ },
+ },
+ ),
+ ),
+ Expanded(
+ flex: 1,
+ child: Text(
+ text: 'B',
+ locale: "en-latin-GB",
+ ),
+ ),
+ ],
+ );
+ '''));
+ await tester.pump();
+ expect(tester.firstWidget<Text>(find.byType(Text)).style!.fontFamilyFallback, <String>[ 'a', 'b' ]);
+ expect(tester.widgetList<Text>(find.byType(Text)).map<Locale>((Text widget) => widget.locale!), const <Locale>[Locale('en', 'US'), Locale('en'), Locale.fromSubtags(languageCode: 'en', scriptCode: 'latin', countryCode: 'GB')]);
+ await expectLater(
+ find.byType(RemoteWidget),
+ matchesGoldenFile('goldens/argument_decoders_test.text.png'),
+ );
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = GridView(
+ gridDelegate: { type: 'fixedCrossAxisCount', crossAxisCount: 3 },
+ children: [
+ ColoredBox(color: 0xFF118844),
+ ColoredBox(color: 0xFFEE8844),
+ ColoredBox(color: 0xFF882244),
+ ColoredBox(color: 0xFF449999),
+ ColoredBox(color: 0xFF330088),
+ ColoredBox(color: 0xFF8822CC),
+ ColoredBox(color: 0xFF330000),
+ ColoredBox(color: 0xFF992288),
+ ],
+ );
+ '''));
+ await tester.pump();
+ await expectLater(
+ find.byType(RemoteWidget),
+ matchesGoldenFile('goldens/argument_decoders_test.gridview.fixed.png'),
+ );
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = GridView(
+ gridDelegate: { type: 'maxCrossAxisExtent', maxCrossAxisExtent: 50.0 },
+ children: [
+ ColoredBox(color: 0xFF118844),
+ ColoredBox(color: 0xFFEE8844),
+ ColoredBox(color: 0xFF882244),
+ ColoredBox(color: 0xFF449999),
+ ColoredBox(color: 0xFF330088),
+ ColoredBox(color: 0xFF8822CC),
+ ColoredBox(color: 0xFF330000),
+ ColoredBox(color: 0xFF992288),
+ ],
+ );
+ '''));
+ await tester.pump();
+ await expectLater(
+ find.byType(RemoteWidget),
+ matchesGoldenFile('goldens/argument_decoders_test.gridview.max.png'),
+ );
+
+ int sawGridDelegateDecoder = 0;
+ ArgumentDecoders.gridDelegateDecoders['custom'] = (DataSource source, List<Object> key) {
+ sawGridDelegateDecoder += 1;
+ return null;
+ };
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = GridView(
+ gridDelegate: { type: 'custom' },
+ children: [
+ ColoredBox(color: 0xFF118844),
+ ColoredBox(color: 0xFFEE8844),
+ ColoredBox(color: 0xFF882244),
+ ColoredBox(color: 0xFF449999),
+ ColoredBox(color: 0xFF330088),
+ ColoredBox(color: 0xFF8822CC),
+ ColoredBox(color: 0xFF330000),
+ ColoredBox(color: 0xFF992288),
+ ],
+ );
+ '''));
+ expect(sawGridDelegateDecoder, 0);
+ await tester.pump();
+ expect(sawGridDelegateDecoder, 1);
+ await expectLater(
+ find.byType(RemoteWidget),
+ matchesGoldenFile('goldens/argument_decoders_test.gridview.custom.png'),
+ );
+ });
+}
diff --git a/packages/rfw/test/binary_test.dart b/packages/rfw/test/binary_test.dart
new file mode 100644
index 0000000..de8921c
--- /dev/null
+++ b/packages/rfw/test/binary_test.dart
@@ -0,0 +1,459 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is hand-formatted.
+
+import 'dart:typed_data';
+
+import 'package:flutter_test/flutter_test.dart';
+import 'package:rfw/formats.dart';
+
+void main() {
+ testWidgets('String example', (WidgetTester tester) async {
+ final Uint8List bytes = encodeDataBlob('Hello');
+ expect(bytes, <int>[ 0xFE, 0x52, 0x57, 0x44, 0x04, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x65, 0x6C, 0x6C, 0x6F ]);
+ final Object value = decodeDataBlob(bytes);
+ expect(value, isA<String>());
+ expect(value, 'Hello');
+ });
+
+ testWidgets('Map example', (WidgetTester tester) async {
+ final Uint8List bytes = encodeDataBlob(const <String, Object?>{ 'a': 15 });
+ expect(bytes, <int>[
+ 0xFE, 0x52, 0x57, 0x44, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x02, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ ]);
+ final Object value = decodeDataBlob(bytes);
+ expect(value, isA<DynamicMap>());
+ expect(value, const <String, Object?>{ 'a': 15 });
+ });
+
+ testWidgets('Signature check in decoders', (WidgetTester tester) async {
+ try {
+ decodeDataBlob(Uint8List.fromList(<int>[ 0xFE, 0x52, 0x46, 0x57, 0x04, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x65, 0x6C, 0x6C, 0x6F ]));
+ fail('did not throw exception');
+ } on FormatException catch (e) {
+ expect('$e', contains('File signature mismatch. Expected FE 52 57 44 but found FE 52 46 57.'));
+ }
+ try {
+ decodeLibraryBlob(Uint8List.fromList(<int>[ 0xFE, 0x52, 0x57, 0x44, 0x04, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x65, 0x6C, 0x6C, 0x6F ]));
+ fail('did not throw exception');
+ } on FormatException catch (e) {
+ expect('$e', contains('File signature mismatch. Expected FE 52 46 57 but found FE 52 57 44.'));
+ }
+ });
+
+ testWidgets('Trailing byte check', (WidgetTester tester) async {
+ try {
+ decodeDataBlob(Uint8List.fromList(<int>[ 0xFE, 0x52, 0x57, 0x44, 0x00, 0x00 ]));
+ fail('did not throw exception');
+ } on FormatException catch (e) {
+ expect('$e', contains('Unexpected trailing bytes after value.'));
+ }
+ try {
+ decodeLibraryBlob(Uint8List.fromList(<int>[ 0xFE, 0x52, 0x46, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]));
+ fail('did not throw exception');
+ } on FormatException catch (e) {
+ expect('$e', contains('Unexpected trailing bytes after constructors.'));
+ }
+ });
+
+ testWidgets('Incomplete files in signatures', (WidgetTester tester) async {
+ try {
+ decodeDataBlob(Uint8List.fromList(<int>[ 0xFE, 0x52, 0x57 ]));
+ fail('did not throw exception');
+ } on FormatException catch (e) {
+ expect('$e', contains('Could not read byte at offset 3: unexpected end of file.'));
+ }
+ try {
+ decodeLibraryBlob(Uint8List.fromList(<int>[ 0xFE, 0x52, 0x46 ]));
+ fail('did not throw exception');
+ } on FormatException catch (e) {
+ expect('$e', contains('Could not read byte at offset 3: unexpected end of file.'));
+ }
+ });
+
+ testWidgets('Incomplete files after signatures', (WidgetTester tester) async {
+ try {
+ decodeDataBlob(Uint8List.fromList(<int>[ 0xFE, 0x52, 0x57, 0x44 ]));
+ fail('did not throw exception');
+ } on FormatException catch (e) {
+ expect('$e', contains('Could not read byte at offset 4: unexpected end of file.'));
+ }
+ try {
+ decodeLibraryBlob(Uint8List.fromList(<int>[ 0xFE, 0x52, 0x46, 0x57 ]));
+ fail('did not throw exception');
+ } on FormatException catch (e) {
+ expect('$e', contains('Could not read int64 at offset 4: unexpected end of file.'));
+ }
+ });
+
+ testWidgets('Invalid value tag', (WidgetTester tester) async {
+ try {
+ decodeDataBlob(Uint8List.fromList(<int>[ 0xFE, 0x52, 0x57, 0x44, 0xCC ]));
+ fail('did not throw exception');
+ } on FormatException catch (e) {
+ expect('$e', contains('Unrecognized data type 0xCC while decoding blob.'));
+ }
+ });
+
+ testWidgets('Library encoder smoke test', (WidgetTester tester) async {
+ final Uint8List bytes = encodeLibraryBlob(const RemoteWidgetLibrary(<Import>[], <WidgetDeclaration>[]));
+ expect(bytes, <int>[ 0xFE, 0x52, 0x46, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]);
+ final RemoteWidgetLibrary value = decodeLibraryBlob(bytes);
+ expect(value.imports, isEmpty);
+ expect(value.widgets, isEmpty);
+ });
+
+ testWidgets('Library encoder: imports', (WidgetTester tester) async {
+ final Uint8List bytes = encodeLibraryBlob(const RemoteWidgetLibrary(<Import>[Import(LibraryName(<String>['a']))], <WidgetDeclaration>[]));
+ expect(bytes, <int>[
+ 0xFE, 0x52, 0x46, 0x57, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ ]);
+ final RemoteWidgetLibrary value = decodeLibraryBlob(bytes);
+ expect(value.imports, hasLength(1));
+ expect(value.imports.single.name, const LibraryName(<String>['a']));
+ expect(value.widgets, isEmpty);
+ });
+
+ testWidgets('Doubles', (WidgetTester tester) async {
+ final Uint8List bytes = encodeDataBlob(0.25);
+ expect(bytes, <int>[ 0xFE, 0x52, 0x57, 0x44, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0x3F ]);
+ final Object value = decodeDataBlob(bytes);
+ expect(value, isA<double>());
+ expect(value, 0.25);
+ });
+
+ testWidgets('Library decoder: invalid widget declaration root', (WidgetTester tester) async {
+ final Uint8List bytes = Uint8List.fromList(<int>[
+ 0xfe, 0x52, 0x46, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF,
+ ]);
+ try {
+ decodeLibraryBlob(bytes);
+ } on FormatException catch (e) {
+ expect('$e', contains('Unrecognized data type 0xEF while decoding widget declaration root.'));
+ }
+ });
+
+ testWidgets('Library encoder: args references', (WidgetTester tester) async {
+ final Uint8List bytes = encodeLibraryBlob(const RemoteWidgetLibrary(<Import>[], <WidgetDeclaration>[
+ WidgetDeclaration('a', null, ConstructorCall('b', <String, Object?>{ 'c': <Object?>[ ArgsReference(<Object>['d', 5]) ] })),
+ ]));
+ expect(bytes, <int>[
+ 0xfe, 0x52, 0x46, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63,
+ 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x02, 0x05, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ ]);
+ final RemoteWidgetLibrary value = decodeLibraryBlob(bytes);
+ expect(value.imports, isEmpty);
+ expect(value.widgets, hasLength(1));
+ expect(value.widgets.first.name, 'a');
+ expect(value.widgets.first.initialState, isNull);
+ expect(value.widgets.first.root, isA<ConstructorCall>());
+ expect((value.widgets.first.root as ConstructorCall).name, 'b');
+ expect((value.widgets.first.root as ConstructorCall).arguments, hasLength(1));
+ expect((value.widgets.first.root as ConstructorCall).arguments.keys, <Object?>['c']);
+ expect((value.widgets.first.root as ConstructorCall).arguments['c'], hasLength(1));
+ expect(((value.widgets.first.root as ConstructorCall).arguments['c']! as DynamicList)[0], isA<ArgsReference>());
+ expect((((value.widgets.first.root as ConstructorCall).arguments['c']! as DynamicList)[0]! as ArgsReference).parts, hasLength(2));
+ expect((((value.widgets.first.root as ConstructorCall).arguments['c']! as DynamicList)[0]! as ArgsReference).parts[0], 'd');
+ expect((((value.widgets.first.root as ConstructorCall).arguments['c']! as DynamicList)[0]! as ArgsReference).parts[1], 5);
+ });
+
+ testWidgets('Library encoder: invalid args references', (WidgetTester tester) async {
+ try {
+ encodeLibraryBlob(const RemoteWidgetLibrary(<Import>[], <WidgetDeclaration>[
+ WidgetDeclaration('a', null, ConstructorCall('b', <String, Object?>{ 'c': <Object?>[ ArgsReference(<Object>[false]) ] })),
+ ]));
+ } on StateError catch (e) {
+ expect('$e', contains('Unexpected type bool while encoding blob.'));
+ }
+ });
+
+ testWidgets('Library decoder: invalid args references', (WidgetTester tester) async {
+ final Uint8List bytes = Uint8List.fromList(<int>[
+ 0xfe, 0x52, 0x46, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63,
+ 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xAC,
+ ]);
+ try {
+ decodeLibraryBlob(bytes);
+ } on FormatException catch (e) {
+ expect('$e', contains('Invalid reference type 0xAC while decoding blob.'));
+ }
+ });
+
+ testWidgets('Library encoder: switches', (WidgetTester tester) async {
+ final Uint8List bytes = encodeLibraryBlob(const RemoteWidgetLibrary(<Import>[], <WidgetDeclaration>[
+ WidgetDeclaration('a', null, Switch('b', <Object?, Object>{ null: 'c' })),
+ ]));
+ expect(bytes, <int>[
+ 0xfe, 0x52, 0x46, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x63,
+ ]);
+ final RemoteWidgetLibrary value = decodeLibraryBlob(bytes);
+ expect(value.imports, isEmpty);
+ expect(value.widgets, hasLength(1));
+ expect(value.widgets.first.name, 'a');
+ expect(value.widgets.first.initialState, isNull);
+ expect(value.widgets.first.root, isA<Switch>());
+ expect((value.widgets.first.root as Switch).input, 'b');
+ expect((value.widgets.first.root as Switch).outputs, hasLength(1));
+ expect((value.widgets.first.root as Switch).outputs.keys, <Object?>[null]);
+ expect((value.widgets.first.root as Switch).outputs[null], 'c');
+ });
+
+ testWidgets('Library encoder: switches', (WidgetTester tester) async {
+ final Uint8List bytes = encodeLibraryBlob(const RemoteWidgetLibrary(<Import>[], <WidgetDeclaration>[
+ WidgetDeclaration('a', null, Switch('b', <Object?, Object>{ 'c': 'd' })),
+ ]));
+ expect(bytes, <int>[
+ 0xfe, 0x52, 0x46, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x63, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64,
+ ]);
+ final RemoteWidgetLibrary value = decodeLibraryBlob(bytes);
+ expect(value.imports, isEmpty);
+ expect(value.widgets, hasLength(1));
+ expect(value.widgets.first.name, 'a');
+ expect(value.widgets.first.initialState, isNull);
+ expect(value.widgets.first.root, isA<Switch>());
+ expect((value.widgets.first.root as Switch).input, 'b');
+ expect((value.widgets.first.root as Switch).outputs, hasLength(1));
+ expect((value.widgets.first.root as Switch).outputs.keys, <Object?>['c']);
+ expect((value.widgets.first.root as Switch).outputs['c'], 'd');
+ });
+
+ testWidgets('Bools', (WidgetTester tester) async {
+ final Uint8List bytes = encodeDataBlob(const <Object?>[ false, true ]);
+ expect(bytes, <int>[
+ 0xFE, 0x52, 0x57, 0x44, 0x05, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ ]);
+ final Object value = decodeDataBlob(bytes);
+ expect(value, isA<DynamicList>());
+ expect(value, const <Object?>{ false, true });
+ });
+
+ testWidgets('Library encoder: loops', (WidgetTester tester) async {
+ final Uint8List bytes = encodeLibraryBlob(const RemoteWidgetLibrary(<Import>[], <WidgetDeclaration>[
+ WidgetDeclaration('a', null, ConstructorCall('b', <String, Object?>{ 'c': <Object?>[
+ Loop(<Object?>[], ConstructorCall('d', <String, Object?>{ 'e': LoopReference(0, <Object>[]) })),
+ ] })),
+ ]));
+ expect(bytes, <int>[
+ 0xfe, 0x52, 0x46, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63,
+ 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x0c, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ ]);
+ final RemoteWidgetLibrary value = decodeLibraryBlob(bytes);
+ expect(value.imports, isEmpty);
+ expect(value.widgets, hasLength(1));
+ expect(value.widgets.first.name, 'a');
+ expect(value.widgets.first.initialState, isNull);
+ expect(value.widgets.first.root, isA<ConstructorCall>());
+ expect((value.widgets.first.root as ConstructorCall).name, 'b');
+ expect((value.widgets.first.root as ConstructorCall).arguments, hasLength(1));
+ expect((value.widgets.first.root as ConstructorCall).arguments.keys, <Object?>['c']);
+ expect((value.widgets.first.root as ConstructorCall).arguments['c'], hasLength(1));
+ expect(((value.widgets.first.root as ConstructorCall).arguments['c']! as DynamicList)[0], isA<Loop>());
+ expect((((value.widgets.first.root as ConstructorCall).arguments['c']! as DynamicList)[0]! as Loop).input, isEmpty);
+ expect((((value.widgets.first.root as ConstructorCall).arguments['c']! as DynamicList)[0]! as Loop).output, isA<ConstructorCall>());
+ expect(((((value.widgets.first.root as ConstructorCall).arguments['c']! as DynamicList)[0]! as Loop).output as ConstructorCall).name, 'd');
+ expect(((((value.widgets.first.root as ConstructorCall).arguments['c']! as DynamicList)[0]! as Loop).output as ConstructorCall).arguments, hasLength(1));
+ expect(((((value.widgets.first.root as ConstructorCall).arguments['c']! as DynamicList)[0]! as Loop).output as ConstructorCall).arguments['e'], isA<LoopReference>());
+ expect((((((value.widgets.first.root as ConstructorCall).arguments['c']! as DynamicList)[0]! as Loop).output as ConstructorCall).arguments['e']! as LoopReference).loop, 0);
+ expect((((((value.widgets.first.root as ConstructorCall).arguments['c']! as DynamicList)[0]! as Loop).output as ConstructorCall).arguments['e']! as LoopReference).parts, isEmpty);
+ });
+
+ testWidgets('Library encoder: data references', (WidgetTester tester) async {
+ final Uint8List bytes = encodeLibraryBlob(const RemoteWidgetLibrary(<Import>[], <WidgetDeclaration>[
+ WidgetDeclaration('a', null, ConstructorCall('b', <String, Object?>{ 'c': DataReference(<Object>['d']) })),
+ ]));
+ expect(bytes, <int>[
+ 0xfe, 0x52, 0x46, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63,
+ 0x0B, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x64,
+ ]);
+ final RemoteWidgetLibrary value = decodeLibraryBlob(bytes);
+ expect(value.imports, isEmpty);
+ expect(value.widgets, hasLength(1));
+ expect(value.widgets.first.name, 'a');
+ expect(value.widgets.first.initialState, isNull);
+ expect(value.widgets.first.root, isA<ConstructorCall>());
+ expect((value.widgets.first.root as ConstructorCall).name, 'b');
+ expect((value.widgets.first.root as ConstructorCall).arguments, hasLength(1));
+ expect((value.widgets.first.root as ConstructorCall).arguments.keys, <Object?>['c']);
+ expect((value.widgets.first.root as ConstructorCall).arguments['c'], isA<DataReference>());
+ expect(((value.widgets.first.root as ConstructorCall).arguments['c']! as DataReference).parts, const <Object>[ 'd' ]);
+ });
+
+ testWidgets('Library encoder: state references', (WidgetTester tester) async {
+ final Uint8List bytes = encodeLibraryBlob(const RemoteWidgetLibrary(<Import>[], <WidgetDeclaration>[
+ WidgetDeclaration('a', null, ConstructorCall('b', <String, Object?>{ 'c': StateReference(<Object>['d']) })),
+ ]));
+ expect(bytes, <int>[
+ 0xfe, 0x52, 0x46, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63,
+ 0x0D, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x64,
+ ]);
+ final RemoteWidgetLibrary value = decodeLibraryBlob(bytes);
+ expect(value.imports, isEmpty);
+ expect(value.widgets, hasLength(1));
+ expect(value.widgets.first.name, 'a');
+ expect(value.widgets.first.initialState, isNull);
+ expect(value.widgets.first.root, isA<ConstructorCall>());
+ expect((value.widgets.first.root as ConstructorCall).name, 'b');
+ expect((value.widgets.first.root as ConstructorCall).arguments, hasLength(1));
+ expect((value.widgets.first.root as ConstructorCall).arguments.keys, <Object?>['c']);
+ expect((value.widgets.first.root as ConstructorCall).arguments['c'], isA<StateReference>());
+ expect(((value.widgets.first.root as ConstructorCall).arguments['c']! as StateReference).parts, const <Object>[ 'd' ]);
+ });
+
+ testWidgets('Library encoder: event handler', (WidgetTester tester) async {
+ final Uint8List bytes = encodeLibraryBlob(const RemoteWidgetLibrary(<Import>[], <WidgetDeclaration>[
+ WidgetDeclaration('a', null, ConstructorCall('b', <String, Object?>{ 'c': EventHandler('d', <String, Object?>{}) })),
+ ]));
+ expect(bytes, <int>[
+ 0xfe, 0x52, 0x46, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63,
+ 0x0E, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+ ]);
+ final RemoteWidgetLibrary value = decodeLibraryBlob(bytes);
+ expect(value.imports, isEmpty);
+ expect(value.widgets, hasLength(1));
+ expect(value.widgets.first.name, 'a');
+ expect(value.widgets.first.initialState, isNull);
+ expect(value.widgets.first.root, isA<ConstructorCall>());
+ expect((value.widgets.first.root as ConstructorCall).name, 'b');
+ expect((value.widgets.first.root as ConstructorCall).arguments, hasLength(1));
+ expect((value.widgets.first.root as ConstructorCall).arguments.keys, <Object?>['c']);
+ expect((value.widgets.first.root as ConstructorCall).arguments['c'], isA<EventHandler>());
+ expect(((value.widgets.first.root as ConstructorCall).arguments['c']! as EventHandler).eventName, 'd');
+ expect(((value.widgets.first.root as ConstructorCall).arguments['c']! as EventHandler).eventArguments, isEmpty);
+ });
+
+ testWidgets('Library encoder: state setter', (WidgetTester tester) async {
+ final Uint8List bytes = encodeLibraryBlob(const RemoteWidgetLibrary(<Import>[], <WidgetDeclaration>[
+ WidgetDeclaration('a', null, ConstructorCall('b', <String, Object?>{ 'c': SetStateHandler(StateReference(<Object>['d']), false) })),
+ ]));
+ expect(bytes, <int>[
+ 0xfe, 0x52, 0x46, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63,
+ 0x11, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x64, 0x00,
+ ]);
+ final RemoteWidgetLibrary value = decodeLibraryBlob(bytes);
+ expect(value.imports, isEmpty);
+ expect(value.widgets, hasLength(1));
+ expect(value.widgets.first.name, 'a');
+ expect(value.widgets.first.initialState, isNull);
+ expect(value.widgets.first.root, isA<ConstructorCall>());
+ expect((value.widgets.first.root as ConstructorCall).name, 'b');
+ expect((value.widgets.first.root as ConstructorCall).arguments, hasLength(1));
+ expect((value.widgets.first.root as ConstructorCall).arguments.keys, <Object?>['c']);
+ expect((value.widgets.first.root as ConstructorCall).arguments['c'], isA<SetStateHandler>());
+ expect(((value.widgets.first.root as ConstructorCall).arguments['c']! as SetStateHandler).stateReference.parts, <Object>['d']);
+ expect(((value.widgets.first.root as ConstructorCall).arguments['c']! as SetStateHandler).value, false);
+ });
+
+ testWidgets('Library encoder: switch', (WidgetTester tester) async {
+ final Uint8List bytes = encodeLibraryBlob(const RemoteWidgetLibrary(<Import>[], <WidgetDeclaration>[
+ WidgetDeclaration('a', null, ConstructorCall('b', <String, Object?>{ 'c': Switch(false, <Object?, Object>{} ) })),
+ ]));
+ expect(bytes, <int>[
+ 0xfe, 0x52, 0x46, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63,
+ 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ ]);
+ final RemoteWidgetLibrary value = decodeLibraryBlob(bytes);
+ expect(value.imports, isEmpty);
+ expect(value.widgets, hasLength(1));
+ expect(value.widgets.first.name, 'a');
+ expect(value.widgets.first.initialState, isNull);
+ expect(value.widgets.first.root, isA<ConstructorCall>());
+ expect((value.widgets.first.root as ConstructorCall).name, 'b');
+ expect((value.widgets.first.root as ConstructorCall).arguments, hasLength(1));
+ expect((value.widgets.first.root as ConstructorCall).arguments.keys, <Object?>['c']);
+ expect((value.widgets.first.root as ConstructorCall).arguments['c'], isA<Switch>());
+ expect(((value.widgets.first.root as ConstructorCall).arguments['c']! as Switch).input, false);
+ expect(((value.widgets.first.root as ConstructorCall).arguments['c']! as Switch).outputs, isEmpty);
+ });
+
+ testWidgets('Library encoder: initial state', (WidgetTester tester) async {
+ final Uint8List bytes = encodeLibraryBlob(const RemoteWidgetLibrary(<Import>[], <WidgetDeclaration>[
+ WidgetDeclaration('a', <String, Object?>{}, ConstructorCall('b', <String, Object?>{})),
+ ]));
+ expect(bytes, <int>[
+ 0xfe, 0x52, 0x46, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ ]);
+ final RemoteWidgetLibrary value = decodeLibraryBlob(bytes);
+ expect(value.imports, isEmpty);
+ expect(value.widgets, hasLength(1));
+ expect(value.widgets.first.name, 'a');
+ expect(value.widgets.first.initialState, isNull);
+ expect(value.widgets.first.root, isA<ConstructorCall>());
+ expect((value.widgets.first.root as ConstructorCall).name, 'b');
+ expect((value.widgets.first.root as ConstructorCall).arguments, isEmpty);
+ });
+
+ testWidgets('Library encoder: initial state', (WidgetTester tester) async {
+ final Uint8List bytes = encodeLibraryBlob(const RemoteWidgetLibrary(<Import>[], <WidgetDeclaration>[
+ WidgetDeclaration('a', <String, Object?>{ 'b': false }, ConstructorCall('c', <String, Object?>{})),
+ ]));
+ expect(bytes, <int>[
+ 0xfe, 0x52, 0x46, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x09,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00,
+ ]);
+ final RemoteWidgetLibrary value = decodeLibraryBlob(bytes);
+ expect(value.imports, isEmpty);
+ expect(value.widgets, hasLength(1));
+ expect(value.widgets.first.name, 'a');
+ expect(value.widgets.first.initialState, isNotNull);
+ expect(value.widgets.first.initialState!, hasLength(1));
+ expect(value.widgets.first.initialState!['b'], false);
+ expect(value.widgets.first.root, isA<ConstructorCall>());
+ expect((value.widgets.first.root as ConstructorCall).name, 'c');
+ expect((value.widgets.first.root as ConstructorCall).arguments, isEmpty);
+ });
+}
diff --git a/packages/rfw/test/core_widgets_test.dart b/packages/rfw/test/core_widgets_test.dart
new file mode 100644
index 0000000..39967d5
--- /dev/null
+++ b/packages/rfw/test/core_widgets_test.dart
@@ -0,0 +1,264 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is hand-formatted.
+
+import 'dart:typed_data';
+
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:rfw/formats.dart' show parseLibraryFile;
+import 'package:rfw/rfw.dart';
+
+void main() {
+ testWidgets('Core widgets', (WidgetTester tester) async {
+ final Runtime runtime = Runtime()
+ ..update(const LibraryName(<String>['core']), createCoreWidgets());
+ final DynamicContent data = DynamicContent();
+ final List<String> eventLog = <String>[];
+ await tester.pumpWidget(
+ RemoteWidget(
+ runtime: runtime,
+ data: data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
+ onEvent: (String eventName, DynamicMap eventArguments) {
+ eventLog.add('$eventName $eventArguments');
+ },
+ ),
+ );
+ expect(tester.takeException().toString(), contains('Could not find remote widget named'));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = GestureDetector(
+ onTapDown: event 'tapdown' { },
+ onTapUp: event 'tapup' { },
+ onTap: event 'tap' { },
+ child: ColoredBox(),
+ );
+ '''));
+ await tester.pump();
+ await tester.tap(find.byType(ColoredBox));
+ expect(eventLog, <String>['tapdown {}', 'tapup {}', 'tap {}']);
+ eventLog.clear();
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = IntrinsicHeight();
+ '''));
+ await tester.pump();
+ expect(find.byType(IntrinsicHeight), findsOneWidget);
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = IntrinsicWidth();
+ '''));
+ await tester.pump();
+ expect(find.byType(IntrinsicWidth), findsOneWidget);
+
+ ArgumentDecoders.imageProviderDecoders['beepboop'] = (DataSource source, List<Object> key) {
+ return MemoryImage(Uint8List.fromList(<int>[
+ 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00, 0x80, 0xff, 0x00, 0xc0, 0xc0, 0xc0,
+ 0x00, 0x00, 0x00, 0x21, 0xf9, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x44, 0x01, 0x00, 0x3b,
+ ]));
+ };
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = Image(source: 'beepboop');
+ '''));
+ await tester.pump();
+ expect(find.byType(Image), findsOneWidget);
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = SingleChildScrollView(child: ListBody());
+ '''));
+ await tester.pump();
+ expect(find.byType(SingleChildScrollView), findsOneWidget);
+ expect(find.byType(ListBody), findsOneWidget);
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = Directionality(
+ textDirection: "rtl",
+ child: ListView(
+ children: [
+ Container(height: 67.0),
+ Container(height: 67.0),
+ Container(height: 67.0),
+ Container(height: 67.0),
+ Container(height: 67.0),
+ Container(height: 67.0),
+ Container(height: 67.0),
+ Container(height: 67.0),
+ Container(height: 67.0),
+ Container(height: 67.0), // number 10 is not visible
+ ],
+ ),
+ );
+ '''));
+ await tester.pump();
+ expect(find.byType(ListView), findsOneWidget);
+ expect(find.byType(Container), findsNWidgets(9));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = Opacity(onEnd: event 'end' {});
+ '''));
+ await tester.pump();
+ expect(tester.widget<AnimatedOpacity>(find.byType(AnimatedOpacity)).onEnd, isNot(isNull));
+ expect(eventLog, isEmpty);
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = Directionality(textDirection: "ltr", child: Padding(padding: [12.0]));
+ '''));
+ await tester.pump();
+ expect(tester.widget<Padding>(find.byType(Padding)).padding.resolve(TextDirection.ltr), const EdgeInsets.all(12.0));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = Directionality(textDirection: "ltr", child: Padding(padding: [24.0]));
+ '''));
+ await tester.pump();
+ expect(tester.widget<Padding>(find.byType(Padding)).padding.resolve(TextDirection.ltr), const EdgeInsets.all(12.0));
+ await tester.pump(const Duration(seconds: 4));
+ expect(tester.widget<Padding>(find.byType(Padding)).padding.resolve(TextDirection.ltr), const EdgeInsets.all(24.0));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = Placeholder();
+ '''));
+ await tester.pump();
+ expect(find.byType(Placeholder), findsOneWidget);
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = Directionality(
+ textDirection: "ltr",
+ child: Stack(
+ children: [
+ Positioned(
+ start: 0.0,
+ top: 0.0,
+ width: 10.0,
+ height: 10.0,
+ child: ColoredBox(),
+ ),
+ ],
+ ),
+ );
+ '''));
+ await tester.pump();
+ expect(find.byType(Stack), findsOneWidget);
+ expect(find.byType(Positioned), findsOneWidget);
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = Directionality(
+ textDirection: "rtl",
+ child: Rotation(turns: 0.0),
+ );
+ '''));
+ await tester.pump();
+ expect(find.byType(AnimatedRotation), findsOneWidget);
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = Directionality(
+ textDirection: "rtl",
+ child: Rotation(turns: 1.0, onEnd: event 'end' { from: "rotation" }),
+ );
+ '''));
+ await tester.pump();
+ expect(find.byType(AnimatedRotation), findsOneWidget);
+ expect(eventLog, isEmpty);
+ await tester.pump(const Duration(seconds: 1));
+ expect(eventLog, <String>['end {from: rotation}']);
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = Directionality(
+ textDirection: "rtl",
+ child: Row(
+ crossAxisAlignment: "start",
+ children: [SizedBox(width: 10.0)]
+ ),
+ );
+ '''));
+ await tester.pump();
+ expect(tester.getTopLeft(find.byType(SizedBox)), const Offset(790.0, 0.0));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = Directionality(
+ textDirection: "rtl",
+ child: Row(
+ crossAxisAlignment: "start",
+ children: [Spacer()]
+ ),
+ );
+ '''));
+ await tester.pump();
+ expect(tester.getTopLeft(find.byType(SizedBox)), const Offset(0.0, 0.0));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = SizedBoxExpand();
+ '''));
+ await tester.pump();
+ expect(find.byType(SizedBox), findsOneWidget);
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = SizedBoxShrink();
+ '''));
+ await tester.pump();
+ expect(find.byType(SizedBox), findsOneWidget);
+ });
+
+ testWidgets('More core widgets', (WidgetTester tester) async {
+ final Runtime runtime = Runtime()
+ ..update(const LibraryName(<String>['core']), createCoreWidgets());
+ final DynamicContent data = DynamicContent();
+ final List<String> eventLog = <String>[];
+ await tester.pumpWidget(
+ MaterialApp(
+ home: RemoteWidget(
+ runtime: runtime,
+ data: data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
+ onEvent: (String eventName, DynamicMap eventArguments) {
+ eventLog.add('$eventName $eventArguments');
+ },
+ ),
+ ),
+ );
+ expect(tester.takeException().toString(), contains('Could not find remote widget named'));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = SafeArea(
+ child: SizedBoxShrink(),
+ );
+ '''));
+ await tester.pump();
+ expect(find.byType(SafeArea), findsOneWidget);
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = Scale();
+ '''));
+ await tester.pump();
+ expect(find.byType(AnimatedScale), findsOneWidget);
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = Wrap();
+ '''));
+ await tester.pump();
+ expect(find.byType(Wrap), findsOneWidget);
+ });
+}
diff --git a/packages/rfw/test/goldens/argument_decoders_test.containers.png b/packages/rfw/test/goldens/argument_decoders_test.containers.png
new file mode 100644
index 0000000..01d4ef7
--- /dev/null
+++ b/packages/rfw/test/goldens/argument_decoders_test.containers.png
Binary files differ
diff --git a/packages/rfw/test/goldens/argument_decoders_test.gridview.custom.png b/packages/rfw/test/goldens/argument_decoders_test.gridview.custom.png
new file mode 100644
index 0000000..d879701
--- /dev/null
+++ b/packages/rfw/test/goldens/argument_decoders_test.gridview.custom.png
Binary files differ
diff --git a/packages/rfw/test/goldens/argument_decoders_test.gridview.fixed.png b/packages/rfw/test/goldens/argument_decoders_test.gridview.fixed.png
new file mode 100644
index 0000000..687b9c2
--- /dev/null
+++ b/packages/rfw/test/goldens/argument_decoders_test.gridview.fixed.png
Binary files differ
diff --git a/packages/rfw/test/goldens/argument_decoders_test.gridview.max.png b/packages/rfw/test/goldens/argument_decoders_test.gridview.max.png
new file mode 100644
index 0000000..5c117b7
--- /dev/null
+++ b/packages/rfw/test/goldens/argument_decoders_test.gridview.max.png
Binary files differ
diff --git a/packages/rfw/test/goldens/argument_decoders_test.text.png b/packages/rfw/test/goldens/argument_decoders_test.text.png
new file mode 100644
index 0000000..3107fe1
--- /dev/null
+++ b/packages/rfw/test/goldens/argument_decoders_test.text.png
Binary files differ
diff --git a/packages/rfw/test/goldens/material_test.drawer.png b/packages/rfw/test/goldens/material_test.drawer.png
new file mode 100644
index 0000000..db50253
--- /dev/null
+++ b/packages/rfw/test/goldens/material_test.drawer.png
Binary files differ
diff --git a/packages/rfw/test/goldens/material_test.scaffold.png b/packages/rfw/test/goldens/material_test.scaffold.png
new file mode 100644
index 0000000..befab3c
--- /dev/null
+++ b/packages/rfw/test/goldens/material_test.scaffold.png
Binary files differ
diff --git a/packages/rfw/test/material_widgets_test.dart b/packages/rfw/test/material_widgets_test.dart
new file mode 100644
index 0000000..9ce6bd0
--- /dev/null
+++ b/packages/rfw/test/material_widgets_test.dart
@@ -0,0 +1,144 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:rfw/formats.dart' show parseLibraryFile;
+import 'package:rfw/rfw.dart';
+
+void main() {
+ testWidgets('Material widgets', (WidgetTester tester) async {
+ final Runtime runtime = Runtime()
+ ..update(const LibraryName(<String>['core']), createCoreWidgets())
+ ..update(
+ const LibraryName(<String>['material']), createMaterialWidgets());
+ final DynamicContent data = DynamicContent();
+ final List<String> eventLog = <String>[];
+ await tester.pumpWidget(
+ MaterialApp(
+ home: RemoteWidget(
+ runtime: runtime,
+ data: data,
+ widget: const FullyQualifiedWidgetName(
+ LibraryName(<String>['test']), 'root'),
+ onEvent: (String eventName, DynamicMap eventArguments) {
+ eventLog.add('$eventName $eventArguments');
+ },
+ ),
+ ),
+ );
+ expect(tester.takeException().toString(),
+ contains('Could not find remote widget named'));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ import material;
+ widget root = Scaffold(
+ appBar: AppBar(
+ title: Text(text: 'Title'),
+ flexibleSpace: Placeholder(),
+ bottom: SizedBox(height: 56.0, child: Placeholder()),
+ ),
+ drawer: Drawer(
+ child: ListView(
+ children: [
+ DrawerHeader(),
+ ListTile(
+ visualDensity: 'adaptivePlatformDensity',
+ title: Text(text: 'title'),
+ subtitle: Text(text: 'title'),
+ ),
+ ListTile(
+ visualDensity: 'comfortable',
+ title: Text(text: 'title'),
+ subtitle: Text(text: 'title'),
+ ),
+ ListTile(
+ visualDensity: 'compact',
+ title: Text(text: 'title'),
+ subtitle: Text(text: 'title'),
+ ),
+ ListTile(
+ visualDensity: 'standard',
+ title: Text(text: 'title'),
+ subtitle: Text(text: 'title'),
+ ),
+ ListTile(
+ visualDensity: { horizontal: -4.0, vertical: 4.0 },
+ title: Text(text: 'title'),
+ subtitle: Text(text: 'title'),
+ ),
+ AboutListTile(),
+ ],
+ ),
+ ),
+ body: ListView(
+ children: [
+ Card(
+ margin: [20.0],
+ child: ListBody(
+ children: [
+ ButtonBar(
+ children: [
+ ElevatedButton(
+ onPressed: event 'button' { },
+ child: Text(text: 'Elevated'),
+ ),
+ OutlinedButton(
+ onPressed: event 'button' { },
+ child: Text(text: 'Outlined'),
+ ),
+ TextButton(
+ onPressed: event 'button' { },
+ child: Text(text: 'Text'),
+ ),
+ VerticalDivider(),
+ InkWell(
+ child: Text(text: 'Ink'),
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ Divider(),
+ Padding(
+ padding: [20.0],
+ child: Center(
+ child: CircularProgressIndicator(
+ value: 0.6,
+ ),
+ ),
+ ),
+ Divider(),
+ Padding(
+ padding: [20.0],
+ child: Center(
+ child: LinearProgressIndicator(
+ value: 0.6,
+ ),
+ ),
+ ),
+ ],
+ ),
+ floatingActionButton: FloatingActionButton(
+ onPressed: event 'fab' {},
+ child: Placeholder(),
+ ),
+ );
+ '''));
+ await tester.pump();
+ await expectLater(
+ find.byType(RemoteWidget),
+ matchesGoldenFile('goldens/material_test.scaffold.png'),
+ );
+ await tester.tapAt(const Offset(20.0, 20.0));
+ await tester.pump();
+ await tester.pump(const Duration(seconds: 1));
+ await expectLater(
+ find.byType(RemoteWidget),
+ matchesGoldenFile('goldens/material_test.drawer.png'),
+ );
+ });
+}
diff --git a/packages/rfw/test/model_test.dart b/packages/rfw/test/model_test.dart
new file mode 100644
index 0000000..3931b66
--- /dev/null
+++ b/packages/rfw/test/model_test.dart
@@ -0,0 +1,104 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is hand-formatted.
+
+import 'package:flutter_test/flutter_test.dart';
+import 'package:rfw/formats.dart';
+
+void main() {
+ testWidgets('$LibraryName', (WidgetTester tester) async {
+ T deconst<T>(T value) => value;
+ final LibraryName a = LibraryName(<String>['core', deconst<String>('widgets')]);
+ final LibraryName b = LibraryName(<String>['core', deconst<String>('widgets')]);
+ final LibraryName c = LibraryName(<String>['core', deconst<String>('material')]);
+ const LibraryName d = LibraryName(<String>['core']);
+ expect('$a', 'core.widgets');
+ expect('$c', 'core.material');
+ expect(a, equals(b));
+ expect(a.hashCode, equals(b.hashCode));
+ expect(a, isNot(equals(c)));
+ expect(a.hashCode, isNot(equals(c.hashCode)));
+ expect(a.compareTo(b), 0);
+ expect(b.compareTo(a), 0);
+ expect(a.compareTo(c), 1);
+ expect(c.compareTo(a), -1);
+ expect(b.compareTo(c), 1);
+ expect(c.compareTo(b), -1);
+ expect(a.compareTo(d), 1);
+ expect(b.compareTo(d), 1);
+ expect(c.compareTo(d), 1);
+ expect(d.compareTo(a), -1);
+ expect(d.compareTo(b), -1);
+ expect(d.compareTo(c), -1);
+ });
+
+ testWidgets('$FullyQualifiedWidgetName', (WidgetTester tester) async {
+ const FullyQualifiedWidgetName aa = FullyQualifiedWidgetName(LibraryName(<String>['a']), 'a');
+ const FullyQualifiedWidgetName ab = FullyQualifiedWidgetName(LibraryName(<String>['a']), 'b');
+ const FullyQualifiedWidgetName bb = FullyQualifiedWidgetName(LibraryName(<String>['b']), 'b');
+ expect('$aa', 'a:a');
+ expect(aa, isNot(equals(bb)));
+ expect(aa.hashCode, isNot(equals(bb.hashCode)));
+ expect(aa.compareTo(aa), 0);
+ expect(aa.compareTo(ab), -1);
+ expect(aa.compareTo(bb), -1);
+ expect(ab.compareTo(aa), 1);
+ expect(ab.compareTo(ab), 0);
+ expect(ab.compareTo(bb), -1);
+ expect(bb.compareTo(aa), 1);
+ expect(bb.compareTo(ab), 1);
+ expect(bb.compareTo(bb), 0);
+ });
+
+ testWidgets('toStrings', (WidgetTester tester) async {
+ expect('$missing', '<missing>');
+ expect('${const Loop(0, 1)}', '...for loop in 0: 1');
+ expect('${const Switch(0, <Object?, Object>{1: 2})}', 'switch 0 {1: 2}');
+ expect('${const ConstructorCall("a", <String, Object>{})}', 'a({})');
+ expect('${const ArgsReference(<Object>["a"])}', 'args.a');
+ expect('${const BoundArgsReference(false, <Object>["a"])}', 'args(false).a');
+ expect('${const DataReference(<Object>["a"])}', 'data.a');
+ expect('${const LoopReference(0, <Object>["a"])}', 'loop0.a');
+ expect('${const BoundLoopReference(0, <Object>["a"])}', 'loop(0).a');
+ expect('${const StateReference(<Object>["a"])}', 'state.a');
+ expect('${const BoundStateReference(0, <Object>["a"])}', 'state^0.a');
+ expect('${const EventHandler("a", <String, Object?>{})}', 'event a {}');
+ expect('${const SetStateHandler(StateReference(<Object>["a"]), false)}', 'set state.a = false');
+ expect('${const Import(LibraryName(<String>["a"]))}', 'import a;');
+ expect('${const WidgetDeclaration("a", null, ConstructorCall("b", <String, Object>{}))}', 'widget a = b({});');
+ expect('${const WidgetDeclaration("a", <String, Object?>{ "x": false }, ConstructorCall("b", <String, Object>{}))}', 'widget a = b({});');
+ expect('${const RemoteWidgetLibrary(<Import>[Import(LibraryName(<String>["a"]))], <WidgetDeclaration>[WidgetDeclaration("a", null, ConstructorCall("b", <String, Object>{}))])}', 'import a;\nwidget a = b({});');
+ });
+
+ testWidgets('$BoundArgsReference', (WidgetTester tester) async {
+ final Object target = Object();
+ final BoundArgsReference result = const ArgsReference(<Object>[0]).bind(target);
+ expect(result.arguments, target);
+ expect(result.parts, const <Object>[0]);
+ });
+
+ testWidgets('$DataReference', (WidgetTester tester) async {
+ final DataReference result = const DataReference(<Object>[0]).constructReference(<Object>[1]);
+ expect(result.parts, const <Object>[0, 1]);
+ });
+
+ testWidgets('$LoopReference', (WidgetTester tester) async {
+ final LoopReference result = const LoopReference(9, <Object>[0]).constructReference(<Object>[1]);
+ expect(result.parts, const <Object>[0, 1]);
+ });
+
+ testWidgets('$BoundLoopReference', (WidgetTester tester) async {
+ final Object target = Object();
+ final BoundLoopReference result = const LoopReference(9, <Object>[0]).bind(target).constructReference(<Object>[1]);
+ expect(result.value, target);
+ expect(result.parts, const <Object>[0, 1]);
+ });
+
+ testWidgets('$BoundStateReference', (WidgetTester tester) async {
+ final BoundStateReference result = const StateReference(<Object>[0]).bind(9).constructReference(<Object>[1]);
+ expect(result.depth, 9);
+ expect(result.parts, const <Object>[0, 1]);
+ });
+}
diff --git a/packages/rfw/test/remote_widget_test.dart b/packages/rfw/test/remote_widget_test.dart
new file mode 100644
index 0000000..a83f569
--- /dev/null
+++ b/packages/rfw/test/remote_widget_test.dart
@@ -0,0 +1,49 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/widgets.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:rfw/formats.dart' show parseLibraryFile;
+import 'package:rfw/rfw.dart';
+
+void main() {
+ testWidgets('RemoteWidget', (WidgetTester tester) async {
+ final Runtime runtime1 = Runtime()
+ ..update(const LibraryName(<String>['core']), createCoreWidgets())
+ ..update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = Placeholder();
+ '''));
+ final Runtime runtime2 = Runtime()
+ ..update(const LibraryName(<String>['core']), createCoreWidgets())
+ ..update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = Container();
+ '''));
+ final DynamicContent data = DynamicContent();
+ await tester.pumpWidget(
+ RemoteWidget(
+ runtime: runtime1,
+ data: data,
+ widget: const FullyQualifiedWidgetName(
+ LibraryName(<String>['test']), 'root'),
+ ),
+ );
+ expect(find.byType(RemoteWidget), findsOneWidget);
+ expect(find.byType(Placeholder), findsOneWidget);
+ expect(find.byType(Container), findsNothing);
+
+ await tester.pumpWidget(
+ RemoteWidget(
+ runtime: runtime2,
+ data: data,
+ widget: const FullyQualifiedWidgetName(
+ LibraryName(<String>['test']), 'root'),
+ ),
+ );
+ expect(find.byType(RemoteWidget), findsOneWidget);
+ expect(find.byType(Placeholder), findsNothing);
+ expect(find.byType(Container), findsOneWidget);
+ });
+}
diff --git a/packages/rfw/test/runtime_test.dart b/packages/rfw/test/runtime_test.dart
new file mode 100644
index 0000000..0e3f476
--- /dev/null
+++ b/packages/rfw/test/runtime_test.dart
@@ -0,0 +1,1063 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is hand-formatted.
+
+import 'dart:convert';
+import 'dart:typed_data';
+
+import 'package:flutter/widgets.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:rfw/formats.dart' show parseDataFile, parseLibraryFile;
+import 'package:rfw/rfw.dart';
+
+void main() {
+ testWidgets('list lookup', (WidgetTester tester) async {
+ final Runtime runtime = Runtime()
+ ..update(const LibraryName(<String>['core']), createCoreWidgets());
+ final DynamicContent data = DynamicContent(<String, Object?>{
+ 'list': <Object?>[ 0, 1, 2, 3, 4 ],
+ });
+ await tester.pumpWidget(
+ RemoteWidget(
+ runtime: runtime,
+ data: data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
+ ),
+ );
+ expect(find.byType(RemoteWidget), findsOneWidget);
+ expect(tester.takeException().toString(), contains('Could not find remote widget named'));
+ expect(find.byType(ErrorWidget), findsOneWidget);
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = Column(
+ children: [
+ ...for v in data.list: Text(text: v, textDirection: "ltr"),
+ ],
+ );
+ '''));
+ await tester.pump();
+ expect(find.byType(Text), findsNWidgets(5));
+ });
+
+ testWidgets('data updates', (WidgetTester tester) async {
+ int buildCount = 0;
+ int? lastValue;
+ final Runtime runtime = Runtime()
+ ..update(const LibraryName(<String>['core']), LocalWidgetLibrary(<String, LocalWidgetBuilder>{
+ 'Test': (BuildContext context, DataSource source) {
+ buildCount += 1;
+ lastValue = source.v<int>(<Object>['value']);
+ return const SizedBox.shrink();
+ },
+ }));
+ final DynamicContent data = DynamicContent(<String, Object?>{
+ 'list': <Object?>[
+ <String, Object?>{ 'a': 0 },
+ <String, Object?>{ 'a': 1 },
+ <String, Object?>{ 'a': 2 },
+ ],
+ });
+ await tester.pumpWidget(
+ RemoteWidget(
+ runtime: runtime,
+ data: data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
+ ),
+ );
+ expect(tester.takeException().toString(), contains('Could not find remote widget named'));
+ expect(buildCount, 0);
+ expect(lastValue, isNull);
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = Test(value: data.list.1.a);
+ '''));
+ await tester.pump();
+ expect(buildCount, 1);
+ expect(lastValue, 1);
+
+ data.update('list', <Object?>[
+ <String, Object?>{ 'a': 0 },
+ <String, Object?>{ 'a': 3 },
+ <String, Object?>{ 'a': 2 },
+ ]);
+ await tester.pump();
+ expect(buildCount, 2);
+ expect(lastValue, 3);
+
+ data.update('list', <Object?>[
+ <String, Object?>{ 'a': 1 },
+ <String, Object?>{ 'a': 3 },
+ ]);
+ await tester.pump();
+ expect(buildCount, 2);
+ expect(lastValue, 3);
+
+ data.update('list', <Object?>[
+ <String, Object?>{ 'a': 1 },
+ <String, Object?>{ },
+ ]);
+ await tester.pump();
+ expect(buildCount, 3);
+ expect(lastValue, null);
+
+ data.update('list', <Object?>[
+ <String, Object?>{ 'a': 1 },
+ ]);
+ await tester.pump();
+ expect(buildCount, 3);
+ expect(lastValue, null);
+ });
+
+ testWidgets('$RemoteFlutterWidgetsException', (WidgetTester tester) async {
+ expect(const RemoteFlutterWidgetsException('test').toString(), 'test');
+ });
+
+ testWidgets('deepClone', (WidgetTester tester) async {
+ final Map<String, Object> map = <String, Object>{
+ 'outer': <String, Object>{
+ 'inner': true,
+ }
+ };
+ expect(identical(deepClone(map), map), isFalse);
+ expect(deepClone(map), equals(map));
+ });
+
+ testWidgets('updateText, updateBinary, clearLibraries', (WidgetTester tester) async {
+ final Runtime runtime = Runtime()
+ ..update(const LibraryName(<String>['core']), createCoreWidgets());
+ final DynamicContent data = DynamicContent();
+ await tester.pumpWidget(
+ RemoteWidget(
+ runtime: runtime,
+ data: data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
+ ),
+ );
+ expect(find.byType(RemoteWidget), findsOneWidget);
+ expect(tester.takeException().toString(), contains('Could not find remote widget named'));
+ expect(find.byType(ErrorWidget), findsOneWidget);
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = ColoredBox(color: 0xFF000000);
+ '''));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0xFF000000));
+
+ runtime.update(const LibraryName(<String>['test']), decodeLibraryBlob(encodeLibraryBlob(parseLibraryFile('''
+ import core;
+ widget root = ColoredBox(color: 0xFF000001);
+ '''))));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0xFF000001));
+
+ runtime.clearLibraries();
+ await tester.pump();
+ expect(find.byType(RemoteWidget), findsOneWidget);
+ expect(tester.takeException().toString(), contains('Could not find remote widget named'));
+ expect(find.byType(ErrorWidget), findsOneWidget);
+ });
+
+ testWidgets('Runtime cached build', (WidgetTester tester) async {
+ final Runtime runtime = Runtime()
+ ..update(const LibraryName(<String>['core']), createCoreWidgets());
+ final DynamicContent data = DynamicContent();
+ await tester.pumpWidget(
+ RemoteWidget(
+ runtime: runtime,
+ data: data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['core']), 'Placeholder'),
+ ),
+ );
+
+ expect(find.byType(Placeholder), findsOneWidget);
+ await tester.pumpWidget(
+ RemoteWidget(
+ runtime: runtime,
+ data: data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['core']), 'SizedBoxShrink'),
+ ),
+ );
+ expect(find.byType(Placeholder), findsNothing);
+ await tester.pumpWidget(
+ RemoteWidget(
+ runtime: runtime,
+ data: data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['core']), 'Placeholder'),
+ ),
+ );
+ expect(find.byType(Placeholder), findsOneWidget);
+ });
+
+ testWidgets('Import loops', (WidgetTester tester) async {
+ final Runtime runtime = Runtime()
+ ..update(const LibraryName(<String>['a']), parseLibraryFile('''
+ import b;
+ '''))
+ ..update(const LibraryName(<String>['b']), parseLibraryFile('''
+ import a;
+ '''));
+ final DynamicContent data = DynamicContent();
+ await tester.pumpWidget(
+ RemoteWidget(
+ runtime: runtime,
+ data: data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['a']), 'widget'),
+ ),
+ );
+ expect(tester.takeException().toString(), 'Library a indirectly depends on itself via b which depends on a.');
+ });
+
+ testWidgets('Import loops', (WidgetTester tester) async {
+ final Runtime runtime = Runtime()
+ ..update(const LibraryName(<String>['a']), parseLibraryFile('''
+ import a;
+ '''));
+ final DynamicContent data = DynamicContent();
+ await tester.pumpWidget(
+ RemoteWidget(
+ runtime: runtime,
+ data: data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['a']), 'widget'),
+ ),
+ );
+ expect(tester.takeException().toString(), 'Library a depends on itself.');
+ });
+
+ testWidgets('Missing libraries in import', (WidgetTester tester) async {
+ final Runtime runtime = Runtime()
+ ..update(const LibraryName(<String>['a']), parseLibraryFile('''
+ import b;
+ '''));
+ final DynamicContent data = DynamicContent();
+ await tester.pumpWidget(
+ RemoteWidget(
+ runtime: runtime,
+ data: data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['a']), 'widget'),
+ ),
+ );
+ expect(tester.takeException().toString(), contains('Could not find remote widget named'));
+ expect(tester.widget<ErrorWidget>(find.byType(ErrorWidget)).message, 'Could not find remote widget named widget in a, possibly because some dependencies were missing: b');
+ });
+
+ testWidgets('Missing libraries in specified widget', (WidgetTester tester) async {
+ final Runtime runtime = Runtime();
+ final DynamicContent data = DynamicContent();
+ await tester.pumpWidget(
+ RemoteWidget(
+ runtime: runtime,
+ data: data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['a']), 'widget'),
+ ),
+ );
+ expect(tester.takeException().toString(), contains('Could not find remote widget named'));
+ expect(tester.widget<ErrorWidget>(find.byType(ErrorWidget)).message, 'Could not find remote widget named widget in a, possibly because some dependencies were missing: a');
+ });
+
+ testWidgets('Missing libraries in import via dependency', (WidgetTester tester) async {
+ final Runtime runtime = Runtime()
+ ..update(const LibraryName(<String>['a']), parseLibraryFile('''
+ import b;
+ widget widget = test();
+ '''));
+ final DynamicContent data = DynamicContent();
+ await tester.pumpWidget(
+ RemoteWidget(
+ runtime: runtime,
+ data: data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['a']), 'widget'),
+ ),
+ );
+ expect(tester.takeException().toString(), contains('Could not find remote widget named'));
+ expect(tester.widget<ErrorWidget>(find.byType(ErrorWidget)).message, 'Could not find remote widget named test in a, possibly because some dependencies were missing: b');
+ });
+
+ testWidgets('Missing widget', (WidgetTester tester) async {
+ final Runtime runtime = Runtime()
+ ..update(const LibraryName(<String>['a']), parseLibraryFile(''));
+ final DynamicContent data = DynamicContent();
+ await tester.pumpWidget(
+ RemoteWidget(
+ runtime: runtime,
+ data: data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['a']), 'widget'),
+ ),
+ );
+ expect(tester.takeException().toString(), contains('Could not find remote widget named'));
+ expect(tester.widget<ErrorWidget>(find.byType(ErrorWidget)).message, 'Could not find remote widget named widget in a.');
+ });
+
+ testWidgets('Runtime', (WidgetTester tester) async {
+ final Runtime runtime = Runtime()
+ ..update(const LibraryName(<String>['core']), createCoreWidgets());
+ final DynamicContent data = DynamicContent();
+ await tester.pumpWidget(
+ RemoteWidget(
+ runtime: runtime,
+ data: data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
+ ),
+ );
+ expect(tester.takeException().toString(), contains('Could not find remote widget named'));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root { level: 0 } = inner(level: state.level);
+ widget inner { level: 1 } = ColoredBox(color: args.level);
+ '''));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x00000000));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root { level: 0 } = inner(level: state.level);
+ widget inner { level: 1 } = ColoredBox(color: state.level);
+ '''));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x00000001));
+ });
+
+ testWidgets('Runtime', (WidgetTester tester) async {
+ final Runtime runtime = Runtime()
+ ..update(const LibraryName(<String>['core']), createCoreWidgets());
+ final DynamicContent data = DynamicContent();
+ await tester.pumpWidget(
+ RemoteWidget(
+ runtime: runtime,
+ data: data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
+ ),
+ );
+ expect(tester.takeException().toString(), contains('Could not find remote widget named'));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root { level: 0 } = switch state.level {
+ 0: ColoredBox(color: 2),
+ };
+ '''));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x00000002));
+ });
+
+ testWidgets('Runtime', (WidgetTester tester) async {
+ final Runtime runtime = Runtime()
+ ..update(const LibraryName(<String>['core']), createCoreWidgets());
+ final DynamicContent data = DynamicContent();
+ await tester.pumpWidget(
+ RemoteWidget(
+ runtime: runtime,
+ data: data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
+ ),
+ );
+ expect(tester.takeException().toString(), contains('Could not find remote widget named'));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root { level: 0 } = GestureDetector(
+ onTap: set state.level = 1,
+ child: ColoredBox(color: state.level),
+ );
+ '''));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x00000000));
+ await tester.tap(find.byType(ColoredBox));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x00000001));
+ });
+
+ testWidgets('DynamicContent', (WidgetTester tester) async {
+ final Runtime runtime = Runtime()
+ ..update(const LibraryName(<String>['core']), createCoreWidgets());
+ final DynamicContent data = DynamicContent();
+ await tester.pumpWidget(
+ RemoteWidget(
+ runtime: runtime,
+ data: data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
+ ),
+ );
+ expect(tester.takeException().toString(), contains('Could not find remote widget named'));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = ColoredBox(color: data.color.value);
+ '''));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0xFF000000));
+
+ data.update('color', json.decode('{"value":1}'));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x00000001));
+
+ data.update('color', parseDataFile('{value:2}'));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x00000002));
+
+ data.update('color', decodeDataBlob(Uint8List.fromList(<int>[
+ 0xFE, 0x52, 0x57, 0x44, // signature
+ 0x07, // data is a map
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ...which has one key
+ 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ...which has five letters
+ 0x76, 0x61, 0x6c, 0x75, 0x65, // ...which are "value"
+ 0x02, // and the value is an integer
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ...which is the number 2
+ ])));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x00000002));
+ });
+
+ testWidgets('DynamicContent', (WidgetTester tester) async {
+ final DynamicContent data = DynamicContent(<String, Object?>{'hello': 'world'});
+ expect(data.toString(), 'DynamicContent({hello: world})');
+ });
+
+ testWidgets('binding loop variables', (WidgetTester tester) async {
+ final Runtime runtime = Runtime()
+ ..update(const LibraryName(<String>['core']), createCoreWidgets());
+ final DynamicContent data = DynamicContent(<String, Object?>{
+ 'list': <Object?>[
+ <String, Object?>{
+ 'a': <String, Object?>{ 'b': 0xEE },
+ 'c': <Object?>[ 0xDD ],
+ },
+ ],
+ });
+ final List<String> eventLog = <String>[];
+ await tester.pumpWidget(
+ RemoteWidget(
+ runtime: runtime,
+ data: data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
+ onEvent: (String eventName, DynamicMap eventArguments) {
+ eventLog.add('$eventName $eventArguments');
+ },
+ ),
+ );
+ expect(find.byType(RemoteWidget), findsOneWidget);
+ expect(tester.takeException().toString(), contains('Could not find remote widget named'));
+ expect(find.byType(ErrorWidget), findsOneWidget);
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget verify = ColoredBox(color: args.value.0.q.0);
+ widget root = verify(
+ value: [
+ ...for v in data.list: {
+ q: [ v.a.b ],
+ },
+ ],
+ );
+ '''));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x000000EE));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget verify = ColoredBox(color: args.value.0.q.0);
+ widget root = verify(
+ value: [
+ ...for v in data.list: {
+ q: [ ...for w in v.c: w ],
+ },
+ ],
+ );
+ '''));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x000000DD));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget verify = ColoredBox(color: args.value.0.q);
+ widget root = verify(
+ value: [
+ ...for v in data.list: {
+ q: switch v.a.b {
+ 0xEE: 0xCC,
+ default: 0xFF,
+ },
+ },
+ ],
+ );
+ '''));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x000000CC));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget verify { state: true } = ColoredBox(color: args.value.c.0);
+ widget remote = SizedBox(child: args.corn.0);
+ widget root = remote(
+ corn: [
+ ...for v in data.list:
+ verify(value: v),
+ ],
+ );
+ '''));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x000000DD));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget verify { state: true } = ColoredBox(color: args.value);
+ widget remote = SizedBox(child: args.corn.0);
+ widget root = remote(
+ corn: [
+ ...for v in data.list:
+ verify(value: switch v.c.0 {
+ 0: 0xFF000000,
+ 0xDD: 0xFF0D0D0D,
+ default: v,
+ }),
+ ],
+ );
+ '''));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0xFF0D0D0D));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget verify { state: true } = switch args.value.c.0 {
+ 0xDD: ColoredBox(color: 0xFF0D0D0D),
+ default: ColoredBox(color: args.value),
+ };
+ widget remote = SizedBox(child: args.corn.0);
+ widget root = remote(
+ corn: [
+ ...for v in data.list:
+ verify(value: v),
+ ],
+ );
+ '''));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0xFF0D0D0D));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget verify { state: true } = GestureDetector(
+ onTap: event 'test' { test: args.value.a.b },
+ child: ColoredBox(),
+ );
+ widget remote = SizedBox(child: args.corn.0);
+ widget root = remote(
+ corn: [
+ ...for v in data.list:
+ verify(value: v),
+ ],
+ );
+ '''));
+ expect(eventLog, isEmpty);
+ await tester.pump();
+ await tester.tap(find.byType(ColoredBox));
+ expect(eventLog, <String>['test {test: ${0xEE}}']);
+ eventLog.clear();
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget verify { state: 0x00 } = GestureDetector(
+ onTap: set state.state = args.value.a.b,
+ child: ColoredBox(color: switch state.state {
+ 0x00: 0xFF000001,
+ 0xEE: 0xFF000002,
+ }),
+ );
+ widget remote = SizedBox(child: args.corn.0);
+ widget root = remote(
+ corn: [
+ ...for v in data.list:
+ verify(value: v),
+ ],
+ );
+ '''));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0xFF000001));
+ await tester.tap(find.byType(ColoredBox));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0xFF000002));
+
+ });
+
+ testWidgets('list lookup of esoteric values', (WidgetTester tester) async {
+ final Runtime runtime = Runtime()
+ ..update(const LibraryName(<String>['core']), createCoreWidgets());
+ final DynamicContent data = DynamicContent();
+ await tester.pumpWidget(
+ RemoteWidget(
+ runtime: runtime,
+ data: data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
+ ),
+ );
+ expect(tester.takeException().toString(), contains('Could not find remote widget named'));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = test(list: ['A'], loop: [...for b in ["B", "C"]: b]);
+ widget test = Text(
+ textDirection: "ltr",
+ text: [
+ '>',
+ ...for a in args.list: a,
+ ...for b in args.loop: b,
+ '<',
+ ],
+ );
+ '''));
+ await tester.pump();
+ expect(find.text('>ABC<'), findsOneWidget);
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = Text(
+ textDirection: "ltr",
+ text: [
+ '>',
+ ...for a in root(): a,
+ '<',
+ ],
+ );
+ '''));
+ await tester.pump();
+ expect(find.text('><'), findsOneWidget);
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = test(list: [test()]);
+ widget test = Text(
+ textDirection: "ltr",
+ text: [
+ '>',
+ ...for a in args.list: a,
+ '<',
+ ],
+ );
+ '''));
+ await tester.pump();
+ expect(find.text('><'), findsOneWidget);
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root { list: [ 0x01 ] } = GestureDetector(
+ onTap: set state.list = [ 0x02, 0x03 ],
+ child: Column(
+ children: [
+ ...for v in state.list:
+ SizedBox(height: 10.0, width: 10.0, child: ColoredBox(color: v)),
+ ],
+ ),
+ );
+ '''));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x00000001));
+ await tester.tap(find.byType(ColoredBox));
+ await tester.pump();
+ expect(tester.firstWidget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x00000002));
+ expect(find.byType(ColoredBox), findsNWidgets(2));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = Column(
+ children: [
+ ...for v in switch 0 {
+ 0: [ColoredBox(color: 0x00000001)],
+ default: [ColoredBox(color: 0xFFFF0000)],
+ }: v,
+ ],
+ );
+ '''));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x00000001));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = Column(
+ children: [
+ ...for v in {
+ a: [ColoredBox(color: 0x00000001)],
+ b: [ColoredBox(color: 0xFFFF0000)],
+ }: v,
+ ],
+ );
+ '''));
+ await tester.pump();
+ expect(find.byType(ColoredBox), findsNothing);
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = test(
+ list: [...for w in [ColoredBox(color: 0xFF00FF00)]: w],
+ );
+ widget test = Column(
+ children: [
+ ...for v in args.list: v,
+ ],
+ );
+ '''));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0xFF00FF00));
+ });
+
+ testWidgets('data lookup', (WidgetTester tester) async {
+ final Runtime runtime = Runtime()
+ ..update(const LibraryName(<String>['core']), createCoreWidgets());
+ final DynamicContent data = DynamicContent(<String, Object?>{
+ 'map': <String, Object?>{ 'list': <Object?>[ 0xAB ] },
+ });
+ await tester.pumpWidget(
+ RemoteWidget(
+ runtime: runtime,
+ data: data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
+ ),
+ );
+ expect(tester.takeException().toString(), contains('Could not find remote widget named'));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = test(list: data.map.list);
+ widget test = ColoredBox(color: args.list.0);
+ '''));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x000000AB));
+ });
+
+ testWidgets('args lookup', (WidgetTester tester) async {
+ final Runtime runtime = Runtime()
+ ..update(const LibraryName(<String>['core']), createCoreWidgets());
+ final DynamicContent data = DynamicContent();
+ await tester.pumpWidget(
+ RemoteWidget(
+ runtime: runtime,
+ data: data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
+ ),
+ );
+ expect(tester.takeException().toString(), contains('Could not find remote widget named'));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = test1(map: { 'list': [ 0xAC ] });
+ widget test1 = test2(list: args.map.list);
+ widget test2 = ColoredBox(color: args.list.0);
+ '''));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x000000AC));
+ });
+
+ testWidgets('state lookup', (WidgetTester tester) async {
+ final Runtime runtime = Runtime()
+ ..update(const LibraryName(<String>['core']), createCoreWidgets());
+ final DynamicContent data = DynamicContent();
+ await tester.pumpWidget(
+ RemoteWidget(
+ runtime: runtime,
+ data: data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
+ ),
+ );
+ expect(tester.takeException().toString(), contains('Could not find remote widget named'));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root { map: { 'list': [ 0xAD ] } } = test(list: state.map.list);
+ widget test = ColoredBox(color: args.list.0);
+ '''));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x000000AD));
+ });
+
+ testWidgets('switch', (WidgetTester tester) async {
+ final Runtime runtime = Runtime()
+ ..update(const LibraryName(<String>['core']), createCoreWidgets());
+ final DynamicContent data = DynamicContent();
+ await tester.pumpWidget(
+ RemoteWidget(
+ runtime: runtime,
+ data: data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
+ ),
+ );
+ expect(tester.takeException().toString(), contains('Could not find remote widget named'));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = ColoredBox(color: switch data.a.b {
+ 0: 0x11111111,
+ default: 0x22222222,
+ });
+ '''));
+ data.update('a', parseDataFile('{ b: 1 }'));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x22222222));
+ data.update('a', parseDataFile('{ b: 0 }'));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x11111111));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = switch true {};
+ '''));
+ await tester.pump();
+ expect(tester.takeException().toString(), 'Switch in test:root did not resolve to a widget (got <missing>).');
+ });
+
+ testWidgets('events with arguments', (WidgetTester tester) async {
+ final Runtime runtime = Runtime()
+ ..update(const LibraryName(<String>['core']), createCoreWidgets());
+ final DynamicContent data = DynamicContent();
+ final List<String> eventLog = <String>[];
+ await tester.pumpWidget(
+ RemoteWidget(
+ runtime: runtime,
+ data: data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
+ onEvent: (String eventName, DynamicMap eventArguments) {
+ eventLog.add('$eventName $eventArguments');
+ },
+ ),
+ );
+ expect(tester.takeException().toString(), contains('Could not find remote widget named'));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = GestureDetector(
+ onTap: event 'tap' {
+ list: [...for a in [0,1]: a],
+ },
+ child: ColoredBox(),
+ );
+ '''));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0xFF000000));
+ expect(eventLog, isEmpty);
+ await tester.tap(find.byType(ColoredBox));
+ expect(eventLog, <String>['tap {list: [0, 1]}']);
+ eventLog.clear();
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = GestureDetector(
+ onTap: [ event 'tap' { a: 1 }, event 'tap' { a: 2 }, event 'final tap' { } ],
+ child: ColoredBox(),
+ );
+ '''));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0xFF000000));
+ expect(eventLog, isEmpty);
+ await tester.tap(find.byType(ColoredBox));
+ expect(eventLog, <String>['tap {a: 1}', 'tap {a: 2}', 'final tap {}']);
+ eventLog.clear();
+ });
+
+ testWidgets('_CurriedWidget toStrings', (WidgetTester tester) async {
+ final Runtime runtime = Runtime()
+ ..update(const LibraryName(<String>['core']), createCoreWidgets());
+ final DynamicContent data = DynamicContent();
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget stateless = ColoredBox(color: 0xAA);
+ widget stateful { test: false } = ColoredBox(color: 0xBB);
+ widget switchy = switch true { default: ColoredBox(color: 0xCC) };
+ '''));
+ expect(
+ (runtime.build(
+ tester.element(find.byType(Container)),
+ const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'stateless'),
+ data,
+ (String eventName, DynamicMap eventArguments) {},
+ ) as dynamic).curriedWidget.toString(),
+ 'core:ColoredBox {} {color: 170}',
+ );
+ expect(
+ (runtime.build(
+ tester.element(find.byType(Container)),
+ const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'stateful'),
+ data,
+ (String eventName, DynamicMap eventArguments) {},
+ ) as dynamic).curriedWidget.toString(),
+ 'test:stateful {test: false} {} = core:ColoredBox {} {color: 187}',
+ );
+ expect(
+ (runtime.build(
+ tester.element(find.byType(Container)),
+ const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'switchy'),
+ data,
+ (String eventName, DynamicMap eventArguments) {},
+ ) as dynamic).curriedWidget.toString(),
+ 'test:switchy {} {} = switch true {null: core:ColoredBox {} {color: 204}}',
+ );
+ });
+
+ testWidgets('state setting', (WidgetTester tester) async {
+ final Runtime runtime = Runtime()
+ ..update(const LibraryName(<String>['core']), createCoreWidgets());
+ final DynamicContent data = DynamicContent();
+ await tester.pumpWidget(
+ RemoteWidget(
+ runtime: runtime,
+ data: data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
+ ),
+ );
+ expect(tester.takeException().toString(), contains('Could not find remote widget named'));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root { a: 0 } = GestureDetector(
+ onTap: set state.b = 0,
+ child: ColoredBox(),
+ );
+ '''));
+ await tester.pump();
+ await tester.tap(find.byType(ColoredBox));
+ expect(tester.takeException().toString(), 'b does not identify existing state.');
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root { a: 0 } = GestureDetector(
+ onTap: set state.0 = 0,
+ child: ColoredBox(),
+ );
+ '''));
+ await tester.pump();
+ await tester.tap(find.byType(ColoredBox));
+ expect(tester.takeException().toString(), '0 does not identify existing state.');
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root { a: [] } = GestureDetector(
+ onTap: set state.a.b = 0,
+ child: ColoredBox(),
+ );
+ '''));
+ await tester.pump();
+ await tester.tap(find.byType(ColoredBox));
+ expect(tester.takeException().toString(), 'a.b does not identify existing state.');
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root { a: [] } = GestureDetector(
+ onTap: set state.a.0 = 0,
+ child: ColoredBox(),
+ );
+ '''));
+ await tester.pump();
+ await tester.tap(find.byType(ColoredBox));
+ expect(tester.takeException().toString(), 'a.0 does not identify existing state.');
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root { a: true } = GestureDetector(
+ onTap: set state.a.0 = 0,
+ child: ColoredBox(),
+ );
+ '''));
+ await tester.pump();
+ await tester.tap(find.byType(ColoredBox));
+ expect(tester.takeException().toString(), 'a.0 does not identify existing state.');
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root { a: true } = GestureDetector(
+ onTap: set state.a.b = 0,
+ child: ColoredBox(),
+ );
+ '''));
+ await tester.pump();
+ await tester.tap(find.byType(ColoredBox));
+ expect(tester.takeException().toString(), 'a.b does not identify existing state.');
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root { a: { b: 0 } } = GestureDetector(
+ onTap: set state.a = 15,
+ child: ColoredBox(color: state.a),
+ );
+ '''));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0xFF000000));
+ await tester.tap(find.byType(ColoredBox));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x0000000F));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root { a: [ 0 ] } = GestureDetector(
+ onTap: set state.a = 10,
+ child: ColoredBox(color: state.a),
+ );
+ '''));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0xFF000000));
+ await tester.tap(find.byType(ColoredBox));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x0000000A));
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root { a: [ [ 1 ] ] } = GestureDetector(
+ onTap: set state.a.0.0 = 11,
+ child: ColoredBox(color: state.a.0.0),
+ );
+ '''));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x00000001));
+ await tester.tap(find.byType(ColoredBox));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x0000000B));
+ });
+
+ testWidgets('DataSource', (WidgetTester tester) async {
+ final Runtime runtime = Runtime();
+ final DynamicContent data = DynamicContent();
+ final List<String> eventLog = <String>[];
+ await tester.pumpWidget(
+ RemoteWidget(
+ runtime: runtime,
+ data: data,
+ widget: const FullyQualifiedWidgetName(LibraryName(<String>['remote']), 'test'),
+ onEvent: (String name, DynamicMap arguments) {
+ eventLog.add('$name $arguments');
+ },
+ ),
+ );
+ expect(tester.takeException().toString(), contains('Could not find remote widget named'));
+
+ runtime.update(const LibraryName(<String>['local']), LocalWidgetLibrary(<String, LocalWidgetBuilder>{
+ 'Test': (BuildContext context, DataSource source) {
+ expect(source.isList(<Object>['a']), isFalse);
+ expect(source.isList(<Object>['b']), isTrue);
+ expect(source.length(<Object>['b']), 1);
+ expect(source.child(<Object>['missing']), isA<ErrorWidget>());
+ expect(tester.takeException().toString(), 'Not a widget at [missing] (got <missing>) for local:Test.');
+ expect(source.childList(<Object>['a']), <Matcher>[isA<ErrorWidget>()]);
+ expect(tester.takeException().toString(), 'Not a widget list at [a] (got 0) for local:Test.');
+ expect(source.childList(<Object>['b']), <Matcher>[isA<ErrorWidget>()]);
+ expect(tester.takeException().toString(), 'Not a widget at [b] (got 1) for local:Test.');
+ expect(eventLog, isEmpty);
+ source.voidHandler(<Object>['callback'], <String, Object?>{ 'extra': 4, 'b': 3 })!();
+ expect(eventLog, <String>['e {a: 1, b: 3, extra: 4}']);
+ return const ColoredBox(color: Color(0xAABBCCDD));
+ },
+ }));
+ runtime.update(const LibraryName(<String>['remote']), parseLibraryFile('''
+ import local;
+ widget test = Test(a: 0, b: [1], callback: event 'e' { a: 1, b: 2 });
+ '''));
+ await tester.pump();
+ expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0xAABBCCDD));
+ bool tested = false;
+ tester.element(find.byType(ColoredBox)).visitAncestorElements((Element node) {
+ expect(node.toString(), equalsIgnoringHashCodes('_Widget(state: _WidgetState#00000(name: "local:Test"))'));
+ tested = true;
+ return false;
+ });
+ expect(tested, isTrue);
+ });
+}
diff --git a/packages/rfw/test/text_test.dart b/packages/rfw/test/text_test.dart
new file mode 100644
index 0000000..3ab1ca9
--- /dev/null
+++ b/packages/rfw/test/text_test.dart
@@ -0,0 +1,147 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is hand-formatted.
+
+import 'package:flutter_test/flutter_test.dart';
+import 'package:rfw/formats.dart';
+
+void main() {
+ testWidgets('empty parseDataFile', (WidgetTester tester) async {
+ final DynamicMap result = parseDataFile('{}');
+ expect(result, <String, Object?>{ });
+ });
+
+ testWidgets('empty parseLibraryFile', (WidgetTester tester) async {
+ final RemoteWidgetLibrary result = parseLibraryFile('');
+ expect(result.imports, isEmpty);
+ expect(result.widgets, isEmpty);
+ });
+
+ testWidgets('space parseDataFile', (WidgetTester tester) async {
+ final DynamicMap result = parseDataFile(' \n {} \n ');
+ expect(result, <String, Object?>{ });
+ });
+
+ testWidgets('space parseLibraryFile', (WidgetTester tester) async {
+ final RemoteWidgetLibrary result = parseLibraryFile(' \n ');
+ expect(result.imports, isEmpty);
+ expect(result.widgets, isEmpty);
+ });
+
+ testWidgets('error handling in parseDataFile', (WidgetTester tester) async {
+ void test(String input, String expectedMessage) {
+ try {
+ parseDataFile(input);
+ fail('parsing `$input` did not result in an error (expected "$expectedMessage").');
+ } on ParserException catch (e) {
+ expect('$e', expectedMessage);
+ }
+ }
+ test('', 'Expected symbol "{" but found <EOF> at line 1 column 0.');
+ test('}', 'Expected symbol "{" but found } at line 1 column 1.');
+ test('1', 'Expected symbol "{" but found 1 at line 1 column 1.');
+ test('1.0', 'Expected symbol "{" but found 1.0 at line 1 column 3.');
+ test('a', 'Expected symbol "{" but found a at line 1 column 1.');
+ test('"a"', 'Expected symbol "{" but found "a" at line 1 column 3.');
+ test('&', 'Unexpected character U+0026 ("&") at line 1 column 1.');
+ test('\t', 'Unexpected character U+0009 at line 1 column 1.');
+ test('{ a: 0, a: 0 }', 'Duplicate key "a" in map at line 1 column 10.');
+ test('{ a: 0; }', 'Expected symbol "}" but found ; at line 1 column 7.');
+ test('{ a: [ 0 ; ] }', 'Expected comma but found ; at line 1 column 10.');
+ test('{ } x', 'Expected end of file but found x at line 1 column 5.');
+ test('{ a: a }', 'Unexpected a at line 1 column 7.');
+ test('{ ... }', 'Expected symbol "}" but found … at line 1 column 5.');
+ test('{ a: ... }', 'Unexpected … at line 1 column 8.');
+ });
+
+ testWidgets('valid values in parseDataFile', (WidgetTester tester) async {
+ expect(parseDataFile('{ }\n\n \n\n'), <String, Object?>{ });
+ expect(parseDataFile('{ a: "b" }'), <String, Object?>{ 'a': 'b' });
+ expect(parseDataFile('{ a: [ "b", 9 ] }'), <String, Object?>{ 'a': <Object?>[ 'b', 9 ] });
+ expect(parseDataFile('{ a: { } }'), <String, Object?>{ 'a': <String, Object?>{ } });
+ expect(parseDataFile('{ a: 123.456e7 }'), <String, Object?>{ 'a': 123.456e7 });
+ expect(parseDataFile('{ a: true }'), <String, Object?>{ 'a': true });
+ expect(parseDataFile('{ a: false }'), <String, Object?>{ 'a': false });
+ expect(parseDataFile('{ "a": 0 }'), <String, Object?>{ 'a': 0 });
+ expect(parseDataFile('{ "a": null }'), <String, Object?>{ });
+ });
+
+ testWidgets('error handling in parseLibraryFile', (WidgetTester tester) async {
+ void test(String input, String expectedMessage) {
+ try {
+ parseLibraryFile(input);
+ fail('parsing `$input` did not result in an error (expected "$expectedMessage").');
+ } on ParserException catch (e) {
+ expect('$e', expectedMessage);
+ }
+ }
+ test('2', 'Expected keywords "import" or "widget", or end of file but found 2 at line 1 column 1.');
+ test('impor', 'Expected keywords "import" or "widget", or end of file but found impor at line 1 column 5.');
+ test('import', 'Expected string but found <EOF> at line 1 column 6.');
+ test('import 2', 'Expected string but found 2 at line 1 column 8.');
+ test('import foo', 'Expected symbol ";" but found <EOF> at line 1 column 10.');
+ test('import foo.', 'Expected string but found <EOF> at line 1 column 11.');
+ test('import foo,', 'Expected symbol ";" but found , at line 1 column 11.');
+ test('import foo+', 'Unexpected character U+002B ("+") inside identifier at line 1 column 11.');
+ test('import foo.1', 'Expected string but found 1 at line 1 column 12.');
+ test('import foo.+', 'Unexpected character U+002B ("+") after period at line 1 column 12.');
+ test('widget a = b(c: [ ...for args in []: "e" ]);', 'args is a reserved word at line 1 column 30.');
+ test('widget a = switch 0 { 0: a(), 0: b() };', 'Switch has duplicate cases for key 0 at line 1 column 32.');
+ test('widget a = switch 0 { default: a(), default: b() };', 'Switch has multiple default cases at line 1 column 44.');
+ test('widget a = b(c: args)', 'Expected symbol "." but found ) at line 1 column 21.');
+ test('widget a = b(c: args.=)', 'Unexpected = at line 1 column 22.');
+ test('widget a = b(c: [ ... ]);', 'Expected identifier but found ] at line 1 column 23.');
+ test('widget a = b(c: [ ...baa ]);', 'Expected for but found baa at line 1 column 25.');
+ test('widget a = 0;', 'Expected identifier but found 0 at line 1 column 13.');
+ });
+
+ testWidgets('parseLibraryFile: imports', (WidgetTester tester) async {
+ final RemoteWidgetLibrary result = parseLibraryFile('import foo.bar;');
+ expect(result.imports, hasLength(1));
+ expect(result.imports.single.toString(), 'import foo.bar;');
+ expect(result.widgets, isEmpty);
+ });
+
+ testWidgets('parseLibraryFile: loops', (WidgetTester tester) async {
+ final RemoteWidgetLibrary result = parseLibraryFile('widget a = b(c: [ ...for d in []: "e" ]);');
+ expect(result.imports, isEmpty);
+ expect(result.widgets, hasLength(1));
+ expect(result.widgets.single.toString(), 'widget a = b({c: [...for loop in []: e]});');
+ });
+
+ testWidgets('parseLibraryFile: switch', (WidgetTester tester) async {
+ expect(parseLibraryFile('widget a = switch 0 { 0: a() };').toString(), 'widget a = switch 0 {0: a({})};');
+ expect(parseLibraryFile('widget a = switch 0 { default: a() };').toString(), 'widget a = switch 0 {null: a({})};');
+ expect(parseLibraryFile('widget a = b(c: switch 1 { 2: 3 });').toString(), 'widget a = b({c: switch 1 {2: 3}});');
+ });
+
+ testWidgets('parseLibraryFile: references', (WidgetTester tester) async {
+ expect(parseLibraryFile('widget a = b(c: [...for d in []: d]);').toString(), 'widget a = b({c: [...for loop in []: loop0.]});');
+ expect(parseLibraryFile('widget a = b(c:args.foo.bar);').toString(), 'widget a = b({c: args.foo.bar});');
+ expect(parseLibraryFile('widget a = b(c:data.foo.bar);').toString(), 'widget a = b({c: data.foo.bar});');
+ expect(parseLibraryFile('widget a = b(c:state.foo.bar);').toString(), 'widget a = b({c: state.foo.bar});');
+ expect(parseLibraryFile('widget a = b(c: [...for d in []: d.bar]);').toString(), 'widget a = b({c: [...for loop in []: loop0.bar]});');
+ expect(parseLibraryFile('widget a = b(c:args.foo."bar");').toString(), 'widget a = b({c: args.foo.bar});');
+ expect(parseLibraryFile('widget a = b(c:data.foo."bar");').toString(), 'widget a = b({c: data.foo.bar});');
+ expect(parseLibraryFile('widget a = b(c:state.foo."bar");').toString(), 'widget a = b({c: state.foo.bar});');
+ expect(parseLibraryFile('widget a = b(c: [...for d in []: d."bar"]);').toString(), 'widget a = b({c: [...for loop in []: loop0.bar]});');
+ expect(parseLibraryFile('widget a = b(c:args.foo.9);').toString(), 'widget a = b({c: args.foo.9});');
+ expect(parseLibraryFile('widget a = b(c:data.foo.9);').toString(), 'widget a = b({c: data.foo.9});');
+ expect(parseLibraryFile('widget a = b(c:state.foo.9);').toString(), 'widget a = b({c: state.foo.9});');
+ expect(parseLibraryFile('widget a = b(c: [...for d in []: d.9]);').toString(), 'widget a = b({c: [...for loop in []: loop0.9]});');
+ });
+
+ testWidgets('parseLibraryFile: event handlers', (WidgetTester tester) async {
+ expect(parseLibraryFile('widget a = b(c: event "d" { });').toString(), 'widget a = b({c: event d {}});');
+ expect(parseLibraryFile('widget a = b(c: set state.d = 0);').toString(), 'widget a = b({c: set state.d = 0});');
+ });
+
+ testWidgets('parseLibraryFile: stateful widgets', (WidgetTester tester) async {
+ expect(parseLibraryFile('widget a {} = c();').toString(), 'widget a = c({});');
+ expect(parseLibraryFile('widget a {b: 0} = c();').toString(), 'widget a = c({});');
+ final RemoteWidgetLibrary result = parseLibraryFile('widget a {b: 0} = c();');
+ expect(result.widgets.single.initialState, <String, Object?>{'b': 0});
+ });
+}