原文來自 flutter community
原由
最近想要實現一個flutter地圖外掛,但是flutter只提供了基於google map的官方外掛,所以想要使用高德地圖和百度地圖自己做一個,就看到了這篇文章,順便翻譯一下分享給大家。
這裡先貼出google map外掛文章以便有梯子的同學可以直接使用Exploring Google Maps in Flutter
譯文
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 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.javaPlatformViewFactory
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.javaPlatformView
和MethodCallHandler
以至於我們可以將原生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
。有一個TextView
用Container
包裹,這個container元件作為一個垂直排列元件的第一個子元件,第二個子元件是一個flutter Text
。
我們同樣實現了方法onTextViewCreated
,在這個方法裡面去呼叫setText
。
這個例子證明了可以對任何原生Android view轉換成flutterwidget,並且像其他flutter元件一樣繪製和轉換。