Мне также потребовались некоторые собственные виджеты Android для связи с моим кодом дротика, и после некоторой работы мне удалось это сделать. На мой взгляд, документация о том, как это сделать, немного скудна, но проявив немного творчества, мне удалось заставить это работать. Я не провел достаточно тестов, чтобы назвать это 100% готовым к производству, но, похоже, он работает ...
Настройка дротика
Перейдите к main.dart
и добавьте следующую функцию верхнего уровня:
void initializeAndroidWidgets() {
if (Platform.isAndroid) {
// Intialize flutter
WidgetsFlutterBinding.ensureInitialized();
const MethodChannel channel = MethodChannel('com.example.app/widget');
final CallbackHandle callback = PluginUtilities.getCallbackHandle(onWidgetUpdate);
final handle = callback.toRawHandle();
channel.invokeMethod('initialize', handle);
}
}
затем вызовите эту функцию перед запуском вашего приложения
void main() {
initializeAndroidWidgets();
runApp(MyApp());
}
это гарантирует, что мы сможем получить дескриптор обратного вызова на нативной стороне для нашей точки входа.
Теперь добавьте точку входа так:
void onWidgetUpdate() {
// Intialize flutter
WidgetsFlutterBinding.ensureInitialized();
const MethodChannel channel = MethodChannel('com.example.app/widget');
// If you use dependency injection you will need to inject
// your objects before using them.
channel.setMethodCallHandler(
(call) async {
final id = call.arguments;
print('on Dart ${call.method}!');
// Do your stuff here...
final result = Random().nextDouble();
return {
// Pass back the id of the widget so we can
// update it later
'id': id,
// Some data
'value': result,
};
},
);
}
Эта функция будет точкой входа для наших виджетов и будет вызываться при вызове метода onUpdate
нашего виджета. Затем мы можем передать некоторые данные (например, после вызова api).
Настройка Android
Примеры здесь находятся на Kotlin, но должны работать с некоторыми незначительными изменениями также и на Java.
Создайте класс WidgetHelper
, который поможет нам хранить и обрабатывать нашу точку входа:
class WidgetHelper {
companion object {
private const val WIDGET_PREFERENCES_KEY = "widget_preferences"
private const val WIDGET_HANDLE_KEY = "handle"
const val CHANNEL = "com.example.app/widget"
const val NO_HANDLE = -1L
fun setHandle(context: Context, handle: Long) {
context.getSharedPreferences(
WIDGET_PREFERENCES_KEY,
Context.MODE_PRIVATE
).edit().apply {
putLong(WIDGET_HANDLE_KEY, handle)
apply()
}
}
fun getRawHandle(context: Context): Long {
return context.getSharedPreferences(
WIDGET_PREFERENCES_KEY,
Context.MODE_PRIVATE
).getLong(WIDGET_HANDLE_KEY, NO_HANDLE)
}
}
}
Замени свой MainActivity
на это:
class MainActivity : FlutterActivity(), MethodChannel.MethodCallHandler {
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine)
val channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, WidgetHelper.CHANNEL)
channel.setMethodCallHandler(this)
}
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
when (call.method) {
"initialize" -> {
if (call.arguments == null) return
WidgetHelper.setHandle(this, call.arguments as Long)
}
}
}
}
Это просто гарантирует, что мы сохраним дескриптор (хэш точки входа) в SharedPreferences
, чтобы иметь возможность получить его позже в виджете.
Теперь измените свой AppWidgetProvider
, чтобы он выглядел примерно так:
class Foo : AppWidgetProvider(), MethodChannel.Result {
private val TAG = this::class.java.simpleName
companion object {
private var channel: MethodChannel? = null;
}
private lateinit var context: Context
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
this.context = context
initializeFlutter()
for (appWidgetId in appWidgetIds) {
updateWidget("onUpdate ${Math.random()}", appWidgetId, context)
// Pass over the id so we can update it later...
channel?.invokeMethod("update", appWidgetId, this)
}
}
private fun initializeFlutter() {
if (channel == null) {
FlutterMain.startInitialization(context)
FlutterMain.ensureInitializationComplete(context, arrayOf())
val handle = WidgetHelper.getRawHandle(context)
if (handle == WidgetHelper.NO_HANDLE) {
Log.w(TAG, "Couldn't update widget because there is no handle stored!")
return
}
val callbackInfo = FlutterCallbackInformation.lookupCallbackInformation(handle)
// You could also use a hard coded value to save you from all
// the hassle with SharedPreferences, but alas when running your
// app in release mode this would fail.
val entryPointFunctionName = callbackInfo.callbackName
// Instantiate a FlutterEngine.
val engine = FlutterEngine(context.applicationContext)
val entryPoint = DartEntrypoint(FlutterMain.findAppBundlePath(), entryPointFunctionName)
engine.dartExecutor.executeDartEntrypoint(entryPoint)
// Register Plugins when in background. When there
// is already an engine running, this will be ignored (although there will be some
// warnings in the log).
GeneratedPluginRegistrant.registerWith(engine)
channel = MethodChannel(engine.dartExecutor.binaryMessenger, WidgetHelper.CHANNEL)
}
}
override fun success(result: Any?) {
Log.d(TAG, "success $result")
val args = result as HashMap<*, *>
val id = args["id"] as Int
val value = args["value"] as Int
updateWidget("onDart $value", id, context)
}
override fun notImplemented() {
Log.d(TAG, "notImplemented")
}
override fun error(errorCode: String?, errorMessage: String?, errorDetails: Any?) {
Log.d(TAG, "onError $errorCode")
}
override fun onDisabled(context: Context?) {
super.onDisabled(context)
channel = null
}
}
internal fun updateWidget(text: String, id: Int, context: Context) {
val views = RemoteViews(context.packageName, R.layout.small_widget).apply {
setTextViewText(R.id.appwidget_text, text)
}
val manager = AppWidgetManager.getInstance(context)
manager.updateAppWidget(id, views)
}
Здесь важен initializeFlutter
, который гарантирует, что мы сможем получить дескриптор нашей точки входа. Затем в onUpdate
мы вызываем channel?.invokeMethod("update", appWidgetId, this)
, который запускает обратный вызов в нашем MethodChannel
на стороне дротика, определенной ранее. Затем мы обрабатываем результат позже, в success
(по крайней мере, когда вызов будет успешным).
Надеюсь, это даст вам общее представление о том, как этого добиться ...
person
bnxm
schedule
25.04.2020
PendingIntent
. То же самое и здесь, канал не равен нулю, метод dart вызывается, но вызов внутри кода Dart никогда не выполняется. - person Timo Bähr   schedule 26.04.2019