gpt4 book ai didi

android - 从原生 Android 主屏幕小部件调用 Flutter (Dart) 代码

转载 作者:IT老高 更新时间:2023-10-28 12:30:50 25 4
gpt4 key购买 nike

我向我的 Flutter 应用程序添加了一个原生 Android 主屏幕小部件。

在我的 AppWidgetProvider 实现中,我想使用平台 channel 在 onUpdate() 方法中调用 dart 代码。

这可能吗?如果可以,如何实现?

我当前的 Android (Java) 代码:

package com.westy92.checkiday;

import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.util.Log;

import io.flutter.plugin.common.MethodChannel;
import io.flutter.view.FlutterNativeView;

public class HomeScreenWidget extends AppWidgetProvider {

private static final String TAG = "HomeScreenWidget";
private static final String CHANNEL = "com.westy92.checkiday/widget";

private static FlutterNativeView backgroundFlutterView = null;
private static MethodChannel channel = null;

@Override
public void onEnabled(Context context) {
Log.i(TAG, "onEnabled!");
backgroundFlutterView = new FlutterNativeView(context, true);
channel = new MethodChannel(backgroundFlutterView, CHANNEL);
}

@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
Log.i(TAG, "onUpdate!");
if (channel != null) {
Log.i(TAG, "channel not null, invoking dart method!");
channel.invokeMethod("foo", "extraJunk");
Log.i(TAG, "after invoke dart method!");
}
}
}

Dart 代码:

void main() {
runApp(Checkiday());
}

class Checkiday extends StatefulWidget {
@override
_CheckidayState createState() => _CheckidayState();
}

class _CheckidayState extends State<Checkiday> {
static const MethodChannel platform = MethodChannel('com.westy92.checkiday/widget');

@override
void initState() {
super.initState();
platform.setMethodCallHandler(nativeMethodCallHandler);
}

Future<dynamic> nativeMethodCallHandler(MethodCall methodCall) async {
print('Native call!');
switch (methodCall.method) {
case 'foo':
return 'some string';
default:
// todo - throw not implemented
}
}

@override
Widget build(BuildContext context) {
// ...
}
}

当我将小部件添加到主屏幕时,我看到:

I/HomeScreenWidget(10999): onEnabled!
I/HomeScreenWidget(10999): onUpdate!
I/HomeScreenWidget(10999): channel not null, invoking dart method!
I/HomeScreenWidget(10999): after invoke dart method!

但是,我的 dart 代码似乎没有收到调用。

最佳答案

我还需要一些原生的 android 小部件来与我的 dart 代码进行通信,经过一些修补后,我设法做到了。在我看来,关于如何做到这一点的文档有点稀少,但我凭借一点创造力设法让它发挥作用。我还没有做足够的测试来称这个 100% 生产就绪,但它似乎正在工作......

Dart 设置

转到 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());
}

这将确保我们可以在 native 端为我们的入口点获取回调句柄。

现在像这样添加一个入口点:

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)

// Instantiate a FlutterEngine.
val engine = FlutterEngine(context.applicationContext)
val callback = DartExecutor.DartCallback(context.assets, loader.findAppBundlePath(), callbackInfo)
engine.dartExecutor.executeDartCallback(callback)

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 中触发回调前面定义的 Dart 端。然后我们稍后在 success 中处理结果(至少在调用成功时)。

希望这能让您大致了解如何实现这一目标...

关于android - 从原生 Android 主屏幕小部件调用 Flutter (Dart) 代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53940400/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com