diff --git a/add_to_app/README.md b/add_to_app/README.md
index 69410ddc2..17ec43384 100644
--- a/add_to_app/README.md
+++ b/add_to_app/README.md
@@ -65,9 +65,11 @@ There are three Flutter modules included in the codebase:
* `flutter_module` displays the dimensions of the screen, a button that
increments a simple counter, and an optional exit button.
-* `flutter_module_using_plugin` does everything `flutter_module` does and adds
- another button that will open the Flutter documentation in a browser using the
- [`url_launcher`](https://pub.dev/packages/url_launcher) Flutter plugin.
+* `flutter_module_using_plugin` contains 2 programs. The main entrypoint does
+ everything `flutter_module` does and adds another button that will open the
+ Flutter documentation in a browser using the [`url_launcher`](https://pub.dev/packages/url_launcher)
+ Flutter plugin. A second `showCell` entrypoint displays a card meant to be
+ shown as a cell in a list.
* `flutter_module_books` simulates an integration scenario with existing
platform business logic and middleware. It uses the [`pigeon`](https://pub.dev/packages/pigeon)
plugin to make integration easier by generating the platform channel
@@ -132,6 +134,23 @@ following differences:
If you're interested in learning what additional steps an app needs to take in
order to use a Flutter module that relies on plugins, these projects can help.
+### `android_view`
+
+Rather than integrating Flutter as a fullscreen page, the `android_view` demo
+integrates a partial screen `FlutterView` into an Android `RecyclerView`.
+
+* Demonstrates the additional application specific plumbing needed to hook a
+ `FlutterView` to both a separate, application owned `FlutterEngine` and to its
+ host activity which has its own lifecycle. This is different than a relatively
+ more self-contained `FlutterActivity`.
+* Show how a single `FlutterView` can be used in multiple places in the Android
+ activity in a performant way.
+* Demonstrates a `FlutterView`-based UI interwoven seamlessly into a native
+ Android UI.
+* The Flutter cells use the [`sensors`](https://pub.dev/packages/sensors)
+ plugin and displays a Flutter logo, rotate based on the phone's current
+ orientation.
+
### `android_using_prebuilt_module` and `ios_using_prebuilt_module`
These apps are essentially identical to `android_fullscreen` and
diff --git a/add_to_app/android_view/.gitignore b/add_to_app/android_view/.gitignore
new file mode 100644
index 000000000..aa724b770
--- /dev/null
+++ b/add_to_app/android_view/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/add_to_app/android_view/README.md b/add_to_app/android_view/README.md
new file mode 100644
index 000000000..ed2a9a26f
--- /dev/null
+++ b/add_to_app/android_view/README.md
@@ -0,0 +1,14 @@
+# android_view
+
+An example of an Android app that integrates a Flutter add-to-app module at a
+view level. For more information on how to use it, see the [README](../README.md)
+file located in the [/add_to_app](/add_to_app) directory of this repo.
+
+## Getting Started
+
+For more information about Flutter, check out
+[flutter.dev](https://flutter.dev).
+
+For instructions on how to integrate Flutter modules into your existing
+applications, see Flutter's
+[add-to-app documentation](https://flutter.dev/docs/development/add-to-app).
diff --git a/add_to_app/android_view/app/.gitignore b/add_to_app/android_view/app/.gitignore
new file mode 100644
index 000000000..42afabfd2
--- /dev/null
+++ b/add_to_app/android_view/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/add_to_app/android_view/app/build.gradle b/add_to_app/android_view/app/build.gradle
new file mode 100644
index 000000000..dcc063fcc
--- /dev/null
+++ b/add_to_app/android_view/app/build.gradle
@@ -0,0 +1,55 @@
+plugins {
+ id 'com.android.application'
+ id 'kotlin-android'
+}
+
+android {
+ ndkVersion "21.3.6528147"
+ compileSdkVersion 30
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "dev.flutter.example.androidView"
+ minSdkVersion 16
+ targetSdkVersion 30
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+ buildFeatures {
+ viewBinding true
+ }
+}
+
+dependencies {
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+ implementation 'androidx.core:core-ktx:1.3.2'
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation 'com.google.android.material:material:1.2.1'
+ implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
+ implementation 'androidx.vectordrawable:vectordrawable:1.1.0'
+ implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
+ implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
+ implementation 'androidx.navigation:navigation-fragment-ktx:2.3.2'
+ implementation 'androidx.navigation:navigation-ui-ktx:2.3.2'
+ implementation "androidx.recyclerview:recyclerview:1.1.0"
+ implementation project(path: ':flutter')
+ testImplementation 'junit:junit:4.+'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.2'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
+}
\ No newline at end of file
diff --git a/add_to_app/android_view/app/proguard-rules.pro b/add_to_app/android_view/app/proguard-rules.pro
new file mode 100644
index 000000000..481bb4348
--- /dev/null
+++ b/add_to_app/android_view/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/add_to_app/android_view/app/src/main/AndroidManifest.xml b/add_to_app/android_view/app/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..919153dcc
--- /dev/null
+++ b/add_to_app/android_view/app/src/main/AndroidManifest.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/add_to_app/android_view/app/src/main/java/dev/flutter/example/androidView/FlutterViewEngine.kt b/add_to_app/android_view/app/src/main/java/dev/flutter/example/androidView/FlutterViewEngine.kt
new file mode 100644
index 000000000..afc0db2d5
--- /dev/null
+++ b/add_to_app/android_view/app/src/main/java/dev/flutter/example/androidView/FlutterViewEngine.kt
@@ -0,0 +1,220 @@
+// Copyright 2019 The Flutter team. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package dev.flutter.example.androidView
+
+import android.content.Intent
+import androidx.activity.ComponentActivity
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleObserver
+import androidx.lifecycle.OnLifecycleEvent
+import io.flutter.embedding.android.FlutterView
+import io.flutter.embedding.engine.FlutterEngine
+import io.flutter.plugin.platform.PlatformPlugin
+
+/**
+ * This is an application-specific wrapper class that exists to expose the intersection of an
+ * application's active activity and an application's visible view to a [FlutterEngine] for
+ * rendering.
+ *
+ * Omitted features from the [io.flutter.embedding.android.FlutterActivity] include:
+ * * **State restoration**. If you're integrating at the view level, you should handle activity
+ * state restoration yourself.
+ * * **Engine creations**. At this level of granularity, you must make an engine and attach.
+ * and all engine features like initial route etc must be configured on the engine yourself.
+ * * **Splash screens**. You must implement it yourself. Read from
+ * `addOnFirstFrameRenderedListener` as needed.
+ * * **Transparency, surface/texture**. These are just [FlutterView] level APIs. Set them on the
+ * [FlutterView] directly.
+ * * **Intents**. This doesn't do any translation of intents into actions in the [FlutterEngine].
+ * you must do them yourself.
+ * * **Back buttons**. You must decide whether to send it to Flutter via
+ * [FlutterEngine.getNavigationChannel.popRoute()], or consume it natively. Though that
+ * decision may be difficult due to https://github.com/flutter/flutter/issues/67011.
+ * * **Low memory signals**. You're strongly encouraged to pass the low memory signals (such
+ * as from the host `Activity`'s `onTrimMemory` callbacks) to the [FlutterEngine] to let
+ * Flutter and the Dart VM cull its own memory usage.
+ *
+ * Your own [FlutterView] integrating application may need a similar wrapper but you must decide on
+ * what the appropriate intersection between the [FlutterView], the [FlutterEngine] and your
+ * `Activity` should be for your own application.
+ */
+class FlutterViewEngine(val engine: FlutterEngine) : LifecycleObserver{
+ private var flutterView: FlutterView? = null
+ private var activity: ComponentActivity? = null
+ private var platformPlugin: PlatformPlugin? = null
+
+ /**
+ * This is the intersection of an available activity and of a visible [FlutterView]. This is
+ * where Flutter would start rendering.
+ */
+ private fun hookActivityAndView() {
+ // Assert state.
+ activity!!.let { activity ->
+ flutterView!!.let { flutterView ->
+ platformPlugin = PlatformPlugin(activity, engine.platformChannel)
+
+ engine.activityControlSurface.attachToActivity(activity, activity.lifecycle)
+ flutterView.attachToFlutterEngine(engine)
+ activity.lifecycle.addObserver(this)
+ }
+ }
+ }
+
+ /**
+ * Lost the intersection of either an available activity or a visible
+ * [FlutterView].
+ */
+ private fun unhookActivityAndView() {
+ // Stop reacting to activity events.
+ activity!!.lifecycle.removeObserver(this)
+
+ // Plugins are no longer attached to an activity.
+ engine.activityControlSurface.detachFromActivity()
+
+ // Release Flutter's control of UI such as system chrome.
+ platformPlugin!!.destroy()
+ platformPlugin = null
+
+ // Set Flutter's application state to detached.
+ engine.lifecycleChannel.appIsDetached();
+
+ // Detach rendering pipeline.
+ flutterView!!.detachFromFlutterEngine()
+ }
+
+ /**
+ * Signal that a host `Activity` is now ready. If there is no [FlutterView] instance currently
+ * attached to the view hierarchy and visible, Flutter is not yet rendering.
+ *
+ * You can also choose at this point whether to notify the plugins that an `Activity` is
+ * attached or not. You can also choose at this point whether to connect a Flutter
+ * [PlatformPlugin] at this point which allows your Dart program to trigger things like
+ * haptic feedback and read the clipboard. This sample arbitrarily chooses no for both.
+ */
+ fun attachToActivity(activity: ComponentActivity) {
+ this.activity = activity
+ if (flutterView != null) {
+ hookActivityAndView()
+ }
+ }
+
+ /**
+ * Signal that a host `Activity` now no longer connected. If there were a [FlutterView] in
+ * the view hierarchy and visible at this moment, that [FlutterView] will stop rendering.
+ *
+ * You can also choose at this point whether to notify the plugins that an `Activity` is
+ * no longer attached or not. You can also choose at this point whether to disconnect Flutter's
+ * [PlatformPlugin] at this point which stops your Dart program being able to trigger things
+ * like haptic feedback and read the clipboard. This sample arbitrarily chooses yes for both.
+ */
+ fun detachActivity() {
+ if (flutterView != null) {
+ unhookActivityAndView()
+ }
+ activity = null
+ }
+
+ /**
+ * Signal that a [FlutterView] instance is created and attached to a visible Android view
+ * hierarchy.
+ *
+ * If an `Activity` was also previously provided, this puts Flutter into the rendering state
+ * for this [FlutterView]. This also connects this wrapper class to listen to the `Activity`'s
+ * lifecycle to pause rendering when the activity is put into the background while the
+ * view is still attached to the view hierarchy.
+ */
+ fun attachFlutterView(flutterView: FlutterView) {
+ this.flutterView = flutterView
+ if (activity != null) {
+ hookActivityAndView()
+ }
+ }
+
+ /**
+ * Signal that the attached [FlutterView] instance destroyed or no longer attached to a visible
+ * Android view hierarchy.
+ *
+ * If an `Activity` was attached, this stops Flutter from rendering. It also makes this wrapper
+ * class stop listening to the `Activity`'s lifecycle since it's no longer rendering.
+ */
+ fun detachFlutterView() {
+ unhookActivityAndView()
+ flutterView = null
+ }
+
+ /**
+ * Callback to let Flutter respond to the `Activity`'s resumed lifecycle event while both an
+ * `Activity` and a [FlutterView] are attached.
+ */
+ @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
+ private fun resumeActivity() {
+ if (activity != null) {
+ engine.lifecycleChannel.appIsResumed()
+ }
+
+ platformPlugin?.updateSystemUiOverlays()
+ }
+
+ /**
+ * Callback to let Flutter respond to the `Activity`'s paused lifecycle event while both an
+ * `Activity` and a [FlutterView] are attached.
+ */
+ @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
+ private fun pauseActivity() {
+ if (activity != null) {
+ engine.lifecycleChannel.appIsInactive()
+ }
+ }
+
+ /**
+ * Callback to let Flutter respond to the `Activity`'s stopped lifecycle event while both an
+ * `Activity` and a [FlutterView] are attached.
+ */
+ @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
+ private fun stopActivity() {
+ if (activity != null) {
+ engine.lifecycleChannel.appIsPaused()
+ }
+ }
+
+ // These events aren't used but would be needed for Flutter plugins consuming
+ // these events to function.
+
+ /**
+ * Pass through the `Activity`'s `onRequestPermissionsResult` signal to plugins that may be
+ * listening to it while the `Activity` and the [FlutterView] are connected.
+ */
+ fun onRequestPermissionsResult(
+ requestCode: Int,
+ permissions: Array,
+ grantResults: IntArray
+ ) {
+ if (activity != null && flutterView != null) {
+ engine
+ .activityControlSurface
+ .onRequestPermissionsResult(requestCode, permissions, grantResults);
+ }
+ }
+
+ /**
+ * Pass through the `Activity`'s `onActivityResult` signal to plugins that may be
+ * listening to it while the `Activity` and the [FlutterView] are connected.
+ */
+ fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ if (activity != null && flutterView != null) {
+ engine.activityControlSurface.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+
+ /**
+ * Pass through the `Activity`'s `onUserLeaveHint` signal to plugins that may be
+ * listening to it while the `Activity` and the [FlutterView] are connected.
+ */
+ fun onUserLeaveHint() {
+ if (activity != null && flutterView != null) {
+ engine.activityControlSurface.onUserLeaveHint();
+ }
+ }
+}
diff --git a/add_to_app/android_view/app/src/main/java/dev/flutter/example/androidView/ListAdapter.kt b/add_to_app/android_view/app/src/main/java/dev/flutter/example/androidView/ListAdapter.kt
new file mode 100644
index 000000000..7a74ccf4c
--- /dev/null
+++ b/add_to_app/android_view/app/src/main/java/dev/flutter/example/androidView/ListAdapter.kt
@@ -0,0 +1,112 @@
+// Copyright 2019 The Flutter team. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package dev.flutter.example.androidView
+
+import android.content.Context
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import dev.flutter.example.androidView.databinding.AndroidCardBinding
+import io.flutter.embedding.android.FlutterView
+import io.flutter.plugin.common.MethodChannel
+import java.util.*
+import kotlin.random.Random
+
+/**
+ * A demo-specific implementation of a [RecyclerView.Adapter] to setup the demo environment used
+ * to display view-level Flutter cells inside a list.
+ *
+ * The only instructional parts of this class are to show when to call
+ * [FlutterViewEngine.attachFlutterView] and [FlutterViewEngine.detachActivity] on a
+ * [FlutterViewEngine] equivalent class that you may want to create in your own application.
+ */
+class ListAdapter(context: Context, private val flutterViewEngine: FlutterViewEngine) : RecyclerView.Adapter() {
+ // Save the previous cells determined to be Flutter cells to avoid a confusing visual effect
+ // that the Flutter cells change position when scrolling back.
+ var previousFlutterCells = TreeSet();
+
+ private val matchParentLayout = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
+
+ private val random = Random.Default
+ private val flutterView = FlutterView(context)
+ private val flutterChannel = MethodChannel(flutterViewEngine.engine.dartExecutor, "dev.flutter.example/cell")
+
+ private var flutterCell: Cell? = null
+
+ /**
+ * A [RecyclerView.ViewHolder] based on the `android_card` layout XML.
+ */
+ inner class Cell(val binding: AndroidCardBinding) : RecyclerView.ViewHolder(binding.root) {
+
+ }
+
+ override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): Cell {
+ val binding = AndroidCardBinding.inflate(LayoutInflater.from(viewGroup.context), viewGroup, false)
+
+ // Let the default view holder have an "Android card" inflated from the layout XML. When
+ // needed, hide the Android card and show a Flutter one instead.
+ return Cell(binding)
+ }
+
+ override fun onBindViewHolder(cell: Cell, position: Int) {
+ // While scrolling forward, if no Flutter is presently showing, let the next one have a 1/3
+ // chance of being Flutter.
+ //
+ // While scrolling backward, let it be deterministic, and only show cells that were
+ // previously Flutter cells as Flutter cells.
+ if (previousFlutterCells.contains(position)
+ || ((previousFlutterCells.isEmpty() || position > previousFlutterCells.last())
+ && flutterCell == null
+ && random.nextInt(3) == 0)) {
+ // If we're restoring a cell at a previous location, the current cell may not have
+ // recycled yet since that JVM timing is indeterministic. Yank it from the current one.
+ //
+ // This shouldn't produce any visual glitches since in the forward direction,
+ // Flutter cells were only introduced once the previous Flutter cell recycled.
+ if (flutterCell != null) {
+ Log.w("FeedAdapter", "While restoring a previous Flutter cell, a current "
+ + "yet to be recycled Flutter cell was detached.")
+ flutterCell!!.binding.root.removeView(flutterView)
+ flutterViewEngine.detachFlutterView()
+ flutterCell = null
+ }
+
+ // Add the Flutter card and hide the Android card for the cells chosen to be Flutter
+ // cells.
+ cell.binding.root.addView(flutterView, matchParentLayout)
+ cell.binding.androidCard.visibility = View.GONE
+
+ // Keep track of the cell so we know which one to restore back to the "Android cell"
+ // state when the view gets recycled.
+ flutterCell = cell
+ // Keep track that this position has once been a Flutter cell. Let it be a Flutter cell
+ // again when scrolling back to this position.
+ previousFlutterCells.add(position)
+
+ // This is what makes the Flutter cell start rendering.
+ flutterViewEngine.attachFlutterView(flutterView)
+ // Tell Flutter which index it's at so Flutter could show the cell number too in its
+ // own widget tree.
+ flutterChannel.invokeMethod("setCellNumber", position)
+ } else {
+ // If it's not selected as a Flutter cell, just show the Android card.
+ cell.binding.androidCard.visibility = View.VISIBLE
+ cell.binding.cellNumber.text = position.toString();
+ }
+ }
+
+ override fun getItemCount() = 100
+
+ override fun onViewRecycled(cell: Cell) {
+ if (cell == flutterCell) {
+ cell.binding.root.removeView(flutterView)
+ flutterViewEngine.detachFlutterView()
+ flutterCell = null
+ }
+ super.onViewRecycled(cell)
+ }
+}
diff --git a/add_to_app/android_view/app/src/main/java/dev/flutter/example/androidView/MainActivity.kt b/add_to_app/android_view/app/src/main/java/dev/flutter/example/androidView/MainActivity.kt
new file mode 100644
index 000000000..9398eae56
--- /dev/null
+++ b/add_to_app/android_view/app/src/main/java/dev/flutter/example/androidView/MainActivity.kt
@@ -0,0 +1,110 @@
+// Copyright 2019 The Flutter team. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package dev.flutter.example.androidView
+
+import android.content.Intent
+import android.os.Bundle
+import android.os.Parcelable
+import androidx.appcompat.app.AppCompatActivity
+import androidx.recyclerview.widget.LinearLayoutManager
+import dev.flutter.example.androidView.databinding.ActivityMainBinding
+import io.flutter.FlutterInjector
+import io.flutter.embedding.engine.FlutterEngine
+import io.flutter.embedding.engine.dart.DartExecutor
+import java.util.*
+import kotlin.collections.ArrayList
+
+// There are 3 files in this sample. MainActivity and ListAdapter are just
+// fictional setups. FlutterViewEngine is instructional and demonstrates the
+// various plumbing needed for a functioning FlutterView integration.
+/**
+ * Main activity for this demo that shows a page with a `RecyclerView`.
+ *
+ * There are 3 files in this sample. MainActivity and ListAdapter are just fictional setups.
+ * FlutterViewEngine is instructional and demonstrates the various plumbing needed for a functioning
+ * FlutterView integration.
+ */
+class MainActivity : AppCompatActivity() {
+
+ private lateinit var binding: ActivityMainBinding
+ private lateinit var flutterViewEngine: FlutterViewEngine
+
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ binding = ActivityMainBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ // TODO: create a multi-engine version after
+ // https://github.com/flutter/flutter/issues/72009 is built.
+ val engine = FlutterEngine(applicationContext)
+ engine.dartExecutor.executeDartEntrypoint(
+ DartExecutor.DartEntrypoint(
+ FlutterInjector.instance().flutterLoader().findAppBundlePath(),
+ "showCell"))
+
+ flutterViewEngine = FlutterViewEngine(engine)
+ // The activity and FlutterView have different lifecycles.
+ // Attach the activity right away but only start rendering when the
+ // view is also scrolled into the screen.
+ flutterViewEngine.attachToActivity(this)
+
+ val layoutManager = LinearLayoutManager(this)
+ val recyclerView = binding.recyclerView
+ val adapter = ListAdapter(this, flutterViewEngine)
+ recyclerView.layoutManager = layoutManager
+ recyclerView.adapter = adapter
+
+ // If the activity was restarted, keep track of the previous scroll
+ // position and of the previous cell indices that were randomly selected
+ // as Flutter cells to preserve immersion.
+ layoutManager.onRestoreInstanceState(savedInstanceState?.getParcelable("layoutManager"))
+ val previousFlutterCellsArray = savedInstanceState?.getIntegerArrayList("adapter")
+ if (previousFlutterCellsArray != null) {
+ adapter.previousFlutterCells = TreeSet(previousFlutterCellsArray)
+ }
+ }
+
+ override fun onSaveInstanceState(outState: Bundle) {
+ super.onSaveInstanceState(outState)
+
+ outState.putParcelable("layoutManager", binding.recyclerView.layoutManager?.onSaveInstanceState())
+ val previousFlutterCells = (binding.recyclerView.adapter as? ListAdapter)?.previousFlutterCells
+ if (previousFlutterCells != null) {
+ outState.putIntegerArrayList(
+ "adapter",
+ ArrayList(previousFlutterCells)
+ )
+ }
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ flutterViewEngine.detachActivity()
+ }
+
+ // These below aren't used here in this demo but would be needed for Flutter plugins that may
+ // consume these events.
+
+ override fun onRequestPermissionsResult(
+ requestCode: Int,
+ permissions: Array,
+ grantResults: IntArray
+ ) {
+ flutterViewEngine.onRequestPermissionsResult(requestCode, permissions, grantResults)
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults)
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ flutterViewEngine.onActivityResult(requestCode, resultCode, data)
+ super.onActivityResult(requestCode, resultCode, data)
+ }
+
+ override fun onUserLeaveHint() {
+ flutterViewEngine.onUserLeaveHint()
+ super.onUserLeaveHint()
+ }
+}
diff --git a/add_to_app/android_view/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/add_to_app/android_view/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 000000000..2b068d114
--- /dev/null
+++ b/add_to_app/android_view/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/add_to_app/android_view/app/src/main/res/drawable/ic_dashboard_black_24dp.xml b/add_to_app/android_view/app/src/main/res/drawable/ic_dashboard_black_24dp.xml
new file mode 100644
index 000000000..46fc8deec
--- /dev/null
+++ b/add_to_app/android_view/app/src/main/res/drawable/ic_dashboard_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/add_to_app/android_view/app/src/main/res/drawable/ic_home_black_24dp.xml b/add_to_app/android_view/app/src/main/res/drawable/ic_home_black_24dp.xml
new file mode 100644
index 000000000..f8bb0b556
--- /dev/null
+++ b/add_to_app/android_view/app/src/main/res/drawable/ic_home_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/add_to_app/android_view/app/src/main/res/drawable/ic_launcher_background.xml b/add_to_app/android_view/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 000000000..07d5da9cb
--- /dev/null
+++ b/add_to_app/android_view/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/add_to_app/android_view/app/src/main/res/drawable/ic_notifications_black_24dp.xml b/add_to_app/android_view/app/src/main/res/drawable/ic_notifications_black_24dp.xml
new file mode 100644
index 000000000..78b75c39b
--- /dev/null
+++ b/add_to_app/android_view/app/src/main/res/drawable/ic_notifications_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/add_to_app/android_view/app/src/main/res/layout/activity_main.xml b/add_to_app/android_view/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 000000000..492a955dc
--- /dev/null
+++ b/add_to_app/android_view/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/add_to_app/android_view/app/src/main/res/layout/android_card.xml b/add_to_app/android_view/app/src/main/res/layout/android_card.xml
new file mode 100644
index 000000000..800762412
--- /dev/null
+++ b/add_to_app/android_view/app/src/main/res/layout/android_card.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/add_to_app/android_view/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/add_to_app/android_view/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 000000000..eca70cfe5
--- /dev/null
+++ b/add_to_app/android_view/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/add_to_app/android_view/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/add_to_app/android_view/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 000000000..eca70cfe5
--- /dev/null
+++ b/add_to_app/android_view/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/add_to_app/android_view/app/src/main/res/mipmap-hdpi/ic_launcher.png b/add_to_app/android_view/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..a571e6009
Binary files /dev/null and b/add_to_app/android_view/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/add_to_app/android_view/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/add_to_app/android_view/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 000000000..61da551c5
Binary files /dev/null and b/add_to_app/android_view/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/add_to_app/android_view/app/src/main/res/mipmap-mdpi/ic_launcher.png b/add_to_app/android_view/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..c41dd2853
Binary files /dev/null and b/add_to_app/android_view/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/add_to_app/android_view/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/add_to_app/android_view/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 000000000..db5080a75
Binary files /dev/null and b/add_to_app/android_view/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/add_to_app/android_view/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/add_to_app/android_view/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..6dba46dab
Binary files /dev/null and b/add_to_app/android_view/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/add_to_app/android_view/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/add_to_app/android_view/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..da31a871c
Binary files /dev/null and b/add_to_app/android_view/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/add_to_app/android_view/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/add_to_app/android_view/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..15ac68172
Binary files /dev/null and b/add_to_app/android_view/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/add_to_app/android_view/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/add_to_app/android_view/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..b216f2d31
Binary files /dev/null and b/add_to_app/android_view/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/add_to_app/android_view/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/add_to_app/android_view/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..f25a41974
Binary files /dev/null and b/add_to_app/android_view/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/add_to_app/android_view/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/add_to_app/android_view/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..e96783ccc
Binary files /dev/null and b/add_to_app/android_view/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/add_to_app/android_view/app/src/main/res/values-night/themes.xml b/add_to_app/android_view/app/src/main/res/values-night/themes.xml
new file mode 100644
index 000000000..82b8203a2
--- /dev/null
+++ b/add_to_app/android_view/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/add_to_app/android_view/app/src/main/res/values/colors.xml b/add_to_app/android_view/app/src/main/res/values/colors.xml
new file mode 100644
index 000000000..f8c6127d3
--- /dev/null
+++ b/add_to_app/android_view/app/src/main/res/values/colors.xml
@@ -0,0 +1,10 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+
\ No newline at end of file
diff --git a/add_to_app/android_view/app/src/main/res/values/dimens.xml b/add_to_app/android_view/app/src/main/res/values/dimens.xml
new file mode 100644
index 000000000..e00c2dd14
--- /dev/null
+++ b/add_to_app/android_view/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
\ No newline at end of file
diff --git a/add_to_app/android_view/app/src/main/res/values/strings.xml b/add_to_app/android_view/app/src/main/res/values/strings.xml
new file mode 100644
index 000000000..1e7414695
--- /dev/null
+++ b/add_to_app/android_view/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Flutter View Integration
+
\ No newline at end of file
diff --git a/add_to_app/android_view/app/src/main/res/values/themes.xml b/add_to_app/android_view/app/src/main/res/values/themes.xml
new file mode 100644
index 000000000..2d692a11c
--- /dev/null
+++ b/add_to_app/android_view/app/src/main/res/values/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/add_to_app/android_view/build.gradle b/add_to_app/android_view/build.gradle
new file mode 100644
index 000000000..dd599f66f
--- /dev/null
+++ b/add_to_app/android_view/build.gradle
@@ -0,0 +1,26 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+ ext.kotlin_version = "1.4.21"
+ repositories {
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath "com.android.tools.build:gradle:4.2.0-beta02"
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
\ No newline at end of file
diff --git a/add_to_app/android_view/gradle.properties b/add_to_app/android_view/gradle.properties
new file mode 100644
index 000000000..252175276
--- /dev/null
+++ b/add_to_app/android_view/gradle.properties
@@ -0,0 +1,19 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app"s APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
\ No newline at end of file
diff --git a/add_to_app/android_view/gradle/wrapper/gradle-wrapper.jar b/add_to_app/android_view/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000..f6b961fd5
Binary files /dev/null and b/add_to_app/android_view/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/add_to_app/android_view/gradle/wrapper/gradle-wrapper.properties b/add_to_app/android_view/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..b2f6c917a
--- /dev/null
+++ b/add_to_app/android_view/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Dec 18 11:51:17 PST 2020
+distributionBase=GRADLE_USER_HOME
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
+distributionPath=wrapper/dists
+zipStorePath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
diff --git a/add_to_app/android_view/gradlew b/add_to_app/android_view/gradlew
new file mode 100755
index 000000000..cccdd3d51
--- /dev/null
+++ b/add_to_app/android_view/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/add_to_app/android_view/gradlew.bat b/add_to_app/android_view/gradlew.bat
new file mode 100644
index 000000000..e95643d6a
--- /dev/null
+++ b/add_to_app/android_view/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/add_to_app/android_view/settings.gradle b/add_to_app/android_view/settings.gradle
new file mode 100644
index 000000000..f32227045
--- /dev/null
+++ b/add_to_app/android_view/settings.gradle
@@ -0,0 +1,7 @@
+rootProject.name = "Flutter View Integration"
+include ':app'
+setBinding(new Binding([gradle: this]))
+evaluate(new File(
+ settingsDir.parentFile,
+ 'flutter_module_using_plugin/.android/include_flutter.groovy'
+))
diff --git a/add_to_app/flutter_module_using_plugin/lib/cell.dart b/add_to_app/flutter_module_using_plugin/lib/cell.dart
new file mode 100644
index 000000000..3715d684f
--- /dev/null
+++ b/add_to_app/flutter_module_using_plugin/lib/cell.dart
@@ -0,0 +1,136 @@
+// Copyright 2019 The Flutter team. 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:math';
+
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:sensors/sensors.dart';
+
+// This is on alternate entrypoint for this module to display Flutter UI in
+// a (multi-)view integration scenario.
+void main() {
+ runApp(Cell());
+}
+
+class Cell extends StatefulWidget {
+ @override
+ State createState() => new _CellState();
+}
+
+class _CellState extends State with WidgetsBindingObserver {
+ static const double gravity = 9.81;
+ static final AccelerometerEvent defaultPosition = AccelerometerEvent(0, 0, 0);
+
+ int cellNumber = 0;
+ Random _random;
+ AppLifecycleState appLifecycleState;
+
+ @override
+ void initState() {
+ final channel = MethodChannel('dev.flutter.example/cell');
+ channel.setMethodCallHandler((MethodCall call) async {
+ if (call.method == 'setCellNumber') {
+ setState(() {
+ cellNumber = call.arguments as int;
+ _random = Random(cellNumber);
+ });
+ }
+ });
+ // Keep track of what the current platform lifecycle state is.
+ WidgetsBinding.instance.addObserver(this);
+ super.initState();
+ }
+
+ @override
+ void dispose() {
+ WidgetsBinding.instance.removeObserver(this);
+ super.dispose();
+ }
+
+ @override
+ void didChangeAppLifecycleState(AppLifecycleState state) {
+ setState(() {
+ appLifecycleState = state;
+ });
+ }
+
+ // Show a random bright color.
+ Color randomLightColor() {
+ _random ??= Random(cellNumber);
+
+ return Color.fromARGB(255, _random.nextInt(50) + 205,
+ _random.nextInt(50) + 205, _random.nextInt(50) + 205);
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return MaterialApp(
+ // The Flutter cells will be noticeably different (due to background color
+ // and the Flutter logo). The banner breaks immersion.
+ debugShowCheckedModeBanner: false,
+ home: Container(
+ color: Colors.white,
+ child: Builder(
+ builder: (BuildContext context) {
+ return Card(
+ // Mimic the platform Material look.
+ margin: EdgeInsets.symmetric(horizontal: 36, vertical: 24),
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(16),
+ ),
+ elevation: 16,
+ color: randomLightColor(),
+ child: Stack(
+ children: [
+ Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Text(
+ // Show a number provided by the platform based on
+ // the cell's index.
+ cellNumber.toString(),
+ style: Theme.of(context).textTheme.headline3,
+ ),
+ ],
+ ),
+ ),
+ Positioned(
+ left: 42,
+ top: 0,
+ bottom: 0,
+ child: Opacity(
+ opacity: 0.2,
+ child: StreamBuilder(
+ // Don't continuously rebuild for nothing when the
+ // cell isn't visible.
+ stream: appLifecycleState == AppLifecycleState.resumed
+ ? accelerometerEvents
+ : Stream.value(defaultPosition),
+ initialData: defaultPosition,
+ builder: (BuildContext context,
+ AsyncSnapshot snapshot) {
+ return Transform(
+ // Figure out the phone's orientation relative
+ // to gravity's direction. Ignore the z vector.
+ transform: Matrix4.rotationX(
+ snapshot.data.y / gravity * pi / 2)
+ ..multiply(Matrix4.rotationY(
+ snapshot.data.x / gravity * pi / 2)),
+ alignment: Alignment.center,
+ child: FlutterLogo(size: 72));
+ },
+ ),
+ ),
+ ),
+ ],
+ ),
+ );
+ },
+ ),
+ ),
+ );
+ }
+}
diff --git a/add_to_app/flutter_module_using_plugin/lib/main.dart b/add_to_app/flutter_module_using_plugin/lib/main.dart
index 3ecd94ea0..bcdb18031 100644
--- a/add_to_app/flutter_module_using_plugin/lib/main.dart
+++ b/add_to_app/flutter_module_using_plugin/lib/main.dart
@@ -7,6 +7,8 @@ import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart' as launcher;
+import 'cell.dart';
+
/// The entrypoint for the flutter module.
void main() {
// This call ensures the Flutter binding has been set up before creating the
@@ -23,6 +25,14 @@ void main() {
);
}
+/// This is on alternate entrypoint for this module to display Flutter UI in
+/// a (multi-)view integration scenario.
+// This is unfortunately in this file due to
+// https://github.com/flutter/flutter/issues/72630.
+void showCell() {
+ runApp(Cell());
+}
+
/// A simple model that uses a [MethodChannel] as the source of truth for the
/// state of a counter.
///
diff --git a/add_to_app/flutter_module_using_plugin/pubspec.lock b/add_to_app/flutter_module_using_plugin/pubspec.lock
index 9bceaf2b3..6328a967d 100644
--- a/add_to_app/flutter_module_using_plugin/pubspec.lock
+++ b/add_to_app/flutter_module_using_plugin/pubspec.lock
@@ -7,49 +7,49 @@ packages:
name: async
url: "https://pub.dartlang.org"
source: hosted
- version: "2.5.0-nullsafety.1"
+ version: "2.5.0-nullsafety.3"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.dartlang.org"
source: hosted
- version: "2.1.0-nullsafety.1"
+ version: "2.1.0-nullsafety.3"
characters:
dependency: transitive
description:
name: characters
url: "https://pub.dartlang.org"
source: hosted
- version: "1.1.0-nullsafety.3"
+ version: "1.1.0-nullsafety.5"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
- version: "1.2.0-nullsafety.1"
+ version: "1.2.0-nullsafety.3"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.dartlang.org"
source: hosted
- version: "1.1.0-nullsafety.1"
+ version: "1.1.0-nullsafety.3"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.dartlang.org"
source: hosted
- version: "1.15.0-nullsafety.3"
+ version: "1.15.0-nullsafety.5"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
- version: "1.2.0-nullsafety.1"
+ version: "1.2.0-nullsafety.3"
flutter:
dependency: "direct main"
description: flutter
@@ -65,20 +65,27 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
+ js:
+ dependency: transitive
+ description:
+ name: js
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.6.3-nullsafety.3"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
- version: "0.12.10-nullsafety.1"
+ version: "0.12.10-nullsafety.3"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
- version: "1.3.0-nullsafety.3"
+ version: "1.3.0-nullsafety.6"
nested:
dependency: transitive
description:
@@ -92,7 +99,7 @@ packages:
name: path
url: "https://pub.dartlang.org"
source: hosted
- version: "1.8.0-nullsafety.1"
+ version: "1.8.0-nullsafety.3"
plugin_platform_interface:
dependency: transitive
description:
@@ -107,6 +114,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "4.3.2+3"
+ sensors:
+ dependency: "direct main"
+ description:
+ name: sensors
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.4.2+6"
sky_engine:
dependency: transitive
description: flutter
@@ -118,49 +132,49 @@ packages:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
- version: "1.8.0-nullsafety.2"
+ version: "1.8.0-nullsafety.4"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.dartlang.org"
source: hosted
- version: "1.10.0-nullsafety.1"
+ version: "1.10.0-nullsafety.6"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.dartlang.org"
source: hosted
- version: "2.1.0-nullsafety.1"
+ version: "2.1.0-nullsafety.3"
string_scanner:
dependency: transitive
description:
name: string_scanner
url: "https://pub.dartlang.org"
source: hosted
- version: "1.1.0-nullsafety.1"
+ version: "1.1.0-nullsafety.3"
term_glyph:
dependency: transitive
description:
name: term_glyph
url: "https://pub.dartlang.org"
source: hosted
- version: "1.2.0-nullsafety.1"
+ version: "1.2.0-nullsafety.3"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
- version: "0.2.19-nullsafety.2"
+ version: "0.2.19-nullsafety.6"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.dartlang.org"
source: hosted
- version: "1.3.0-nullsafety.3"
+ version: "1.3.0-nullsafety.5"
url_launcher:
dependency: "direct main"
description:
@@ -209,7 +223,7 @@ packages:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
- version: "2.1.0-nullsafety.3"
+ version: "2.1.0-nullsafety.5"
sdks:
- dart: ">=2.10.0-110 <2.11.0"
- flutter: ">=1.22.0 <2.0.0"
+ dart: ">=2.12.0-0.0 <3.0.0"
+ flutter: ">=1.22.0"
diff --git a/add_to_app/flutter_module_using_plugin/pubspec.yaml b/add_to_app/flutter_module_using_plugin/pubspec.yaml
index 39a7737c5..2613ff379 100644
--- a/add_to_app/flutter_module_using_plugin/pubspec.yaml
+++ b/add_to_app/flutter_module_using_plugin/pubspec.yaml
@@ -11,6 +11,7 @@ dependencies:
sdk: flutter
provider: ^4.1.0
url_launcher: ^5.2.5
+ sensors: ^0.4.2
dev_dependencies:
flutter_test:
|