【譯】Flutter PlatformView: 如何通過原生view建立widget

kinsomy發表於2018-12-04

原文來自 flutter community

medium.com/flutter-com…)

原由

最近想要實現一個flutter地圖外掛,但是flutter只提供了基於google map的官方外掛,所以想要使用高德地圖和百度地圖自己做一個,就看到了這篇文章,順便翻譯一下分享給大家。

這裡先貼出google map外掛文章以便有梯子的同學可以直接使用Exploring Google Maps in Flutter

譯文

【譯】Flutter PlatformView: 如何通過原生view建立widget

Flutter最近剛剛有了一個新的元件叫做PlatformView,它允許開發者在flutter裡面嵌入Android原生的view。這一開放舉措為實現注入地圖和webview提供了許多新的可能。(github.com/flutter/flu…).

在這篇教程裡,我們將探索如何建立一個TextViewPlugin,在plugin裡我們會暴露一個android原生TextView作為flutter元件。

在進入程式碼實現之前需要注意以下幾點:

  • 目前只支援Android(作者文章釋出於2018.9.7,目前已經支援ios)。
  • 需要android api版本在20及以上。
  • 嵌入Android views是一個昂貴的操作,所以應當避免在flutter能夠實現的情況下去使用它。
  • 嵌入Android view的繪製和其他任何flutter widget一樣,view的轉換也同樣使用。
  • 元件會撐滿所有可獲得控制元件,因此它的父元件需要提供一個佈局邊界。
  • 需要切換到flutter的master分支上使用(目前已經不需要)

下面開始實現部分

第一步就是建立一個flutter plugin專案。

【譯】Flutter PlatformView: 如何通過原生view建立widget
【譯】Flutter PlatformView: 如何通過原生view建立widget

在flutter plugin專案建立完成之後在./lib/text_view.dart建立TextView類。

import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
typedef void TextViewCreatedCallback(TextViewController controller);
class TextView extends StatefulWidget {
const TextView({
Key key, this.onTextViewCreated,
}) : super(key: key);
final TextViewCreatedCallback onTextViewCreated;
@override State<
StatefulWidget>
createState() =>
_TextViewState();

}class _TextViewState extends State<
TextView>
{
@override Widget build(BuildContext context) {
if (defaultTargetPlatform == TargetPlatform.android) {
return AndroidView( viewType: 'plugins.felix.angelov/textview', onPlatformViewCreated: _onPlatformViewCreated, );

} return Text( '$defaultTargetPlatform is not yet supported by the text_view plugin');

} void _onPlatformViewCreated(int id) {
if (widget.onTextViewCreated == null) {
return;

} widget.onTextViewCreated(new TextViewController._(id));

}
}class TextViewController {
TextViewController._(int id) : _channel = new MethodChannel('plugins.felix.angelov/textview_$id');
final MethodChannel _channel;
Future<
void>
setText(String text) async {
assert(text != null);
return _channel.invokeMethod('setText', text);

}
}複製程式碼

一個需要重點注意的是當我們在上面程式碼第24行建立了AndroidView,我們需要提供一個viewType,會在稍後介紹。

我們還提供了一個onPlatformCompleted實現,以便我們可以為TextView小部件提供一個TextViewController,然後可以使用它來使用setText方法更新它的文字。

完整的AndroidView文件請見 docs.flutter.io/flutter/wid…

接下來我們需要實現Android部分的TextViewPlugin

我們開啟另一個生成檔案./android/src/main/java/{organization_name
}/TextViewPlugin.java
,用一下內容替換檔案內的內容。

package angelov.felix.textview;
import io.flutter.plugin.common.PluginRegistry.Registrar;
public class TextViewPlugin {
public static void registerWith(Registrar registrar) {
registrar .platformViewRegistry() .registerViewFactory( "plugins.felix.angelov/textview", new TextViewFactory(registrar.messenger()));

}
}複製程式碼

我們需要做的就是實現registerWith方法,傳入text_view.dart中定義的viewType,同時提供一個TextViewFactory,它將會建立原生TextView作為PlatformView

接著我們需要建立./android/src/main/java/{organization_name
}/TextViewFactory.java
,並繼承PlatformViewFactory

package angelov.felix.textview;
import android.content.Context;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.StandardMessageCodec;
import io.flutter.plugin.platform.PlatformView;
import io.flutter.plugin.platform.PlatformViewFactory;
public class TextViewFactory extends PlatformViewFactory {
private final BinaryMessenger messenger;
public TextViewFactory(BinaryMessenger messenger) {
super(StandardMessageCodec.INSTANCE);
this.messenger = messenger;

} @Override public PlatformView create(Context context, int id, Object o) {
return new FlutterTextView(context, messenger, id);

}
}複製程式碼

TextViewFactory實現了create方法,它會返回PlatformView,在我們的例子裡叫做FlutterTextView

然後,建立./android/src/main/java/{organization_name
}/FlutterTextView.java
並且實現PlatformViewMethodCallHandler以至於我們可以將原生view繪製到flutter元件並且通過MethodChannel從dart接收資料。

package angelov.felix.textview;
import android.content.Context;
import android.view.View;
import android.widget.TextView;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import static io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import static io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.platform.PlatformView;
public class FlutterTextView implements PlatformView, MethodCallHandler {
private final TextView textView;
private final MethodChannel methodChannel;
FlutterTextView(Context context, BinaryMessenger messenger, int id) {
textView = new TextView(context);
methodChannel = new MethodChannel(messenger, "plugins.felix.angelov/textview_" + id);
methodChannel.setMethodCallHandler(this);

} @Override public View getView() {
return textView;

} @Override public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
switch (methodCall.method) {
case "setText": setText(methodCall, result);
break;
default: result.notImplemented();

}
} private void setText(MethodCall methodCall, Result result) {
String text = (String) methodCall.arguments;
textView.setText(text);
result.success(null);

} @Override public void dispose() {
}
}複製程式碼

FlutterTextView建立了一個新的TextView並且設定了一個MethodChannel,因此TextView可以從dart程式碼接收資料進行檢視更新。

為了實現PlatformView,我們需要重寫getView方法返回textview物件,同時重寫dispose方法。

為了實現MethodCallHandler,需要重寫onMethodCall,在接收到setText呼叫指令時候更新TextView,或者在不支援的其他指令情況下返回result.notImplemented

現在可以測試我們的新TextView元件。

開啟./example/lib/main.dart用下面的程式碼替換。

import 'package:flutter/material.dart';
import 'package:text_view/text_view.dart';
void main() =>
runApp(MaterialApp(home: TextViewExample()));
class TextViewExample extends StatelessWidget {
@override Widget build(BuildContext context) {
return Scaffold( appBar: AppBar(title: const Text('Flutter TextView example')), body: Column(children: [ Center( child: Container( padding: EdgeInsets.symmetric(vertical: 30.0), width: 130.0, height: 100.0, child: TextView( onTextViewCreated: _onTextViewCreated, ))), Expanded( flex: 3, child: Container( color: Colors.blue[100], child: Center(child: Text("Hello from Flutter!")))) ]));

} void _onTextViewCreated(TextViewController controller) {
controller.setText('Hello from Android!');

}
}複製程式碼

程式碼看著很熟悉,我們執行一個MaterialApp,最外層用Scaffold。有一個TextViewContainer包裹,這個container元件作為一個垂直排列元件的第一個子元件,第二個子元件是一個flutter Text

我們同樣實現了方法onTextViewCreated,在這個方法裡面去呼叫setText

【譯】Flutter PlatformView: 如何通過原生view建立widget

這個例子證明了可以對任何原生Android view轉換成flutterwidget,並且像其他flutter元件一樣繪製和轉換。

來源:https://juejin.im/post/5c063b57e51d451dea18137b

相關文章