diff --git a/add_to_app/android_view/content_sizing_android_view/.gitignore b/add_to_app/android_view/content_sizing_android_view/.gitignore
new file mode 100644
index 00000000000..aa724b77071
--- /dev/null
+++ b/add_to_app/android_view/content_sizing_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/content_sizing_android_view/README.md b/add_to_app/android_view/content_sizing_android_view/README.md
new file mode 100644
index 00000000000..3cf900235eb
--- /dev/null
+++ b/add_to_app/android_view/content_sizing_android_view/README.md
@@ -0,0 +1,4 @@
+# android_view
+
+An example of an Android app that integrates a Flutter add-to-app module at a
+view level. For more information see [../README.md](../README.md).
diff --git a/add_to_app/android_view/content_sizing_android_view/app/.gitignore b/add_to_app/android_view/content_sizing_android_view/app/.gitignore
new file mode 100644
index 00000000000..42afabfd2ab
--- /dev/null
+++ b/add_to_app/android_view/content_sizing_android_view/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/add_to_app/android_view/content_sizing_android_view/app/build.gradle b/add_to_app/android_view/content_sizing_android_view/app/build.gradle
new file mode 100644
index 00000000000..0797ed565f9
--- /dev/null
+++ b/add_to_app/android_view/content_sizing_android_view/app/build.gradle
@@ -0,0 +1,58 @@
+plugins {
+ id 'com.android.application'
+ id 'kotlin-android'
+}
+
+android {
+ compileSdk 36
+
+ lint {
+ baseline = file("lint-baseline.xml")
+ }
+
+ defaultConfig {
+ applicationId "dev.flutter.example.androidView"
+ minSdkVersion 24
+ targetSdk 36
+ 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
+ }
+ namespace 'dev.flutter.example.androidView'
+}
+
+dependencies {
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+ implementation 'androidx.core:core-ktx:1.13.1'
+ implementation 'androidx.appcompat:appcompat:1.7.0'
+ implementation 'com.google.android.material:material:1.12.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
+ implementation 'androidx.vectordrawable:vectordrawable:1.2.0'
+ implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.8.4'
+ implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.4'
+ implementation 'androidx.navigation:navigation-fragment-ktx:2.7.7'
+ implementation 'androidx.navigation:navigation-ui-ktx:2.7.7'
+ implementation "androidx.recyclerview:recyclerview:1.3.2"
+ implementation project(path: ':flutter')
+ testImplementation 'junit:junit:4.+'
+ androidTestImplementation 'androidx.test.ext:junit:1.2.1'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
+}
diff --git a/add_to_app/android_view/content_sizing_android_view/app/lint-baseline.xml b/add_to_app/android_view/content_sizing_android_view/app/lint-baseline.xml
new file mode 100644
index 00000000000..18092041fe1
--- /dev/null
+++ b/add_to_app/android_view/content_sizing_android_view/app/lint-baseline.xml
@@ -0,0 +1,279 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/add_to_app/android_view/content_sizing_android_view/app/proguard-rules.pro b/add_to_app/android_view/content_sizing_android_view/app/proguard-rules.pro
new file mode 100644
index 00000000000..481bb434814
--- /dev/null
+++ b/add_to_app/android_view/content_sizing_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/content_sizing_android_view/app/src/main/AndroidManifest.xml b/add_to_app/android_view/content_sizing_android_view/app/src/main/AndroidManifest.xml
new file mode 100644
index 00000000000..920589d73ae
--- /dev/null
+++ b/add_to_app/android_view/content_sizing_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/content_sizing_android_view/app/src/main/java/dev/flutter/example/androidView/FlutterViewEngine.kt b/add_to_app/android_view/content_sizing_android_view/app/src/main/java/dev/flutter/example/androidView/FlutterViewEngine.kt
new file mode 100644
index 00000000000..e0c96a1b261
--- /dev/null
+++ b/add_to_app/android_view/content_sizing_android_view/app/src/main/java/dev/flutter/example/androidView/FlutterViewEngine.kt
@@ -0,0 +1,243 @@
+// 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.app.Activity
+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.ExclusiveAppComponent
+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, ExclusiveAppComponent{
+ 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(this, 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();
+ }
+ }
+
+ /**
+ * Called when another App Component is about to become attached to the [ ] this App Component
+ * is currently attached to.
+ *
+ *
+ * This App Component's connections to the [io.flutter.embedding.engine.FlutterEngine]
+ * are still valid at the moment of this call.
+ */
+ override fun detachFromFlutterEngine() {
+ // Do nothing here
+ }
+
+ /**
+ * Retrieve the App Component behind this exclusive App Component.
+ *
+ * @return The app component.
+ */
+ override fun getAppComponent(): Activity {
+ return activity!!;
+ }
+}
diff --git a/add_to_app/android_view/content_sizing_android_view/app/src/main/java/dev/flutter/example/androidView/ListAdapter.kt b/add_to_app/android_view/content_sizing_android_view/app/src/main/java/dev/flutter/example/androidView/ListAdapter.kt
new file mode 100644
index 00000000000..f3867c650e2
--- /dev/null
+++ b/add_to_app/android_view/content_sizing_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.WRAP_CONTENT)
+
+ 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/content_sizing_android_view/app/src/main/java/dev/flutter/example/androidView/MainActivity.kt b/add_to_app/android_view/content_sizing_android_view/app/src/main/java/dev/flutter/example/androidView/MainActivity.kt
new file mode 100644
index 00000000000..5367a605659
--- /dev/null
+++ b/add_to_app/android_view/content_sizing_android_view/app/src/main/java/dev/flutter/example/androidView/MainActivity.kt
@@ -0,0 +1,118 @@
+// 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.core.os.BundleCompat
+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.
+ if (savedInstanceState != null) {
+ val state = BundleCompat.getParcelable(
+ savedInstanceState,
+ "layoutManager",
+ Parcelable::class.java
+ )
+ layoutManager.onRestoreInstanceState(state)
+ }
+ 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/content_sizing_android_view/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 00000000000..2b068d11462
--- /dev/null
+++ b/add_to_app/android_view/content_sizing_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/content_sizing_android_view/app/src/main/res/drawable/ic_dashboard_black_24dp.xml b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/drawable/ic_dashboard_black_24dp.xml
new file mode 100644
index 00000000000..46fc8deec32
--- /dev/null
+++ b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/drawable/ic_dashboard_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/add_to_app/android_view/content_sizing_android_view/app/src/main/res/drawable/ic_home_black_24dp.xml b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/drawable/ic_home_black_24dp.xml
new file mode 100644
index 00000000000..f8bb0b55633
--- /dev/null
+++ b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/drawable/ic_home_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/add_to_app/android_view/content_sizing_android_view/app/src/main/res/drawable/ic_launcher_background.xml b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 00000000000..07d5da9cbf1
--- /dev/null
+++ b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/add_to_app/android_view/content_sizing_android_view/app/src/main/res/drawable/ic_notifications_black_24dp.xml b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/drawable/ic_notifications_black_24dp.xml
new file mode 100644
index 00000000000..78b75c39b55
--- /dev/null
+++ b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/drawable/ic_notifications_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/add_to_app/android_view/content_sizing_android_view/app/src/main/res/layout/activity_main.xml b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 00000000000..492a955dcf2
--- /dev/null
+++ b/add_to_app/android_view/content_sizing_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/content_sizing_android_view/app/src/main/res/layout/android_card.xml b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/layout/android_card.xml
new file mode 100644
index 00000000000..3bf306bd17a
--- /dev/null
+++ b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/layout/android_card.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/add_to_app/android_view/content_sizing_android_view/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 00000000000..eca70cfe52e
--- /dev/null
+++ b/add_to_app/android_view/content_sizing_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/content_sizing_android_view/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 00000000000..eca70cfe52e
--- /dev/null
+++ b/add_to_app/android_view/content_sizing_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/content_sizing_android_view/app/src/main/res/mipmap-hdpi/ic_launcher.png b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 00000000000..a571e60098c
Binary files /dev/null and b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/add_to_app/android_view/content_sizing_android_view/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 00000000000..61da551c559
Binary files /dev/null and b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/add_to_app/android_view/content_sizing_android_view/app/src/main/res/mipmap-mdpi/ic_launcher.png b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 00000000000..c41dd285319
Binary files /dev/null and b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/add_to_app/android_view/content_sizing_android_view/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 00000000000..db5080a7527
Binary files /dev/null and b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/add_to_app/android_view/content_sizing_android_view/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 00000000000..6dba46dab19
Binary files /dev/null and b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/add_to_app/android_view/content_sizing_android_view/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 00000000000..da31a871c8d
Binary files /dev/null and b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/add_to_app/android_view/content_sizing_android_view/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 00000000000..15ac681720f
Binary files /dev/null and b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/add_to_app/android_view/content_sizing_android_view/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 00000000000..b216f2d313c
Binary files /dev/null and b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/add_to_app/android_view/content_sizing_android_view/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 00000000000..f25a4197447
Binary files /dev/null and b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/add_to_app/android_view/content_sizing_android_view/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 00000000000..e96783ccce8
Binary files /dev/null and b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/add_to_app/android_view/content_sizing_android_view/app/src/main/res/values-night/themes.xml b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/values-night/themes.xml
new file mode 100644
index 00000000000..82b8203a20e
--- /dev/null
+++ b/add_to_app/android_view/content_sizing_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/content_sizing_android_view/app/src/main/res/values/colors.xml b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/values/colors.xml
new file mode 100644
index 00000000000..f8c6127d327
--- /dev/null
+++ b/add_to_app/android_view/content_sizing_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/content_sizing_android_view/app/src/main/res/values/dimens.xml b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/values/dimens.xml
new file mode 100644
index 00000000000..e00c2dd143c
--- /dev/null
+++ b/add_to_app/android_view/content_sizing_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/content_sizing_android_view/app/src/main/res/values/strings.xml b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/values/strings.xml
new file mode 100644
index 00000000000..97b21376941
--- /dev/null
+++ b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Flutter View Content Sizing Integration
+
\ No newline at end of file
diff --git a/add_to_app/android_view/content_sizing_android_view/app/src/main/res/values/themes.xml b/add_to_app/android_view/content_sizing_android_view/app/src/main/res/values/themes.xml
new file mode 100644
index 00000000000..2d692a11c8b
--- /dev/null
+++ b/add_to_app/android_view/content_sizing_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/content_sizing_android_view/build.gradle b/add_to_app/android_view/content_sizing_android_view/build.gradle
new file mode 100644
index 00000000000..f60b7c42367
--- /dev/null
+++ b/add_to_app/android_view/content_sizing_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.8.22'
+ repositories {
+ google()
+ mavenCentral()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:8.9.1'
+ 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()
+ mavenCentral()
+ }
+}
+
+tasks.register('clean', Delete) {
+ delete rootProject.buildDir
+}
diff --git a/add_to_app/android_view/content_sizing_android_view/gradle.properties b/add_to_app/android_view/content_sizing_android_view/gradle.properties
new file mode 100644
index 00000000000..c8ce6fbcce6
--- /dev/null
+++ b/add_to_app/android_view/content_sizing_android_view/gradle.properties
@@ -0,0 +1,21 @@
+# 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
+android.nonTransitiveRClass=false
+android.nonFinalResIds=false
\ No newline at end of file
diff --git a/add_to_app/android_view/content_sizing_android_view/gradle/wrapper/gradle-wrapper.properties b/add_to_app/android_view/content_sizing_android_view/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000000..d7952dcde62
--- /dev/null
+++ b/add_to_app/android_view/content_sizing_android_view/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Jul 26 11:31:21 EDT 2024
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/add_to_app/android_view/content_sizing_android_view/gradlew b/add_to_app/android_view/content_sizing_android_view/gradlew
new file mode 100755
index 00000000000..cccdd3d517f
--- /dev/null
+++ b/add_to_app/android_view/content_sizing_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/content_sizing_android_view/gradlew.bat b/add_to_app/android_view/content_sizing_android_view/gradlew.bat
new file mode 100644
index 00000000000..f9553162f12
--- /dev/null
+++ b/add_to_app/android_view/content_sizing_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/content_sizing_android_view/settings.gradle b/add_to_app/android_view/content_sizing_android_view/settings.gradle
new file mode 100644
index 00000000000..62f8e6580a0
--- /dev/null
+++ b/add_to_app/android_view/content_sizing_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_content_sizing_android_view/.android/include_flutter.groovy'
+))
diff --git a/add_to_app/android_view/flutter_module_using_plugin_content_sizing_android_view/.gitignore b/add_to_app/android_view/flutter_module_using_plugin_content_sizing_android_view/.gitignore
new file mode 100644
index 00000000000..86f469179f5
--- /dev/null
+++ b/add_to_app/android_view/flutter_module_using_plugin_content_sizing_android_view/.gitignore
@@ -0,0 +1,42 @@
+.DS_Store
+.dart_tool/
+
+.packages
+.pub/
+
+.idea/
+.vagrant/
+.sconsign.dblite
+.svn/
+
+*.swp
+profile
+
+DerivedData/
+
+.generated/
+
+*.pbxuser
+*.mode1v3
+*.mode2v3
+*.perspectivev3
+
+!default.pbxuser
+!default.mode1v3
+!default.mode2v3
+!default.perspectivev3
+
+xcuserdata
+
+*.moved-aside
+
+*.pyc
+*sync/
+Icon?
+.tags*
+
+build/
+.android/
+.ios/
+.flutter-plugins
+.flutter-plugins-dependencies
diff --git a/add_to_app/android_view/flutter_module_using_plugin_content_sizing_android_view/.metadata b/add_to_app/android_view/flutter_module_using_plugin_content_sizing_android_view/.metadata
new file mode 100644
index 00000000000..194fb3cc0a2
--- /dev/null
+++ b/add_to_app/android_view/flutter_module_using_plugin_content_sizing_android_view/.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: 532a8fed41a4f6595965f02f3edf9666ba5ebf44
+ channel: master
+
+project_type: module
diff --git a/add_to_app/android_view/flutter_module_using_plugin_content_sizing_android_view/README.md b/add_to_app/android_view/flutter_module_using_plugin_content_sizing_android_view/README.md
new file mode 100644
index 00000000000..01c484f8242
--- /dev/null
+++ b/add_to_app/android_view/flutter_module_using_plugin_content_sizing_android_view/README.md
@@ -0,0 +1,14 @@
+# flutter_module_using_plugin
+
+An example Flutter module that uses a native plugin, intended for use in the
+Flutter add-to-app samples. For more information on how to use it, see the
+[README.md](../README.md) parent directory.
+
+## 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/flutter_module_using_plugin_content_sizing_android_view/analysis_options.yaml b/add_to_app/android_view/flutter_module_using_plugin_content_sizing_android_view/analysis_options.yaml
new file mode 100644
index 00000000000..13d6fe105a3
--- /dev/null
+++ b/add_to_app/android_view/flutter_module_using_plugin_content_sizing_android_view/analysis_options.yaml
@@ -0,0 +1 @@
+include: package:analysis_defaults/flutter.yaml
diff --git a/add_to_app/android_view/flutter_module_using_plugin_content_sizing_android_view/lib/cell.dart b/add_to_app/android_view/flutter_module_using_plugin_content_sizing_android_view/lib/cell.dart
new file mode 100644
index 00000000000..0c0f57f8502
--- /dev/null
+++ b/add_to_app/android_view/flutter_module_using_plugin_content_sizing_android_view/lib/cell.dart
@@ -0,0 +1,88 @@
+// 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_plus/sensors_plus.dart';
+
+// This is on alternate entrypoint for this module to display Flutter UI in
+// a (multi-)view integration scenario.
+void main() {
+ runApp(const Cell());
+}
+
+class Cell extends StatefulWidget {
+ const Cell({super.key});
+
+ @override
+ State createState() => _CellState();
+}
+
+const String text = '''Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum facilisis vel quam nec scelerisque. Nullam leo sapien, ornare blandit dui ac, varius condimentum leo. Vestibulum quis sem vulputate, varius dui nec, malesuada sem. Aliquam tincidunt pretium dolor, quis ullamcorper nunc consequat quis. Donec at dui in ex pharetra pretium. Quisque molestie massa vel tellus scelerisque feugiat. Ut sed consect etur neque.
+
+''';
+
+class _CellState extends State with WidgetsBindingObserver {
+ static const double gravity = 9.81;
+ static final AccelerometerEvent defaultPosition = AccelerometerEvent(
+ 0,
+ 0,
+ 0,
+ DateTime.now(),
+ );
+
+ int cellNumber = 0;
+ Random? _random;
+ AppLifecycleState? appLifecycleState;
+
+ @override
+ void initState() {
+ const channel = MethodChannel('dev.flutter.example/cell');
+ channel.setMethodCallHandler((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 Text(text * ((cellNumber % 4) + 1),
+ textDirection: TextDirection.ltr,
+ style: TextStyle(fontSize: 20.0));
+ }
+}
diff --git a/add_to_app/android_view/flutter_module_using_plugin_content_sizing_android_view/lib/main.dart b/add_to_app/android_view/flutter_module_using_plugin_content_sizing_android_view/lib/main.dart
new file mode 100644
index 00000000000..95d0874afcc
--- /dev/null
+++ b/add_to_app/android_view/flutter_module_using_plugin_content_sizing_android_view/lib/main.dart
@@ -0,0 +1,180 @@
+// 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 'package:flutter/material.dart';
+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
+ // MethodChannel-based model.
+ WidgetsFlutterBinding.ensureInitialized();
+
+ final model = CounterModel();
+
+ runApp(ChangeNotifierProvider.value(value: model, child: const MyApp()));
+}
+
+/// 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.
+@pragma("vm:entry-point")
+void showCell() {
+ runApp(const Cell());
+}
+
+/// A simple model that uses a [MethodChannel] as the source of truth for the
+/// state of a counter.
+///
+/// Rather than storing app state data within the Flutter module itself (where
+/// the native portions of the app can't access it), this module passes messages
+/// back to the containing app whenever it needs to increment or retrieve the
+/// value of the counter.
+class CounterModel extends ChangeNotifier {
+ CounterModel() {
+ _channel.setMethodCallHandler(_handleMessage);
+ _channel.invokeMethod('requestCounter');
+ }
+
+ final _channel = const MethodChannel('dev.flutter.example/counter');
+
+ int _count = 0;
+
+ int get count => _count;
+
+ void increment() {
+ _channel.invokeMethod('incrementCounter');
+ }
+
+ Future _handleMessage(MethodCall call) async {
+ if (call.method == 'reportCounter') {
+ _count = call.arguments as int;
+ notifyListeners();
+ }
+ }
+}
+
+/// The "app" displayed by this module.
+///
+/// It offers two routes, one suitable for displaying as a full screen and
+/// another designed to be part of a larger UI.
+class MyApp extends StatelessWidget {
+ const MyApp({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return MaterialApp(
+ title: 'Flutter Module Title',
+ theme: ThemeData(colorSchemeSeed: Colors.blue),
+ routes: {
+ '/': (context) => const FullScreenView(),
+ '/mini': (context) => const Contents(),
+ },
+ );
+ }
+}
+
+/// Wraps [Contents] in a Material [Scaffold] so it looks correct when displayed
+/// full-screen.
+class FullScreenView extends StatelessWidget {
+ const FullScreenView({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(title: const Text('Full-screen Flutter with plugin')),
+ body: const Contents(showExit: true),
+ );
+ }
+}
+
+/// The actual content displayed by the module.
+///
+/// This widget displays info about the state of a counter and how much room (in
+/// logical pixels) it's been given. It also offers buttons to increment the
+/// counter, opening the Flutter documentation via the url_launcher plugin, and
+/// (optionally) close the Flutter view.
+class Contents extends StatelessWidget {
+ final bool showExit;
+
+ const Contents({this.showExit = false, super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ final mediaInfo = MediaQuery.of(context);
+
+ return SizedBox.expand(
+ child: Stack(
+ children: [
+ Positioned.fill(
+ child: DecoratedBox(
+ decoration: BoxDecoration(
+ color: Theme.of(context).scaffoldBackgroundColor,
+ ),
+ ),
+ ),
+ const Positioned.fill(
+ child: Opacity(
+ opacity: .25,
+ child: FittedBox(fit: BoxFit.cover, child: FlutterLogo()),
+ ),
+ ),
+ Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Text(
+ 'Window is ${mediaInfo.size.width.toStringAsFixed(1)} x '
+ '${mediaInfo.size.height.toStringAsFixed(1)}',
+ style: Theme.of(context).textTheme.headlineSmall,
+ ),
+ const SizedBox(height: 16),
+ Consumer(
+ builder: (context, model, child) {
+ return Text(
+ 'Taps: ${model.count}',
+ style: Theme.of(context).textTheme.headlineSmall,
+ );
+ },
+ ),
+ const SizedBox(height: 16),
+ Consumer(
+ builder: (context, model, child) {
+ return FilledButton(
+ onPressed: () => model.increment(),
+ child: const Text('Tap me!'),
+ );
+ },
+ ),
+ FilledButton(
+ onPressed: () async {
+ // Use the url_launcher plugin to open the Flutter docs in
+ // a browser.
+ final url = Uri.parse('https://flutter.dev/docs');
+ if (await launcher.canLaunchUrl(url)) {
+ await launcher.launchUrl(url);
+ }
+ },
+ child: const Text('Open Flutter Docs'),
+ ),
+ if (showExit) ...[
+ const SizedBox(height: 16),
+ FilledButton(
+ onPressed: () => SystemNavigator.pop(),
+ child: const Text('Exit this screen'),
+ ),
+ ],
+ ],
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/add_to_app/android_view/flutter_module_using_plugin_content_sizing_android_view/pubspec.yaml b/add_to_app/android_view/flutter_module_using_plugin_content_sizing_android_view/pubspec.yaml
new file mode 100644
index 00000000000..4074a0aa1fc
--- /dev/null
+++ b/add_to_app/android_view/flutter_module_using_plugin_content_sizing_android_view/pubspec.yaml
@@ -0,0 +1,34 @@
+name: flutter_module_using_plugin_content_sizing_android_view
+description: An example Flutter module that uses a plugin.
+version: 1.0.0+1
+resolution: workspace
+
+environment:
+ sdk: ^3.8.1-0
+
+dependencies:
+ flutter:
+ sdk: flutter
+ provider: ^6.1.5
+ url_launcher: ^6.3.2
+ sensors_plus: ^6.1.1
+
+dev_dependencies:
+ analysis_defaults:
+ path: ../../../analysis_defaults
+ flutter_test:
+ sdk: flutter
+
+flutter:
+ uses-material-design: true
+
+ # This section identifies your Flutter project as a module meant for
+ # embedding in a native host app. These identifiers should _not_ ordinarily
+ # be changed after generation - they are used to ensure that the tooling can
+ # maintain consistency when adding or modifying assets and plugins.
+ # They also do not have any bearing on your native host application's
+ # identifiers, which may be completely independent or the same as these.
+ module:
+ androidX: true
+ androidPackage: dev.flutter.example.flutter_module_using_plugin
+ iosBundleIdentifier: dev.flutter.example.flutterModuleUsingPlugin
diff --git a/add_to_app/android_view/flutter_module_using_plugin_content_sizing_android_view/test/widget_test.dart b/add_to_app/android_view/flutter_module_using_plugin_content_sizing_android_view/test/widget_test.dart
new file mode 100644
index 00000000000..f6cdf39aad1
--- /dev/null
+++ b/add_to_app/android_view/flutter_module_using_plugin_content_sizing_android_view/test/widget_test.dart
@@ -0,0 +1,50 @@
+// This is a basic Flutter widget test.
+//
+// To perform an interaction with a widget in your test, use the WidgetTester
+// utility that Flutter provides. For example, you can send tap and scroll
+// gestures. You can also use WidgetTester to find child widgets in the widget
+// tree, read text, and verify that the values of widget properties are correct.
+
+import 'package:flutter/material.dart';
+import 'package:flutter_module_using_plugin_android_view/main.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:provider/provider.dart';
+
+class MockCounterModel extends ChangeNotifier implements CounterModel {
+ int _count = 0;
+
+ @override
+ int get count => _count;
+
+ @override
+ void increment() {
+ _count++;
+ notifyListeners();
+ }
+}
+
+void main() {
+ testWidgets('MiniView smoke test', (tester) async {
+ // Build our app and trigger a frame.
+ await tester.pumpWidget(
+ MaterialApp(
+ home: ChangeNotifierProvider.value(
+ value: MockCounterModel(),
+ child: const Contents(),
+ ),
+ ),
+ );
+
+ // Verify that our counter starts at 0.
+ expect(find.text('Taps: 0'), findsOneWidget);
+ expect(find.text('Taps: 1'), findsNothing);
+
+ // Tap the '+' icon and trigger a frame.
+ await tester.tap(find.text('Tap me!'));
+ await tester.pump();
+
+ // Verify that our counter has incremented.
+ expect(find.text('Taps: 0'), findsNothing);
+ expect(find.text('Taps: 1'), findsOneWidget);
+ });
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index b7d3e3c245f..d61189cc580 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -6,6 +6,7 @@ environment:
workspace:
- add_to_app/android_view/flutter_module_using_plugin_android_view
+ - add_to_app/android_view/flutter_module_using_plugin_content_sizing_android_view
- add_to_app/books/flutter_module_books
- add_to_app/fullscreen/flutter_module_fullscreen
- add_to_app/multiple_flutters/multiple_flutters_module
|