將 Flutter Fragment 新增至 Android 應用程式
本指南說明如何將 Flutter Fragment
新增至現有的 Android 應用程式。在 Android 中,Fragment
代表較大 UI 的模組化部分。Fragment
可能會用於呈現滑動式抽屜、索引標籤內容、ViewPager
中的頁面,或者它可能只是代表單一 Activity
應用程式中的正常畫面。Flutter 提供 FlutterFragment
,以便開發人員可以在可以使用一般 Fragment
的任何地方呈現 Flutter 體驗。
如果 Activity
同樣適用於您的應用程式需求,請考慮使用 FlutterActivity
而不是 FlutterFragment
,它更快且更容易使用。
FlutterFragment
允許開發人員控制 Fragment
中 Flutter 體驗的以下詳細資訊
- 初始 Flutter 路由
- 要執行的 Dart 進入點
- 不透明與半透明背景
FlutterFragment
是否應控制其周圍的Activity
- 是否應使用新的
FlutterEngine
或快取的FlutterEngine
FlutterFragment
還附帶許多必須從其周圍的 Activity
轉發的呼叫。這些呼叫允許 Flutter 適當地對 OS 事件做出反應。
本指南中將說明所有種類的 FlutterFragment
及其需求。
使用新的 FlutterEngine
將 FlutterFragment
新增至 Activity
#使用 FlutterFragment
的第一件事是將其新增至主機 Activity
。
若要將 FlutterFragment
新增至主機 Activity
,請在 Activity
內的 onCreate()
中或在適合您應用程式的其他時間,建立並附加 FlutterFragment
的實例。
class MyActivity : FragmentActivity() {
companion object {
// Define a tag String to represent the FlutterFragment within this
// Activity's FragmentManager. This value can be whatever you'd like.
private const val TAG_FLUTTER_FRAGMENT = "flutter_fragment"
}
// Declare a local variable to reference the FlutterFragment so that you
// can forward calls to it later.
private var flutterFragment: FlutterFragment? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Inflate a layout that has a container for your FlutterFragment. For
// this example, assume that a FrameLayout exists with an ID of
// R.id.fragment_container.
setContentView(R.layout.my_activity_layout)
// Get a reference to the Activity's FragmentManager to add a new
// FlutterFragment, or find an existing one.
val fragmentManager: FragmentManager = supportFragmentManager
// Attempt to find an existing FlutterFragment, in case this is not the
// first time that onCreate() was run.
flutterFragment = fragmentManager
.findFragmentByTag(TAG_FLUTTER_FRAGMENT) as FlutterFragment?
// Create and attach a FlutterFragment if one does not exist.
if (flutterFragment == null) {
var newFlutterFragment = FlutterFragment.createDefault()
flutterFragment = newFlutterFragment
fragmentManager
.beginTransaction()
.add(
R.id.fragment_container,
newFlutterFragment,
TAG_FLUTTER_FRAGMENT
)
.commit()
}
}
}
public class MyActivity extends FragmentActivity {
// Define a tag String to represent the FlutterFragment within this
// Activity's FragmentManager. This value can be whatever you'd like.
private static final String TAG_FLUTTER_FRAGMENT = "flutter_fragment";
// Declare a local variable to reference the FlutterFragment so that you
// can forward calls to it later.
private FlutterFragment flutterFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Inflate a layout that has a container for your FlutterFragment.
// For this example, assume that a FrameLayout exists with an ID of
// R.id.fragment_container.
setContentView(R.layout.my_activity_layout);
// Get a reference to the Activity's FragmentManager to add a new
// FlutterFragment, or find an existing one.
FragmentManager fragmentManager = getSupportFragmentManager();
// Attempt to find an existing FlutterFragment,
// in case this is not the first time that onCreate() was run.
flutterFragment = (FlutterFragment) fragmentManager
.findFragmentByTag(TAG_FLUTTER_FRAGMENT);
// Create and attach a FlutterFragment if one does not exist.
if (flutterFragment == null) {
flutterFragment = FlutterFragment.createDefault();
fragmentManager
.beginTransaction()
.add(
R.id.fragment_container,
flutterFragment,
TAG_FLUTTER_FRAGMENT
)
.commit();
}
}
}
先前的程式碼足以呈現從呼叫您的 main()
Dart 進入點、/
的初始 Flutter 路由和新的 FlutterEngine
開始的 Flutter UI。但是,此程式碼不足以實現所有預期的 Flutter 行為。Flutter 依賴必須從您的主機 Activity
轉發到 FlutterFragment
的各種 OS 訊號。以下範例中顯示了這些呼叫
class MyActivity : FragmentActivity() {
override fun onPostResume() {
super.onPostResume()
flutterFragment!!.onPostResume()
}
override fun onNewIntent(@NonNull intent: Intent) {
flutterFragment!!.onNewIntent(intent)
}
override fun onBackPressed() {
flutterFragment!!.onBackPressed()
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String?>,
grantResults: IntArray
) {
flutterFragment!!.onRequestPermissionsResult(
requestCode,
permissions,
grantResults
)
}
override fun onActivityResult(
requestCode: Int,
resultCode: Int,
data: Intent?
) {
super.onActivityResult(requestCode, resultCode, data)
flutterFragment!!.onActivityResult(
requestCode,
resultCode,
data
)
}
override fun onUserLeaveHint() {
flutterFragment!!.onUserLeaveHint()
}
override fun onTrimMemory(level: Int) {
super.onTrimMemory(level)
flutterFragment!!.onTrimMemory(level)
}
}
public class MyActivity extends FragmentActivity {
@Override
public void onPostResume() {
super.onPostResume();
flutterFragment.onPostResume();
}
@Override
protected void onNewIntent(@NonNull Intent intent) {
flutterFragment.onNewIntent(intent);
}
@Override
public void onBackPressed() {
flutterFragment.onBackPressed();
}
@Override
public void onRequestPermissionsResult(
int requestCode,
@NonNull String[] permissions,
@NonNull int[] grantResults
) {
flutterFragment.onRequestPermissionsResult(
requestCode,
permissions,
grantResults
);
}
@Override
public void onActivityResult(
int requestCode,
int resultCode,
@Nullable Intent data
) {
super.onActivityResult(requestCode, resultCode, data);
flutterFragment.onActivityResult(
requestCode,
resultCode,
data
);
}
@Override
public void onUserLeaveHint() {
flutterFragment.onUserLeaveHint();
}
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
flutterFragment.onTrimMemory(level);
}
}
將 OS 訊號轉發到 Flutter 後,您的 FlutterFragment
會如預期般運作。您現在已將 FlutterFragment
新增至現有的 Android 應用程式。
最簡單的整合路徑是使用新的 FlutterEngine
,它具有非平凡的初始化時間,導致在 Flutter 初始化並首次呈現之前出現空白 UI。透過使用快取的預熱 FlutterEngine
,可以避免大部分這種時間負擔,接下來將討論這一點。
使用預先暖機的 FlutterEngine
#預設情況下,FlutterFragment
會建立自己的 FlutterEngine
實例,這需要非平凡的暖機時間。這表示您的使用者會短暫看到空白的 Fragment
。您可以透過使用現有的預熱 FlutterEngine
實例來減輕大部分暖機時間。
若要在 FlutterFragment
中使用預熱的 FlutterEngine
,請使用 withCachedEngine()
工廠方法建立 FlutterFragment
的實例。
// Somewhere in your app, before your FlutterFragment is needed,
// like in the Application class ...
// Instantiate a FlutterEngine.
val flutterEngine = FlutterEngine(context)
// Start executing Dart code in the FlutterEngine.
flutterEngine.getDartExecutor().executeDartEntrypoint(
DartEntrypoint.createDefault()
)
// Cache the pre-warmed FlutterEngine to be used later by FlutterFragment.
FlutterEngineCache
.getInstance()
.put("my_engine_id", flutterEngine)
FlutterFragment.withCachedEngine("my_engine_id").build()
// Somewhere in your app, before your FlutterFragment is needed,
// like in the Application class ...
// Instantiate a FlutterEngine.
FlutterEngine flutterEngine = new FlutterEngine(context);
// Start executing Dart code in the FlutterEngine.
flutterEngine.getDartExecutor().executeDartEntrypoint(
DartEntrypoint.createDefault()
);
// Cache the pre-warmed FlutterEngine to be used later by FlutterFragment.
FlutterEngineCache
.getInstance()
.put("my_engine_id", flutterEngine);
FlutterFragment.withCachedEngine("my_engine_id").build();
FlutterFragment
在內部瞭解 FlutterEngineCache
並根據給定給 withCachedEngine()
的 ID 擷取預熱的 FlutterEngine
。
透過提供如先前所示的預熱 FlutterEngine
,您的應用程式會盡快呈現第一個 Flutter 畫面。
使用快取引擎的初始路由
#當使用新的 FlutterEngine
設定 FlutterActivity
或 FlutterFragment
時,可以使用初始路由的概念。但是,當使用快取引擎時,FlutterActivity
和 FlutterFragment
不會提供初始路由的概念。這是因為快取引擎預計已在執行 Dart 程式碼,這表示設定初始路由的時間已太晚。
希望其快取引擎從自訂初始路由開始的開發人員,可以在執行 Dart 進入點之前,將其快取的 FlutterEngine
設定為使用自訂初始路由。以下範例示範了使用快取引擎的初始路由
class MyApplication : Application() {
lateinit var flutterEngine : FlutterEngine
override fun onCreate() {
super.onCreate()
// Instantiate a FlutterEngine.
flutterEngine = FlutterEngine(this)
// Configure an initial route.
flutterEngine.navigationChannel.setInitialRoute("your/route/here");
// Start executing Dart code to pre-warm the FlutterEngine.
flutterEngine.dartExecutor.executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
)
// Cache the FlutterEngine to be used by FlutterActivity or FlutterFragment.
FlutterEngineCache
.getInstance()
.put("my_engine_id", flutterEngine)
}
}
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// Instantiate a FlutterEngine.
flutterEngine = new FlutterEngine(this);
// Configure an initial route.
flutterEngine.getNavigationChannel().setInitialRoute("your/route/here");
// Start executing Dart code to pre-warm the FlutterEngine.
flutterEngine.getDartExecutor().executeDartEntrypoint(
DartEntrypoint.createDefault()
);
// Cache the FlutterEngine to be used by FlutterActivity or FlutterFragment.
FlutterEngineCache
.getInstance()
.put("my_engine_id", flutterEngine);
}
}
透過設定導覽通道的初始路由,相關的 FlutterEngine
會在初始執行 runApp()
Dart 函數時顯示所需的路由。
在初始執行 runApp()
之後變更導覽通道的初始路由屬性無效。希望在不同的 Activity
和 Fragment
之間使用相同的 FlutterEngine
並在這些顯示器之間切換路由的開發人員,需要設定方法通道並明確指示其 Dart 程式碼變更 Navigator
路由。
顯示啟動畫面
#即使使用預熱的 FlutterEngine
,Flutter 內容的初始顯示也需要一些等待時間。為了協助改善此短暫等待期間的使用者體驗,Flutter 支援顯示啟動畫面(也稱為「載入畫面」),直到 Flutter 呈現其第一個畫面。如需有關如何顯示啟動畫面的指示,請參閱啟動畫面指南。
使用指定的初始路由執行 Flutter
#Android 應用程式可能包含許多獨立的 Flutter 體驗,這些體驗在不同的 FlutterFragment
中執行,並具有不同的 FlutterEngine
。在這些情況下,每個 Flutter 體驗通常會以不同的初始路由(/
以外的路由)開始。為了方便起見,FlutterFragment
的 Builder
允許您指定所需的初始路由,如下所示
// With a new FlutterEngine.
val flutterFragment = FlutterFragment.withNewEngine()
.initialRoute("myInitialRoute/")
.build()
// With a new FlutterEngine.
FlutterFragment flutterFragment = FlutterFragment.withNewEngine()
.initialRoute("myInitialRoute/")
.build();
從指定的進入點執行 Flutter
#與不同的初始路由類似,不同的 FlutterFragment
可能會想要執行不同的 Dart 進入點。在典型的 Flutter 應用程式中,只有一個 Dart 進入點:main()
,但您可以定義其他進入點。
FlutterFragment
支援指定要針對給定 Flutter 體驗執行的所需 Dart 進入點。若要指定進入點,請建立 FlutterFragment
,如下所示
val flutterFragment = FlutterFragment.withNewEngine()
.dartEntrypoint("mySpecialEntrypoint")
.build()
FlutterFragment flutterFragment = FlutterFragment.withNewEngine()
.dartEntrypoint("mySpecialEntrypoint")
.build();
FlutterFragment
設定會導致執行名為 mySpecialEntrypoint()
的 Dart 進入點。請注意,括號 ()
不包含在 dartEntrypoint
String
名稱中。
控制 FlutterFragment
的渲染模式
#FlutterFragment
可以使用 SurfaceView
來呈現其 Flutter 內容,也可以使用 TextureView
。預設值為 SurfaceView
,其效能明顯優於 TextureView
。但是,SurfaceView
無法插入 Android View
階層的中間。SurfaceView
必須是階層中最底層的 View
,或階層中最頂層的 View
。此外,在 Android N 之前的 Android 版本中,SurfaceView
無法建立動畫,因為它們的版面配置和呈現與其餘的 View
階層不同步。如果您的應用程式需要其中任何一個使用案例,則您需要使用 TextureView
而不是 SurfaceView
。透過使用 texture
RenderMode
建立 FlutterFragment
來選取 TextureView
// With a new FlutterEngine.
val flutterFragment = FlutterFragment.withNewEngine()
.renderMode(FlutterView.RenderMode.texture)
.build()
// With a cached FlutterEngine.
val flutterFragment = FlutterFragment.withCachedEngine("my_engine_id")
.renderMode(FlutterView.RenderMode.texture)
.build()
// With a new FlutterEngine.
FlutterFragment flutterFragment = FlutterFragment.withNewEngine()
.renderMode(FlutterView.RenderMode.texture)
.build();
// With a cached FlutterEngine.
FlutterFragment flutterFragment = FlutterFragment.withCachedEngine("my_engine_id")
.renderMode(FlutterView.RenderMode.texture)
.build();
使用顯示的組態,產生的 FlutterFragment
會將其 UI 呈現到 TextureView
。
顯示具有透明度的 FlutterFragment
#預設情況下,FlutterFragment
會使用 SurfaceView
呈現不透明的背景。(請參閱「控制 FlutterFragment
的渲染模式」。)對於 Flutter 未繪製的任何像素,該背景為黑色。基於效能考量,使用不透明背景進行呈現是偏好的渲染模式。在 Android 上使用透明度呈現 Flutter 會對效能產生負面影響。但是,有許多設計需要在 Flutter 體驗中使用透明像素,以便顯示到基礎 Android UI。因此,Flutter 支援 FlutterFragment
中的半透明。
若要為 FlutterFragment
啟用透明度,請使用下列組態建立它
// Using a new FlutterEngine.
val flutterFragment = FlutterFragment.withNewEngine()
.transparencyMode(FlutterView.TransparencyMode.transparent)
.build()
// Using a cached FlutterEngine.
val flutterFragment = FlutterFragment.withCachedEngine("my_engine_id")
.transparencyMode(FlutterView.TransparencyMode.transparent)
.build()
// Using a new FlutterEngine.
FlutterFragment flutterFragment = FlutterFragment.withNewEngine()
.transparencyMode(FlutterView.TransparencyMode.transparent)
.build();
// Using a cached FlutterEngine.
FlutterFragment flutterFragment = FlutterFragment.withCachedEngine("my_engine_id")
.transparencyMode(FlutterView.TransparencyMode.transparent)
.build();
FlutterFragment
與其 Activity
之間的關係
#有些應用程式會選擇使用 Fragment
作為整個 Android 畫面。在這些應用程式中,Fragment
控制系統外觀(例如 Android 的狀態列、導覽列和方向)是合理的。
在其他應用程式中,Fragment
僅用於表示 UI 的一部分。FlutterFragment
可能用於實作抽屜的內部、影片播放器或單張卡片。在這些情況下,FlutterFragment
影響 Android 的系統外觀是不恰當的,因為在同一個 Window
中還有其他 UI 元件。
FlutterFragment
提供一個概念,有助於區分 FlutterFragment
應該能夠控制其宿主 Activity
的情況,以及 FlutterFragment
僅應影響自身行為的情況。為了防止 FlutterFragment
將其 Activity
暴露給 Flutter 外掛程式,並防止 Flutter 控制 Activity
的系統 UI,請使用 FlutterFragment
的 Builder
中的 shouldAttachEngineToActivity()
方法,如下所示:
// Using a new FlutterEngine.
val flutterFragment = FlutterFragment.withNewEngine()
.shouldAttachEngineToActivity(false)
.build()
// Using a cached FlutterEngine.
val flutterFragment = FlutterFragment.withCachedEngine("my_engine_id")
.shouldAttachEngineToActivity(false)
.build()
// Using a new FlutterEngine.
FlutterFragment flutterFragment = FlutterFragment.withNewEngine()
.shouldAttachEngineToActivity(false)
.build();
// Using a cached FlutterEngine.
FlutterFragment flutterFragment = FlutterFragment.withCachedEngine("my_engine_id")
.shouldAttachEngineToActivity(false)
.build();
將 false
傳遞給 shouldAttachEngineToActivity()
Builder
方法可防止 Flutter 與周圍的 Activity
互動。預設值為 true
,允許 Flutter 和 Flutter 外掛程式與周圍的 Activity
互動。
除非另有說明,本網站上的文件皆反映 Flutter 的最新穩定版本。頁面最後更新時間為 2024-06-26。 檢視原始碼 或 回報問題。