首页 > 解决方案 > 将数据从 Flutter 传递到 SwiftUI 主屏幕 WidgetExtension?访问 SQLite DB 本身还是使用方法通道?

问题描述

我有一个使用 SQFLite 来持久化数据的颤振应用程序。我有兴趣尝试做一个 iOS 主屏幕小部件,据我所知,它必须在 swiftui 中构建。我想知道是否可以从 Swiftui 中的本机小部件访问我的颤振 sqlite 数据库?

更新:尝试使用以下建议但遇到问题。由于如果我将 appdelegate 模块添加为共享目标,小部件扩展似乎会导致 xcode 构建失败......我试图将它跳到一个快速文件调用然后共享它。但我遇到了错误。

我的 Flutter 助手(它目前只是试图从列表中发送一个整数或多或少作为概念证明)

import 'package:flutter/services.dart';
import 'dart:async';

import 'db.dart';


final channelName = 'widgetDataCheckItOff';

Future<void> _handleMethod(MethodCall call) async {
  final String utterance = call.arguments;
  switch(call.method) {
    case "getDataFromFlutter":
      var db = new DB();
      dynamic result = await db.widgetQuery();
      return result;
  }
  final methodChannel = MethodChannel(channelName);
  methodChannel.setMethodCallHandler(_handleMethod);

}

应用委托

import UIKit
import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    var controller : FlutterViewController!;
    var widgetDataCheckItOff : FlutterMethodChannel!;
    class func shared() -> AppDelegate
    {
        return UIApplication.shared.delegate as! AppDelegate
    }
    
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        controller = window?.rootViewController as? FlutterViewController;
        widgetDataCheckItOff = FlutterMethodChannel (
            name : "widgetDataCheckItOff",
            binaryMessenger : controller as! FlutterBinaryMessenger
        )
        
        GeneratedPluginRegistrant.register(with: self)
        if #available(iOS 10.0, *) { UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate }
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
    @available(iOS 9.0, *)
    override func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
        let controller = window.rootViewController as? FlutterViewController
        
        let channel = FlutterMethodChannel(name: "plugins.flutter.io/quick_actions", binaryMessenger: controller! as! FlutterBinaryMessenger)
        channel.invokeMethod("launch", arguments: shortcutItem.type)
    }
    public func getTasks ( args: String, completion:@escaping (Int) -> Int) {
        var x = -1
        widgetDataCheckItOff.setMethodCallHandler { (call : FlutterMethodCall, result: @escaping FlutterResult) in
            //        print("set method call handler")
            if(call.method == "getDataFromFlutter") {
                self.widgetDataCheckItOff.invokeMethod ("getDataFromFlutter", arguments: args, result: {(r:Any?) -> () in
                    x=r as! Int
                });
            }
            else{
                result(FlutterMethodNotImplemented)
                x = -1// to handle undeclared methods
            }
            let result = x
            completion(result)
        }
    }
    
}

跳文件我称为widgetSwift

import Foundation

@objc class WidgetData: NSObject{
    public func getWidgetData(result: Int)-> Int {
        var x = -1
        let a = UIApplication.shared.delegate as! AppDelegate
        a.getTasks(args: "getDataFromFlutter",  completion: {(result) -> Int in
            x = result
        })
        return x
//        let x = AppDelegate.getTasks(<#T##self: AppDelegate##AppDelegate#>);
        
    }
}

和我的小部件扩展部分调用它

struct widget_extension_widgetEntryView : View {
    var entry: Provider.Entry

       var body: some View {
        let w = WidgetData()
        let x: Int =         w.getWidgetData(result: <#Int#>)
        
        let str = "Today's Tasks:  \(x)"
        Text("", tableName: str)
       }
}

扩展给我函数调用的错误:源文件中的编辑器占位符。

widgetSwift 在 getTasks 调用时给了我错误:在范围内找不到“AppDelegate”声明的闭包结果“()”与上下文类型“Int”不兼容

标签: flutterswiftuisqflite

解决方案


Flutter 提供了一种使用MethodChannel.

从颤振MethodChannel文档(这里):

Flutter 使用灵活的系统,允许您调用特定于平台的 API,无论是 Android 上的 Kotlin 或 Java 代码,还是 iOS 上的 Swift 或 Objective-C 代码。

注意:如果需要,方法调用也可以反向发送,平台充当 Dart 中实现的方法的客户端。一个具体的例子是quick_actions插件。

MethodChannel从颤振调用本机平台代码上的函数的用法:

我们在 Flutter、iOS 和 Android 中创建一个同名的方法通道对象。Android 和 iOS 对象将设置一个方法调用处理程序来接收来自 Flutter 的调用。然后 Flutter 代码可以调用 invokeMethod 来调用原生对象上的处理程序

扑:

static final channelName = 'com.example.widget/database';
final methodChannel = MethodChannel(channelName);

await this.methodChannel.invokeMethod("getDataFromDatabase");

安卓:

import androidx.annotation.NonNull;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.MethodChannel;

public class MainActivity extends FlutterActivity {
  private static final String CHANNEL = "com.example.widget/database";

  @Override
  public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
  super.configureFlutterEngine(flutterEngine);
    new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
        .setMethodCallHandler(
          (call, result) -> {
            if (call.method.equals("getDataFromDatabase")) {
              //TODO: Perform method to query database here
              String data;

              if (data != null) {
                result.success(data);
              } else {
                result.error("UNAVAILABLE", "Data not available", null);
              }
            } else {
              result.notImplemented();
            }
          }
        );
  }
}

IOS:

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

    let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
    let batteryChannel = FlutterMethodChannel(name: "com.example.widget/database",
                                              binaryMessenger: controller.binaryMessenger)
    batteryChannel.setMethodCallHandler({
      (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
      // Note: this method is invoked on the UI thread.
      guard call.method == "getDataFromDatabase" else {
         result(FlutterMethodNotImplemented)
         return
      }

      //TODO: Perform method to query database here
      let data;

      if data == nil {
         result(FlutterError(code: "UNAVAILABLE",
                        message: "Data not available",
                        details: nil))
      } else {
         result(data)
      }     
    })

    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

逆向示例——从 Native 调用 Flutter 如果您需要从 Native 代码调用 Flutter 中定义的方法,那么您调用之前的相同方法,但这次您setMethodCallHandler在 Flutter 端和invokeMethodNative 端调用。

扑:

final channelName = 'dataFromFlutterChannel';

final methodChannel = MethodChannel(channelName);
methodChannel.setMethodCallHandler(this._handleMethod);

Future<void> _handleMethod(MethodCall call) async {
   final String utterance = call.arguments; 
   switch(call.method) {
     case "getDataFromFlutter":
        //TODO: Query database here and return the data to native side
   }
}

安卓:

val channelName = 'dataFromFlutterChannel'
val methodChannel = MethodChannel(flutterView, channelName)
    
methodChannel.invokeMethod("getDataFromFlutter", utterance)

IOS:

let rootViewController : FlutterViewController = window?.rootViewController as! FlutterViewController
let channelName = "dataFromFlutterChannel"
let methodChannel = FlutterEventChannel(name: channelName, binaryMessenger: rootViewController)
    
methodChannel.invokeMethod("getDataFromFlutter", utterance)

本文可以帮助您进行更详细的解释。


推荐阅读