Я представил встраивание Flutter в нативный проект раньше.Есть возможность встроить его в виде страницы или представления.Недавно я посмотрел исходный код Flutter и обнаружил, что Flutter также поддерживает встраивание нативного представления во Flutter. макет.Эта функция не была представлена в документе, но она есть.Это очень практичная функция, такая как давно проблемная реализация карты.С помощью этой функции мы можем встраивать родные карты AutoNavi для двух платформ или карты Baidu во Flutter макет и даже предварительный просмотр видео с камеры SDK.
В этой статье в качестве примера используется простой TextView, чтобы показать, как встраивать собственные компоненты в проект Flutter.
Создайте проект на флаттере
Стандартный способ написания собственных расширений компонентов заключается в создании проекта подключаемого модуля, после чего проект Flutter вводит его в проект подключаемого модуля для использования.В этой статье для удобства компоненты написаны и зарегистрированы непосредственно в проекте Flutter. Разработка проекта плагина будет представлена позже.
Используйте AndroidStudio, чтобы создать обычный проект Flutter, изменить файл main.dar и удалить ненужный код в демонстрационных целях.Отсортированный код выглядит следующим образом:
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
),
);
}
}
Напишите и зарегистрируйте нативные компоненты в проектах Android
Процесс добавления нативных компонентов в основном такой:
1. Реализуйте собственный компонент PlatformView, чтобы обеспечить собственное представление.
2. Создайте PlatformViewFactory для создания PlatformView
3. Создайте FlutterPlugin для регистрации нативных компонентов.
Создание нативных компонентов
В проекте FLutter создается несколько папок. lib — это код проекта Flutter. Папки android и ios — соответствующие двухплатформенные нативные проекты. Здесь напрямую открывается каталог проекта Android. Проект генерирует два файла: GeneratedPluginRegistrant и MainActivity по умолчанию. Не перемещайте GeneratedPluginRegistrant. Создайте новый пользовательский вид в пакете с MainActivity. Собственный вид Flutter не может напрямую наследоваться от представления и должен реализовать предоставленный интерфейс PlatformView:
public class MyView implements PlatformView {
private final TextView myNativeView;
MyView(Context context, BinaryMessenger messenger, int id, Map<String, Object> params) {
TextView myNativeView = new TextView(context);
myNativeView.setText("我是来自Android的原生TextView");
this.myNativeView = myNativeView;
}
@Override
public View getView() {
return myNativeView;
}
@Override
public void dispose() {
}
}
Это класс-обертка, который возвращает нативный объект View во Flutter в реализованном методе getView, который удобен для демонстрации и возвращает TextView.
Создать PlatformViewFactory
Затем создайте PlatformViewFactory и создайте класс, наследуемый от PlatformViewFactory:
public class MyViewFactory extends PlatformViewFactory {
private final BinaryMessenger messenger;
public MyViewFactory(BinaryMessenger messenger) {
super(StandardMessageCodec.INSTANCE);
this.messenger = messenger;
}
@SuppressWarnings("unchecked")
@Override
public PlatformView create(Context context, int id, Object args) {
Map<String, Object> params = (Map<String, Object>) args;
return new MyView(context, messenger, id, params);
}
В методе create можно получить три параметра: args — это пользовательский параметр, передаваемый Flutter, который здесь временно недоступен.
Зарегистрировать плагин
Создайте класс плагина MyViewFlutterPlugin и пропишите логику регистрации в статическом методе класса для вызова:
public class MyViewFlutterPlugin {
public static void registerWith(PluginRegistry registry) {
final String key = MyViewFlutterPlugin.class.getCanonicalName();
if (registry.hasPlugin(key)) return;
PluginRegistry.Registrar registrar = registry.registrarFor(key);
registrar.platformViewRegistry().registerViewFactory("plugins.nightfarmer.top/myview", new MyViewFactory(registrar.messenger()));
}
}
используется в приведенном выше кодеplugins.nightfarmer.top/myview
Такая строка, которая является зарегистрированным именем компонента, должна использоваться при вызовах Flutter, вы можете использовать строку в любом формате.
Добавьте регистрационный вызов в метод onCreate MainActivity.
MyViewFlutterPlugin.registerWith(this);
Поскольку он написан непосредственно в проекте Flutter, логика регистрации также может быть написана непосредственно в Activity.Чтобы соответствовать процессу регистрации проекта плагина, рекомендуется записать ее.
Вызов собственного представления в проекте Flutter
Вызов нативного представления очень прост, при использовании представления на платформе Android нужно только создатьAndroidView
компонент и сообщите ему регистрационное имя компонента:
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: AndroidView(viewType: 'plugins.nightfarmer.top/myview'),
),
);
}
}
Поскольку реализована только платформа Android, AndroidView вызывается здесь напрямую.Если вы реализуете двойную платформу, вы можете импортировать ее, введяpackage:flutter/foundation.dart
упаковать и судитьdefaultTargetPlatform
даTargetPlatform.android
ещеTargetPlatform.iOS
внедрить реализации для разных платформ.
Добавить параметры в собственное представление
В некоторых случаях необходимо предоставить некоторые параметры инициализации для собственного компонента, такие как URL-адрес веб-просмотра, такие как координаты центра карты и текст на китайском языке из приведенного выше примера, мы можем передать карту достигать:
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
print(defaultTargetPlatform);
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: AndroidView(
viewType: 'plugins.nightfarmer.top/myview',
creationParams: {
"myContent": "通过参数传入的文本内容",
},
creationParamsCodec: const StandardMessageCodec(),
),
),
);
}
}
creationParams
Параметр карты передается и принимается собственным компонентом,creationParamsCodec
То, что передается, является объектом кодирования, который является фиксированным способом записи.
Затем получите параметры в нативном компоненте и инициализируйте текст TextView:
public class MyView implements PlatformView {
private final TextView myNativeView;
MyView(Context context, BinaryMessenger messenger, int id, Map<String, Object> params) {
TextView myNativeView = new TextView(context);
myNativeView.setText("我是来自Android的原生TextView");
this.myNativeView = myNativeView;
if (params.containsKey("myContent")) {
String myContent = (String) params.get("myContent");
myNativeView.setText(myContent);
}
}
...
}
Следует отметить, что параметры, инициализированные собственными компонентами, не будут повторно назначаться с помощью setState, то есть это параметр инициализации.
Как изменить состояние созданного нативного компонента можно через MethodCall, см. ниже
Общайтесь с нативными компонентами через MethodChannel
Сначала позвольте исходному компоненту реализоватьMethodCallHandler
интерфейс:
public class MyView implements PlatformView, MethodChannel.MethodCallHandler {
private final TextView myNativeView;
MyView(Context context, BinaryMessenger messenger, int id, Map<String, Object> params) {
...
MethodChannel methodChannel = new MethodChannel(messenger, "plugins.nightfarmer.top/myview_" + id);
methodChannel.setMethodCallHandler(this);
}
@Override
public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
// 在接口的回调方法中可以接收到来自Flutter的调用
}
...
}
Затем сделайте следующее в коде дротика:
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
print(defaultTargetPlatform);
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: AndroidView(
viewType: 'plugins.nightfarmer.top/myview',
creationParams: {
"myContent": "通过参数传入的文本内容",
},
creationParamsCodec: const StandardMessageCodec(),
onPlatformViewCreated: onMyViewCreated,
),
),
);
}
MethodChannel _channel;
void onMyViewCreated(int id) {
_channel = new MethodChannel('plugins.nightfarmer.top/myview_$id');
setMyViewText();
}
Future<void> setMyViewText(String text) async {
assert(text != null);
return _channel.invokeMethod('setText', text);
}
}
пройти черезonPlatformViewCreated
Обратный вызов, отслеживать успешное создание исходного компонента и получать идентификатор текущего компонента в параметрах метода обратного вызова, этот идентификатор назначается системой случайным образом, а затем создавать связь с компонентом, добавляя к назначенному идентификатору префикс имя нашего компонента MethodChannel, после получения объекта канала, вы можете отправить сообщение собственному компоненту через метод invokeMethod. Здесь сообщение "setText" отправляется с текстовым содержимым. Ниже приведена логика обработки сообщения, полученного в родной компонент.
@Override
public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
if ("setText".equals(methodCall.method)) {
String text = (String) methodCall.arguments;
myNativeView.setText(text);
result.success(null);
}
}
Метод обработки onMethodCall такой же, как и у обычных подключаемых модулей, и здесь повторяться не будет.
Как производительность
Создайте несколько нативных компонентов через ListView и посмотрите, как это работает:
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
print(defaultTargetPlatform);
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ListView.builder(
itemBuilder: (context, index) {
return Container(
child: AndroidView(
viewType: 'plugins.nightfarmer.top/myview',
creationParams: {
"myContent": "通过参数传入的文本内容$index",
},
creationParamsCodec: const StandardMessageCodec(),
),
height: 100,
);
},
itemCount: 100,
),
);
}
}
Несмотря на то, что он работает таким образом, ListView действительно может нормально скользить, но он может чувствовать очевидную потерю кадров.Можно видеть, что ситуация с созданием нескольких нативных компонентов в одном интерфейсе сильно влияет на производительность, и это не рекомендуется для фактическая разработка Большое количество собственных компонентов представлено в , потому что, за исключением особых случаев, таких как карта / WebView, в основном эффекты пользовательского интерфейса, которые могут быть достигнуты изначально, могут быть достигнуты с помощью механизма пользовательского интерфейса Flutter.
При разработке нативных компонентов горячая загрузка Flutter неэффективна, потому что нативный проект нужно каждый раз компилировать, чтобы сделать его эффективным. Кроме того, моя среда Mac здесь не может нормально работать с Genymotion, мне нужно использовать настоящую машину, а не использовать ее.
--enable-software-rendering
только параметры.
Эта статья закончилась.
Больше галантерейных товаров перемещается в мой личный блогwww.nightfarmer.top/