將 Flutter 檢視新增到 Android 應用
瞭解如何透過 Flutter 檢視執行高階整合。
透過 FlutterView 整合比之前描述的透過 FlutterActivity 和 FlutterFragment 整合需要更多的工作。
從根本上說,Dart 側的 Flutter 框架需要訪問各種活動級別的事件和生命週期才能正常工作。由於 FlutterView(是一個 android.view.View)可以新增到由開發者應用程式擁有的任何活動中,並且由於 FlutterView 沒有訪問活動級別事件的許可權,因此開發者必須手動將這些連線橋接到 FlutterEngine。
您選擇如何將應用程式的活動事件饋送到 FlutterView 將取決於您的應用程式。
一個示例
#
與 FlutterActivity 和 FlutterFragment 的指南不同,FlutterView 整合可以透過示例專案更好地演示。
示例專案位於 https://github.com/flutter/samples/tree/main/add_to_app/android_view,用於記錄一個簡單的 FlutterView 整合,其中 FlutterView 用於如上圖 GIF 所示的卡片 RecycleView 列表中的某些單元格。
通用方法
#FlutterView 級別整合的基本思路是,您必須重新建立在您的 Activity、FlutterView 和 FlutterEngine 之間存在的各種互動,這些互動存在於 FlutterActivityAndFragmentDelegate 中,並在您自己的應用程式程式碼中實現。在 FlutterActivityAndFragmentDelegate 中建立的連線在使用 FlutterActivity 或 FlutterFragment 時會自動完成,但由於在本例中 FlutterView 被新增到應用程式中的 Activity 或 Fragment 中,因此您必須手動重新建立連線。否則,FlutterView 將無法渲染任何內容或缺少其他功能。
一個示例 FlutterViewEngine 類展示了一種可能的應用程式特定連線的實現,該連線位於 Activity、FlutterView 和 FlutterEngine 之間。
需要實現的 API
#Flutter 繪製任何內容所需的絕對最低實現是
- 當
FlutterView新增到已恢復的Activity的檢視層次結構並可見時,呼叫attachToFlutterEngine;以及 - 當託管
FlutterView的Activity可見時,在FlutterEngine的lifecycleChannel欄位上呼叫appIsResumed。
還必須呼叫相反的 detachFromFlutterEngine 和 LifecycleChannel 類上的其他生命週期方法,以避免在 FlutterView 或 Activity 不再可見時洩漏資源。
此外,請參閱 FlutterViewEngine 演示類或 FlutterActivityAndFragmentDelegate 中的剩餘實現,以確保其他功能(如剪貼簿、系統 UI 覆蓋層、外掛等)的正常執行。
內容大小的檢視
#通常,FlutterView 需要固定的尺寸,要麼透過它自己的尺寸,要麼透過匹配父級的尺寸。這可以在 示例專案 中看到。但是,現在允許 FlutterView 根據其內容調整自身大小。透過對高度或寬度使用 content_wrap,FlutterView 可以調整自身大小,如在 內容大小的示例專案 中所示。
- 要在部署應用程式時啟用內容大小的檢視,請將以下設定新增到專案中的
AndroidManifest.xml檔案下的<application>標籤
<meta-data
android:name="io.flutter.embedding.android.EnableContentSizing"
android:value="true" />
限制
#由於內容大小的 Flutter 檢視需要您的 Flutter 應用程式能夠調整自身大小,因此某些小部件不受支援。
- 具有無界大小的小部件,例如
ListView。 - 將大小委託給其子項的小部件,例如
LayoutBuilder。
在實踐中,這意味著許多常見的小部件不受支援,例如 ScaffoldBuilder、CupertinoTimerPicker 或任何內部依賴於 LayoutBuilder 的小部件。如有疑問,可以使用 UnconstrainedBox 來測試小部件對內容大小檢視的可用性,如以下示例所示
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context)
=> MaterialApp(home: MyPage());
}
class MyPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: UnconstrainedBox(
// TODO: Edit this line to check if a widget
// can cause problems with content-sized views.
child: Text('This works!'),
// child: Column(children: [Column(children: [Expanded(child: Text('This blows up!'))])]),
// child: ListView(children: [Text('This blows up!')]),
)
);
}
}