snapshot of https://github.com/flutter/samples.git (main, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Main Branch CI / Test Flutter beta on ubuntu-latest (push) Has been skipped
Main Branch CI / Test Flutter stable on ubuntu-latest (push) Has been skipped
Main Branch CI / Test Flutter beta on macos-latest (push) Has been cancelled
Main Branch CI / Test Flutter stable on macos-latest (push) Has been cancelled

This commit is contained in:
upstream-mirror
2026-09-16 01:43:26 +05:00
commit 9ed6a80afa
2886 changed files with 140184 additions and 0 deletions

View File

@@ -0,0 +1,80 @@
# multiple_flutters
This is a sample that shows how to use the Flutter Engine Groups API to embed
multiple instances of Flutter into an existing Android or iOS project.
## Getting Started
For iOS instructions, see:
[multiple_flutters_ios](./multiple_flutters_ios/README.md).
For Android instructions, see:
[multiple_flutters_android](./multiple_flutters_android/README.md).
## Requirements
* Flutter -- after Flutter v2
* Android
* Android Studio
* iOS
* Xcode
* Cocoapods
## Flutter Engine Group
These examples use the Flutter Engine Group APIs on the host platform which
allows engines to share memory and CPU intensive resources. This leads to easier
embedding of Flutter into an existing app since multiple entrypoints can be
maintained via a FlutterFragment on Android or a UIViewController on iOS.
Before FlutterEngineGroup, users had to juggle the usage of a small number of
engines judiciously.
More info on those API's can be found in the source
code:
- iOS -
[FlutterEngineGroup.h](https://github.com/flutter/engine/blob/master/shell/platform/darwin/ios/framework/Headers/FlutterEngineGroup.h)
- Android -
[FlutterEngineGroup.java](https://github.com/flutter/engine/blob/master/shell/platform/android/io/flutter/embedding/engine/FlutterEngineGroup.java)
## Important Files
### iOS
- [DataModel.swift](./multiple_flutters_ios/MultipleFluttersIos/HostViewController.swift)
— Observable data model that is the source of truth for the host platform and Flutter.
- [HostViewController.swift](./multiple_flutters_ios/MultipleFluttersIos/HostViewController.swift)
— A UIViewController that synchronizes to the DataModel.
- [main.dart](./multiple_flutters_module/lib/main.dart) — The Flutter source
code.
- [SingleFlutterViewController.swift](./multiple_flutters_ios/MultipleFluttersIos/SingleFlutterViewController.swift)
— A subclass of FlutterViewController that synchronizes with the DataModel.
- [DoubleFlutterViewController.swift](./multiple_flutters_ios/MultipleFluttersIos/DoubleFlutterViewController.swift)
— A UIViewController that embeds multiple Flutter instances.
### Android
## Data Synchronization Description
This sample code performs data synchronization to share data between the host
platform and multiple instances of Flutter by combining the
[Observer design pattern](https://en.wikipedia.org/wiki/Observer_pattern) and
[Flutter platform channels](https://flutter.dev/docs/development/platform-integration/platform-channels).
Here is how it works:
- The definitive source of truth for the data lives in the host platform data
model.
- Every host view displaying Flutter content maintains: a Flutter engine, a
bidirectional platform channel, and a subscription to the host data model.
- Flutter instances maintain a copy of the data they are interested in reading,
and this data is seeded by the host when the instance is first displayed.
- Mutations from Flutter code are sent to the host platform via the channel. The
host platform performs the mutations, and then notifies all host view
controllers and Flutter engines of the new value.
- Mutations from host code happen directly on the data model who notifies host
view controllers and Flutter engines of the new value.
This is just one possible way to synchronize the data between the host platform
and multiple Flutter instances. A more complete implementation is proposed in
the work of
[flutter/issues/72030](https://github.com/flutter/flutter/issues/72030).

View File

@@ -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

View File

@@ -0,0 +1,17 @@
# multiple_flutters_android
This is an add-to-app sample that uses the Flutter engine group API to host
multiple instances of Flutter in the app.
## Getting Started
```sh
cd ../multiple_flutters_module
flutter pub get
cd -
open -a "Android Studio" multiple_flutters_android/ # macOS command
# (build and run)
```
For more information see
[multiple_flutters_module](../multiple_flutters_module/README.md).

View File

@@ -0,0 +1 @@
/build

View File

@@ -0,0 +1,51 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
}
android {
signingConfigs {
self {
}
}
compileSdk 36
defaultConfig {
applicationId "dev.flutter.multipleflutters"
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'
signingConfig debug.signingConfig
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
namespace 'dev.flutter.multipleflutters'
}
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'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
implementation project(':flutter')
}

View File

@@ -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

View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Provide required visibility configuration for API level 30 and above -->
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>
</queries>
<application
android:name=".App"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MultipleFlutters">
<activity
android:name=".MainActivity"
android:exported="true"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".SingleFlutterActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"
android:exported="true"
/>
<activity
android:name=".DoubleFlutterActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"
android:exported="true"
/>
<!-- Whether leave or clean up the VM after the last shell shuts down. If your want to let
your app destroy the last shell and re-create shells more quickly, set it to true,
otherwise if you want to clean up the memory of the leak VM, set it to false.
(This setting works after the Flutter 2.10 stable version.)
View https://github.com/flutter/flutter/issues/95903 for more detail. -->
<meta-data
android:name="io.flutter.embedding.android.LeakVM"
android:value="false"
/>
</application>
</manifest>

View File

@@ -0,0 +1,18 @@
package dev.flutter.multipleflutters
import android.app.Application
import io.flutter.embedding.engine.FlutterEngineGroup
/**
* Application class for this app.
*
* This holds onto our engine group.
*/
class App : Application() {
lateinit var engines: FlutterEngineGroup
override fun onCreate() {
super.onCreate()
engines = FlutterEngineGroup(this)
}
}

View File

@@ -0,0 +1,41 @@
package dev.flutter.multipleflutters
import java.lang.ref.WeakReference
/**
* Interface for getting notifications when the DataModel is updated.
*/
interface DataModelObserver {
fun onCountUpdate(newCount: Int)
}
/**
* A singleton/observable data model for the data shared between Flutter and the host platform.
*
* This is the definitive source of truth for all data.
*/
class DataModel {
companion object {
val instance = DataModel()
}
private val observers = mutableListOf<WeakReference<DataModelObserver>>()
public var counter = 0
set(value) {
field = value
for (observer in observers) {
observer.get()?.onCountUpdate(value)
}
}
fun addObserver(observer: DataModelObserver) {
observers.add(WeakReference(observer))
}
fun removeObserver(observer: DataModelObserver) {
observers.removeIf {
if (it.get() != null) it.get() == observer else true
}
}
}

View File

@@ -0,0 +1,91 @@
package dev.flutter.multipleflutters
import android.content.Intent
import android.os.Bundle
import android.widget.FrameLayout
import android.widget.LinearLayout
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
import io.flutter.embedding.android.FlutterFragment
import io.flutter.embedding.engine.FlutterEngineCache
/**
* An activity that displays 2 FlutterFragments vertically.
*/
class DoubleFlutterActivity : FragmentActivity(), EngineBindingsDelegate {
private val topBindings: EngineBindings by lazy {
EngineBindings(activity = this, delegate = this, entrypoint = "topMain")
}
private val bottomBindings: EngineBindings by lazy {
EngineBindings(activity = this, delegate = this, entrypoint = "bottomMain")
}
private val numberOfFlutters = 2
private val engineCountStart : Int
private companion object {
var engineCounter = 0
}
init {
engineCountStart = engineCounter
engineCounter += numberOfFlutters
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val root = LinearLayout(this)
root.layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT
)
root.orientation = LinearLayout.VERTICAL
root.weightSum = numberOfFlutters.toFloat()
val fragmentManager: FragmentManager = supportFragmentManager
setContentView(root)
for (i in 0 until numberOfFlutters) {
val engineId = engineCountStart + i
val containerId = 12345 + engineId
val flutterContainer = FrameLayout(this)
root.addView(flutterContainer)
flutterContainer.id = containerId
flutterContainer.layoutParams = LinearLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT,
1.0f
)
val engine = if (i == 0) topBindings.engine else bottomBindings.engine
FlutterEngineCache.getInstance().put(engineId.toString(), engine)
val flutterFragment =
FlutterFragment.withCachedEngine(engineId.toString()).build<FlutterFragment>()
fragmentManager
.beginTransaction()
.add(
containerId,
flutterFragment
)
.commit()
}
topBindings.attach()
bottomBindings.attach()
}
override fun onDestroy() {
for (i in 0 until numberOfFlutters) {
val engineId = engineCountStart + i
FlutterEngineCache.getInstance().remove(engineId.toString())
}
super.onDestroy()
bottomBindings.detach()
topBindings.detach()
}
override fun onNext() {
val flutterIntent = Intent(this, MainActivity::class.java)
startActivity(flutterIntent)
}
}

View File

@@ -0,0 +1,85 @@
package dev.flutter.multipleflutters
import android.app.Activity
import io.flutter.FlutterInjector
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.dart.DartExecutor
import io.flutter.plugin.common.MethodChannel
/**
* This interface represents the notifications an EngineBindings may be receiving from the Flutter
* instance.
*
* What methods this interface has depends on the messages that are sent over the EngineBinding's
* channel in `main.dart`. Messages that interact with the DataModel are handled automatically
* by the EngineBindings.
*
* @see main.dart for what messages are getting sent from Flutter.
*/
interface EngineBindingsDelegate {
fun onNext()
}
/**
* This binds a FlutterEngine instance with the DataModel and a channel for communicating with that
* engine.
*
* Messages involving the DataModel are handled by the EngineBindings, other messages are forwarded
* to the EngineBindingsDelegate.
*
* @see main.dart for what messages are getting sent from Flutter.
*/
class EngineBindings(activity: Activity, delegate: EngineBindingsDelegate, entrypoint: String) :
DataModelObserver {
val channel: MethodChannel
val engine: FlutterEngine
val delegate: EngineBindingsDelegate
init {
val app = activity.applicationContext as App
// This has to be lazy to avoid creation before the FlutterEngineGroup.
val dartEntrypoint =
DartExecutor.DartEntrypoint(
FlutterInjector.instance().flutterLoader().findAppBundlePath(), entrypoint
)
engine = app.engines.createAndRunEngine(activity, dartEntrypoint)
this.delegate = delegate
channel = MethodChannel(engine.dartExecutor.binaryMessenger, "multiple-flutters")
}
/**
* This setups the messaging connections on the platform channel and the DataModel.
*/
fun attach() {
DataModel.instance.addObserver(this)
channel.invokeMethod("setCount", DataModel.instance.counter)
channel.setMethodCallHandler { call, result ->
when (call.method) {
"incrementCount" -> {
DataModel.instance.counter = DataModel.instance.counter + 1
result.success(null)
}
"next" -> {
this.delegate.onNext()
result.success(null)
}
else -> {
result.notImplemented()
}
}
}
}
/**
* This tears down the messaging connections on the platform channel and the DataModel.
*/
fun detach() {
engine.destroy();
DataModel.instance.removeObserver(this)
channel.setMethodCallHandler(null)
}
override fun onCountUpdate(newCount: Int) {
channel.invokeMethod("setCount", newCount)
}
}

View File

@@ -0,0 +1,61 @@
package dev.flutter.multipleflutters
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.TextView
/**
* The activity that uses standard platform UI APIs.
*
* This doesn't talk directly to or render Flutter content and represents a part of your app that
* would potentially already be written that wants to interface with Flutter.
*/
class MainActivity : AppCompatActivity(), DataModelObserver {
private lateinit var countView: TextView
private val mainActivityIdentifier: Int
private companion object {
/** A count that makes every other MainActivity have 1 or 2 Flutter instances. */
var mainActivityCount = 0
}
init {
mainActivityIdentifier = mainActivityCount
mainActivityCount += 1
}
/** Implemented from AppCompactActivity. */
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
DataModel.instance.addObserver(this)
countView = findViewById(R.id.count)
countView.text = DataModel.instance.counter.toString()
}
/** Implemented from AppCompactActivity. */
override fun onDestroy() {
super.onDestroy()
DataModel.instance.removeObserver(this)
}
/** Event from `activity_main.xml`. */
fun onClickNext(view: View) {
val nextClass =
if (mainActivityIdentifier % 2 == 0) SingleFlutterActivity::class.java else DoubleFlutterActivity::class.java
val flutterIntent = Intent(this, nextClass)
startActivity(flutterIntent)
}
/** Event from `activity_main.xml`. */
fun onClickAdd(view: View) {
DataModel.instance.counter = DataModel.instance.counter + 1
}
/** Implemented from DataModelObserver. */
override fun onCountUpdate(newCount: Int) {
countView.text = newCount.toString()
}
}

View File

@@ -0,0 +1,37 @@
package dev.flutter.multipleflutters
import android.content.Context
import android.content.Intent
import android.os.Bundle
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
/**
* This is an Activity that displays one instance of Flutter.
*
* EngineBindings is used to bridge communication between the Flutter instance and the DataModel.
*/
class SingleFlutterActivity : FlutterActivity(), EngineBindingsDelegate {
private val engineBindings: EngineBindings by lazy {
EngineBindings(activity = this, delegate = this, entrypoint = "main")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
engineBindings.attach()
}
override fun onDestroy() {
super.onDestroy()
engineBindings.detach()
}
override fun provideFlutterEngine(context: Context): FlutterEngine? {
return engineBindings.engine
}
override fun onNext() {
val flutterIntent = Intent(this, MainActivity::class.java)
startActivity(flutterIntent)
}
}

View File

@@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View File

@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="1dp"
android:layout_marginEnd="260dp"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toEndOf="@+id/countLabel"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/countLabel"
android:layout_width="58dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:text="Count:"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:onClick="onClickNext"
android:text="Next"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:onClick="onClickAdd"
android:text="Add"
app:layout_constraintBottom_toTopOf="@+id/button"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,16 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.MultipleFlutters" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_200</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/black</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_200</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
</resources>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
</resources>

View File

@@ -0,0 +1,3 @@
<resources>
<string name="app_name">Multiple Flutters</string>
</resources>

View File

@@ -0,0 +1,16 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.MultipleFlutters" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_500</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_700</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
</resources>

View File

@@ -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.0'
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
}

View File

@@ -0,0 +1,24 @@
# 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
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
android.defaults.buildfeatures.buildconfig=true
android.nonTransitiveRClass=false
android.nonFinalResIds=false

View File

@@ -0,0 +1,6 @@
#Mon Dec 11 09:57:20 AEDT 2023
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

View File

@@ -0,0 +1,234 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${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='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# 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 ;; #(
MSYS* | 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" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

View File

@@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@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 Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@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="-Xmx64m" "-Xms64m"
@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 execute
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 execute
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
: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 %*
: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

View File

@@ -0,0 +1,7 @@
include ':app'
rootProject.name = "Multiple Flutters"
setBinding(new Binding([gradle: this])) // new
evaluate(new File( // new
settingsDir.parentFile, // new
'./multiple_flutters_module/.android/include_flutter.groovy' // new
))

View File

@@ -0,0 +1,6 @@
MultipleFluttersIos.xcworkspace/xcuserdata
MultipleFluttersIos.xcodeproj/project.xcworkspace/xcuserdata
Pods/Pods.xcodeproj/xcuserdata
MultipleFluttersIos.xcodeproj/xcuserdata
Pods/
MultipleFluttersIos.xcodeproj/xcshareddata/

View File

@@ -0,0 +1,437 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 51;
objects = {
/* Begin PBXBuildFile section */
0D61B1E225BB8A2300A1A590 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D61B1E125BB8A2300A1A590 /* AppDelegate.swift */; };
0D61B1E425BB8A2300A1A590 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D61B1E325BB8A2300A1A590 /* SceneDelegate.swift */; };
0D61B1E625BB8A2300A1A590 /* HostViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D61B1E525BB8A2300A1A590 /* HostViewController.swift */; };
0D61B1E925BB8A2300A1A590 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0D61B1E725BB8A2300A1A590 /* Main.storyboard */; };
0D61B1EB25BB8A2400A1A590 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0D61B1EA25BB8A2400A1A590 /* Assets.xcassets */; };
0D61B1EE25BB8A2400A1A590 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0D61B1EC25BB8A2400A1A590 /* LaunchScreen.storyboard */; };
0D61B20125BB8FDF00A1A590 /* DataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D61B20025BB8FDF00A1A590 /* DataModel.swift */; };
0D61B20825BBEA9E00A1A590 /* SingleFlutterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D61B20725BBEA9E00A1A590 /* SingleFlutterViewController.swift */; };
0D61B20B25BBF88700A1A590 /* DoubleFlutterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D61B20A25BBF88700A1A590 /* DoubleFlutterViewController.swift */; };
DA84F1C17CFA00606F7B401C /* Pods_MultipleFluttersIos.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4E03DC90CED28B2143B2CFB1 /* Pods_MultipleFluttersIos.framework */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
0D61B1DE25BB8A2300A1A590 /* MultipleFluttersIos.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MultipleFluttersIos.app; sourceTree = BUILT_PRODUCTS_DIR; };
0D61B1E125BB8A2300A1A590 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
0D61B1E325BB8A2300A1A590 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
0D61B1E525BB8A2300A1A590 /* HostViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostViewController.swift; sourceTree = "<group>"; };
0D61B1E825BB8A2300A1A590 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
0D61B1EA25BB8A2400A1A590 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
0D61B1ED25BB8A2400A1A590 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
0D61B1EF25BB8A2400A1A590 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
0D61B20025BB8FDF00A1A590 /* DataModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataModel.swift; sourceTree = "<group>"; };
0D61B20725BBEA9E00A1A590 /* SingleFlutterViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleFlutterViewController.swift; sourceTree = "<group>"; };
0D61B20A25BBF88700A1A590 /* DoubleFlutterViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DoubleFlutterViewController.swift; sourceTree = "<group>"; };
4E03DC90CED28B2143B2CFB1 /* Pods_MultipleFluttersIos.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MultipleFluttersIos.framework; sourceTree = BUILT_PRODUCTS_DIR; };
4F02FEA4EA8C75406BC5FFC5 /* Pods-MultipleFluttersIos.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MultipleFluttersIos.release.xcconfig"; path = "Target Support Files/Pods-MultipleFluttersIos/Pods-MultipleFluttersIos.release.xcconfig"; sourceTree = "<group>"; };
5EE6997543EAE7F85C674231 /* Pods-MultipleFluttersIos.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MultipleFluttersIos.debug.xcconfig"; path = "Target Support Files/Pods-MultipleFluttersIos/Pods-MultipleFluttersIos.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
0D61B1DB25BB8A2300A1A590 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
DA84F1C17CFA00606F7B401C /* Pods_MultipleFluttersIos.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
0D61B1D525BB8A2300A1A590 = {
isa = PBXGroup;
children = (
0D61B1E025BB8A2300A1A590 /* MultipleFluttersIos */,
0D61B1DF25BB8A2300A1A590 /* Products */,
2AB119CC8909334B007F8D1F /* Pods */,
A2F4F0BD46437369E87BA62F /* Frameworks */,
);
sourceTree = "<group>";
};
0D61B1DF25BB8A2300A1A590 /* Products */ = {
isa = PBXGroup;
children = (
0D61B1DE25BB8A2300A1A590 /* MultipleFluttersIos.app */,
);
name = Products;
sourceTree = "<group>";
};
0D61B1E025BB8A2300A1A590 /* MultipleFluttersIos */ = {
isa = PBXGroup;
children = (
0D61B1E125BB8A2300A1A590 /* AppDelegate.swift */,
0D61B1E325BB8A2300A1A590 /* SceneDelegate.swift */,
0D61B1E525BB8A2300A1A590 /* HostViewController.swift */,
0D61B20025BB8FDF00A1A590 /* DataModel.swift */,
0D61B1E725BB8A2300A1A590 /* Main.storyboard */,
0D61B1EA25BB8A2400A1A590 /* Assets.xcassets */,
0D61B1EC25BB8A2400A1A590 /* LaunchScreen.storyboard */,
0D61B1EF25BB8A2400A1A590 /* Info.plist */,
0D61B20725BBEA9E00A1A590 /* SingleFlutterViewController.swift */,
0D61B20A25BBF88700A1A590 /* DoubleFlutterViewController.swift */,
);
path = MultipleFluttersIos;
sourceTree = "<group>";
};
2AB119CC8909334B007F8D1F /* Pods */ = {
isa = PBXGroup;
children = (
5EE6997543EAE7F85C674231 /* Pods-MultipleFluttersIos.debug.xcconfig */,
4F02FEA4EA8C75406BC5FFC5 /* Pods-MultipleFluttersIos.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
A2F4F0BD46437369E87BA62F /* Frameworks */ = {
isa = PBXGroup;
children = (
4E03DC90CED28B2143B2CFB1 /* Pods_MultipleFluttersIos.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
0D61B1DD25BB8A2300A1A590 /* MultipleFluttersIos */ = {
isa = PBXNativeTarget;
buildConfigurationList = 0D61B1F225BB8A2400A1A590 /* Build configuration list for PBXNativeTarget "MultipleFluttersIos" */;
buildPhases = (
3BC074A3AF94322EF6E73F8B /* [CP] Check Pods Manifest.lock */,
9A714FC9CCEF3D4EFCAF0E2F /* [CP-User] Run Flutter Build multiple_flutters_module Script */,
0D61B1DA25BB8A2300A1A590 /* Sources */,
0D61B1DB25BB8A2300A1A590 /* Frameworks */,
0D61B1DC25BB8A2300A1A590 /* Resources */,
D93C82A75960A3ABB9D69453 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = MultipleFluttersIos;
productName = MultipleFluttersIos;
productReference = 0D61B1DE25BB8A2300A1A590 /* MultipleFluttersIos.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
0D61B1D625BB8A2300A1A590 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1220;
LastUpgradeCheck = 1220;
TargetAttributes = {
0D61B1DD25BB8A2300A1A590 = {
CreatedOnToolsVersion = 12.2;
};
};
};
buildConfigurationList = 0D61B1D925BB8A2300A1A590 /* Build configuration list for PBXProject "MultipleFluttersIos" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 0D61B1D525BB8A2300A1A590;
productRefGroup = 0D61B1DF25BB8A2300A1A590 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
0D61B1DD25BB8A2300A1A590 /* MultipleFluttersIos */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
0D61B1DC25BB8A2300A1A590 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0D61B1EE25BB8A2400A1A590 /* LaunchScreen.storyboard in Resources */,
0D61B1EB25BB8A2400A1A590 /* Assets.xcassets in Resources */,
0D61B1E925BB8A2300A1A590 /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3BC074A3AF94322EF6E73F8B /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-MultipleFluttersIos-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
9A714FC9CCEF3D4EFCAF0E2F /* [CP-User] Run Flutter Build multiple_flutters_module Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
name = "[CP-User] Run Flutter Build multiple_flutters_module Script";
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "set -e\nset -u\nsource \"${SRCROOT}/../multiple_flutters_module/.ios/Flutter/flutter_export_environment.sh\"\nexport VERBOSE_SCRIPT_LOGGING=1 && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/xcode_backend.sh build";
};
D93C82A75960A3ABB9D69453 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-MultipleFluttersIos/Pods-MultipleFluttersIos-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-MultipleFluttersIos/Pods-MultipleFluttersIos-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-MultipleFluttersIos/Pods-MultipleFluttersIos-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
0D61B1DA25BB8A2300A1A590 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0D61B20B25BBF88700A1A590 /* DoubleFlutterViewController.swift in Sources */,
0D61B1E625BB8A2300A1A590 /* HostViewController.swift in Sources */,
0D61B20125BB8FDF00A1A590 /* DataModel.swift in Sources */,
0D61B1E225BB8A2300A1A590 /* AppDelegate.swift in Sources */,
0D61B1E425BB8A2300A1A590 /* SceneDelegate.swift in Sources */,
0D61B20825BBEA9E00A1A590 /* SingleFlutterViewController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
0D61B1E725BB8A2300A1A590 /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
0D61B1E825BB8A2300A1A590 /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
0D61B1EC25BB8A2400A1A590 /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
0D61B1ED25BB8A2400A1A590 /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
0D61B1F025BB8A2400A1A590 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 14.2;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
0D61B1F125BB8A2400A1A590 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 14.2;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
0D61B1F325BB8A2400A1A590 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 5EE6997543EAE7F85C674231 /* Pods-MultipleFluttersIos.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
INFOPLIST_FILE = MultipleFluttersIos/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.MultipleFluttersIos;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
0D61B1F425BB8A2400A1A590 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 4F02FEA4EA8C75406BC5FFC5 /* Pods-MultipleFluttersIos.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
INFOPLIST_FILE = MultipleFluttersIos/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.MultipleFluttersIos;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
0D61B1D925BB8A2300A1A590 /* Build configuration list for PBXProject "MultipleFluttersIos" */ = {
isa = XCConfigurationList;
buildConfigurations = (
0D61B1F025BB8A2400A1A590 /* Debug */,
0D61B1F125BB8A2400A1A590 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
0D61B1F225BB8A2400A1A590 /* Build configuration list for PBXNativeTarget "MultipleFluttersIos" */ = {
isa = XCConfigurationList;
buildConfigurations = (
0D61B1F325BB8A2400A1A590 /* Debug */,
0D61B1F425BB8A2400A1A590 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 0D61B1D625BB8A2300A1A590 /* Project object */;
}

View File

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

View File

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

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:MultipleFluttersIos.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

View File

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

View File

@@ -0,0 +1,47 @@
// Copyright 2021 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 Flutter
import UIKit
/// The app's UIApplicationDelegate.
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
let engines = FlutterEngineGroup(name: "multiple-flutters", project: nil)
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
#if DEBUG
let isDebug = true
#else
let isDebug = false
#endif
if isDebug {
NSLog(
"📣 NOTICE: the memory and CPU costs for Flutter engine groups are significantly greater in debug builds. See also: https://github.com/dart-lang/sdk/issues/36097"
)
} else {
NSLog(
"📣 NOTICE: the memory and CPU costs for Flutter engine groups are significantly less here than in debug builds. See also: https://github.com/dart-lang/sdk/issues/36097"
)
}
return true
}
func application(
_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession,
options: UIScene.ConnectionOptions
) -> UISceneConfiguration {
return UISceneConfiguration(
name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(
_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>
) {
}
}

View File

@@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

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

View File

@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>

View File

@@ -0,0 +1,116 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17506" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="zV5-X7-f4o">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17505"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Navigation Controller-->
<scene sceneID="iJJ-8n-QPC">
<objects>
<navigationController id="zV5-X7-f4o" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" id="NiM-Jj-k9k">
<rect key="frame" x="0.0" y="44" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<connections>
<segue destination="BYZ-38-t0r" kind="relationship" relationship="rootViewController" id="PBq-fn-vqE"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="22r-TG-MDZ" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-1555" y="-191"/>
</scene>
<!--Host View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController storyboardIdentifier="NativeViewCount" id="BYZ-38-t0r" customClass="HostViewController" customModule="MultipleFluttersIos" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Count:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="CgZ-JE-gda">
<rect key="frame" x="20" y="437.5" width="51" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="1234" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dKf-iz-CSn">
<rect key="frame" x="79" y="437.5" width="39" height="21"/>
<constraints>
<constraint firstAttribute="width" constant="39" id="Aw7-RN-dwx"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="vVY-o2-UDW">
<rect key="frame" x="350" y="818" width="44" height="44"/>
<constraints>
<constraint firstAttribute="height" constant="44" id="h7j-l3-taD"/>
<constraint firstAttribute="width" constant="44" id="sQd-lU-0zD"/>
</constraints>
<state key="normal" title="Next"/>
<connections>
<action selector="onNext" destination="BYZ-38-t0r" eventType="touchUpInside" id="XN9-kg-pAi"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ohd-Fe-iAE">
<rect key="frame" x="350" y="772" width="44" height="44"/>
<constraints>
<constraint firstAttribute="height" constant="44" id="8Nc-hA-ZbV"/>
<constraint firstAttribute="width" constant="44" id="P0Q-km-hJ3"/>
</constraints>
<state key="normal" title="Add"/>
<connections>
<action selector="onAddCount" destination="BYZ-38-t0r" eventType="touchUpInside" id="QYB-J4-vI2"/>
</connections>
</button>
</subviews>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="CgZ-JE-gda" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" id="1Dz-Aq-10E"/>
<constraint firstItem="vVY-o2-UDW" firstAttribute="top" secondItem="Ohd-Fe-iAE" secondAttribute="bottom" constant="2" id="EqG-G1-3gr"/>
<constraint firstItem="vVY-o2-UDW" firstAttribute="bottom" secondItem="6Tk-OE-BBY" secondAttribute="bottom" id="IDv-xt-QVd"/>
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="vVY-o2-UDW" secondAttribute="trailing" constant="20" id="IQ1-E7-q9T"/>
<constraint firstItem="dKf-iz-CSn" firstAttribute="leading" secondItem="CgZ-JE-gda" secondAttribute="trailing" constant="8" symbolic="YES" id="ZKK-wt-eqr"/>
<constraint firstItem="dKf-iz-CSn" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" id="hUq-Je-pdK"/>
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="Ohd-Fe-iAE" secondAttribute="trailing" constant="20" id="wT9-j5-NMO"/>
<constraint firstItem="CgZ-JE-gda" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="20" id="ybv-NA-2by"/>
</constraints>
</view>
<navigationItem key="navigationItem" id="NPA-Os-dNw"/>
<connections>
<outlet property="countView" destination="dKf-iz-CSn" id="9dM-Hg-UMg"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-722" y="-192"/>
</scene>
<!--View Controller-->
<scene sceneID="e8d-wy-DfZ">
<objects>
<viewController id="EXG-G5-awH" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="3av-sG-Ibb">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<viewLayoutGuide key="safeArea" id="R4s-uY-wnW"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="xYB-oQ-Y1A" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="4" y="-192"/>
</scene>
</scenes>
<resources>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>

View File

@@ -0,0 +1,56 @@
// Copyright 2021 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 Foundation
/// A protocol that receives updates when the datamodel is changed.
protocol DataModelObserver: AnyObject {
func onCountUpdate(newCount: Int64)
}
/// A wrapper object around a weak reference to an object that implements DataModelObserver.
///
/// This is required since you can't directly hold weak references to protocols in data structures.
private struct DataModelObserverWrapper {
weak var wrapped: DataModelObserver?
init(_ wrapped: DataModelObserver) {
self.wrapped = wrapped
}
}
/// A singleton data model that is observable.
class DataModel {
private var _count: Int64 = 0
var count: Int64 {
get {
return self._count
}
set {
self._count = newValue
for observer in observers {
if let wrapped = observer.wrapped {
wrapped.onCountUpdate(newCount: self._count)
}
}
}
}
private var observers: [DataModelObserverWrapper] = []
static let shared = DataModel()
func addObserver(observer: DataModelObserver) {
observers.append(DataModelObserverWrapper(observer))
}
func removeObserver(observer: DataModelObserver) {
observers.removeAll { (element: DataModelObserverWrapper) -> Bool in
if let wrapped = element.wrapped {
return wrapped === observer
} else {
// Handle observers who dealloc'd without removing themselves.
return true
}
}
}
}

View File

@@ -0,0 +1,29 @@
// Copyright 2021 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 UIKit
/// A UIViewController that hosts two instances of SingleFlutterViewController, splitting the screen
/// between them.
class DoubleFlutterViewController: UIViewController {
private let topFlutter: SingleFlutterViewController = SingleFlutterViewController(
withEntrypoint: "topMain")
private let bottomFlutter: SingleFlutterViewController = SingleFlutterViewController(
withEntrypoint: "bottomMain")
override func viewDidLoad() {
addChild(topFlutter)
addChild(bottomFlutter)
let safeFrame = self.view.safeAreaLayoutGuide.layoutFrame
let halfHeight = safeFrame.height / 2.0
topFlutter.view.frame = CGRect(
x: safeFrame.minX, y: safeFrame.minY, width: safeFrame.width, height: halfHeight)
bottomFlutter.view.frame = CGRect(
x: safeFrame.minX, y: topFlutter.view.frame.maxY, width: safeFrame.width, height: halfHeight)
self.view.addSubview(topFlutter.view)
self.view.addSubview(bottomFlutter.view)
topFlutter.didMove(toParent: self)
bottomFlutter.didMove(toParent: self)
}
}

View File

@@ -0,0 +1,43 @@
// Copyright 2021 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 Flutter
import UIKit
/// UIViewController associated with the NativeViewCount storyboard scene.
class HostViewController: UIViewController, DataModelObserver {
@IBOutlet weak var countView: UILabel!
deinit {
DataModel.shared.removeObserver(observer: self)
}
func onCountUpdate(newCount: Int64) {
self.countView.text = String(format: "%d", newCount)
}
override func viewDidLoad() {
super.viewDidLoad()
DataModel.shared.addObserver(observer: self)
onCountUpdate(newCount: DataModel.shared.count)
}
@IBAction func onAddCount() {
DataModel.shared.count = DataModel.shared.count + 1
}
@IBAction func onNext() {
let navController = self.navigationController!
// Based on the number of view controllers in the stack we push a
// SingleFlutterViewController or DoubleFlutterViewController to alternatve
// between the two.
if navController.viewControllers.count % 4 == 1 {
let vc = SingleFlutterViewController(withEntrypoint: nil)
navController.pushViewController(vc, animated: true)
} else {
let vc = DoubleFlutterViewController()
navController.pushViewController(vc, animated: true)
}
}
}

View File

@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

View File

@@ -0,0 +1,32 @@
// Copyright 2021 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 UIKit
/// The UIWindowSceneDelegate for the app.
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(
_ scene: UIScene, willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
guard let _ = (scene as? UIWindowScene) else { return }
}
func sceneDidDisconnect(_ scene: UIScene) {
}
func sceneDidBecomeActive(_ scene: UIScene) {
}
func sceneWillResignActive(_ scene: UIScene) {
}
func sceneWillEnterForeground(_ scene: UIScene) {
}
func sceneDidEnterBackground(_ scene: UIScene) {
}
}

View File

@@ -0,0 +1,59 @@
// Copyright 2021 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 Flutter
import FlutterPluginRegistrant
import Foundation
/// A FlutterViewController intended for the MyApp widget in the Flutter module.
///
/// This view controller maintains a connection to the Flutter instance and syncs it with the
/// datamodel. In practice you should override the other init methods or switch to composition
/// instead of inheritence.
class SingleFlutterViewController: FlutterViewController, DataModelObserver {
private var channel: FlutterMethodChannel?
init(withEntrypoint entryPoint: String?) {
let appDelegate: AppDelegate = UIApplication.shared.delegate as! AppDelegate
let newEngine = appDelegate.engines.makeEngine(withEntrypoint: entryPoint, libraryURI: nil)
GeneratedPluginRegistrant.register(with: newEngine)
super.init(engine: newEngine, nibName: nil, bundle: nil)
DataModel.shared.addObserver(observer: self)
}
deinit {
DataModel.shared.removeObserver(observer: self)
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func onCountUpdate(newCount: Int64) {
if let channel = channel {
channel.invokeMethod("setCount", arguments: newCount)
}
}
override func viewDidLoad() {
super.viewDidLoad()
channel = FlutterMethodChannel(
name: "multiple-flutters", binaryMessenger: self.engine!.binaryMessenger)
channel!.invokeMethod("setCount", arguments: DataModel.shared.count)
let navController = self.navigationController!
channel!.setMethodCallHandler { (call: FlutterMethodCall, result: @escaping FlutterResult) in
if call.method == "incrementCount" {
DataModel.shared.count = DataModel.shared.count + 1
result(nil)
} else if call.method == "next" {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "NativeViewCount")
navController.pushViewController(vc, animated: true)
result(nil)
} else {
result(FlutterMethodNotImplemented)
}
}
}
}

View File

@@ -0,0 +1,15 @@
# Copyright 2021 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.
flutter_application_path = '../multiple_flutters_module'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
target 'MultipleFluttersIos' do
use_frameworks!
install_all_flutter_pods(flutter_application_path)
end
post_install do |installer|
flutter_post_install(installer) if defined?(flutter_post_install)
end

View File

@@ -0,0 +1,17 @@
# multiple_flutters_ios
This is an add-to-app sample that uses the Flutter Engine Group API to host
multiple instances of Flutter in an iOS app.
## Getting Started
```sh
cd ../multiple_flutters_module
flutter pub get
cd -
pod install
open MultipleFluttersIos.xcworkspace
# (build and run)
```
For more information see: [multiple_flutters/README.md](../README.md)

View File

@@ -0,0 +1,48 @@
.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
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json

View File

@@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: 1d3f6971600f6e3fb144a30fab2b889e34af0c22
channel: master
project_type: module

View File

@@ -0,0 +1,5 @@
# multiple_flutters_module
This is the Flutter module that is embedded in the `multiple_flutters` projects.
See also: [multiple_flutters/README.md](../README.md)

View File

@@ -0,0 +1 @@
include: ../../../analysis_options.yaml

View File

@@ -0,0 +1,111 @@
// Copyright 2021 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:url_launcher/url_launcher.dart' as launcher;
void main() => runApp(const MyApp(color: Colors.blue));
@pragma('vm:entry-point')
void topMain() => runApp(const MyApp(color: Colors.green));
@pragma('vm:entry-point')
void bottomMain() => runApp(const MyApp(color: Colors.purple));
class MyApp extends StatelessWidget {
const MyApp({super.key, required this.color});
final MaterialColor color;
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorSchemeSeed: color,
appBarTheme: AppBarTheme(
backgroundColor: color,
foregroundColor: Colors.white,
elevation: 8,
),
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int? _counter = 0;
late MethodChannel _channel;
@override
void initState() {
super.initState();
_channel = const MethodChannel('multiple-flutters');
_channel.setMethodCallHandler((call) async {
if (call.method == "setCount") {
// A notification that the host platform's data model has been updated.
setState(() {
_counter = call.arguments as int?;
});
} else {
throw Exception('not implemented ${call.method}');
}
});
}
void _incrementCounter() {
// Mutations to the data model are forwarded to the host platform.
_channel.invokeMethod<void>("incrementCount", _counter);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(widget.title)),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('You have pushed the button this many times:'),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
TextButton(
onPressed: _incrementCounter,
child: const Text('Add'),
),
TextButton(
onPressed: () {
_channel.invokeMethod<void>("next", _counter);
},
child: const Text('Next'),
),
ElevatedButton(
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'),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,28 @@
name: multiple_flutters_module
description: A module that is embedded in the multiple_flutters_ios and multiple_flutters_android sample code.
version: 1.0.0+1
resolution: workspace
environment:
sdk: ^3.9.0-0
dependencies:
flutter:
sdk: flutter
url_launcher: ^6.0.6
cupertino_icons: ^1.0.0
dev_dependencies:
analysis_defaults:
path: ../../../analysis_defaults
flutter_test:
sdk: flutter
flutter:
uses-material-design: true
module:
androidX: true
androidPackage: com.example.multiple_flutters_module
iosBundleIdentifier: com.example.multipleFluttersModule