如何在Flutter开发的iOS应用中创建可自定义的iOS 14小组件?
Flutter iOS 应用接入WidgetKit(支持API数据拉取)实现方案
一、前期基础配置
- 打开Xcode中的Flutter iOS项目,分别给主应用
Runnertarget和后续创建的Widget Extension target添加App Group能力,组名统一使用group.你的应用BundleID.widget,两个target的App Group ID必须完全一致,否则无法读取共享数据。 - 在Xcode中依次点击
File > New > Target,选择Widget Extension,自定义组件名称(例如MyAppWidget),无需勾选Include Configuration Intent(应用内自定义场景不需要系统级编辑配置)。
二、核心数据互通实现
1. Flutter侧逻辑
首先实现API数据拉取、存储到共享容器、触发组件刷新的逻辑:
- 使用
app_group_directory依赖获取共享容器路径,将API返回的组件所需数据(标题、内容、更新时间等)序列化为JSON格式,写入共享目录下的固定文件(例如widget_data.json) - 通过MethodChannel调用原生方法触发组件刷新,示例代码:
const MethodChannel _channel = MethodChannel('com.yourapp/widget_control'); // 数据更新后调用此方法刷新小组件 Future<void> refreshWidget() async { try { await _channel.invokeMethod('refresh'); } catch (e) { // 错误处理逻辑 } }
2. iOS主应用桥接逻辑
在Runner项目的AppDelegate.swift中注册MethodChannel,接收Flutter侧的刷新请求:
import UIKit import Flutter import WidgetKit @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { let controller : FlutterViewController = window?.rootViewController as! FlutterViewController let widgetChannel = FlutterMethodChannel(name: "com.yourapp/widget_control", binaryMessenger: controller.binaryMessenger) widgetChannel.setMethodCallHandler({ (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in if call.method == "refresh" { WidgetCenter.shared.reloadAllTimelines() result(nil) } else { result(FlutterMethodNotImplemented) } }) GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } }
3. Widget Extension侧逻辑
Widget侧支持两种数据获取方式,可按需选择:
方式1:读取Flutter侧存入共享容器的预拉取数据
// 定义数据模型 struct WidgetData: Codable { let title: String let content: String let updateTime: Date } // 读取共享容器数据 func getSharedData() -> WidgetData? { let groupID = "group.你的应用BundleID.widget" guard let sharedDir = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: groupID) else { return nil } let dataFile = sharedDir.appendingPathComponent("widget_data.json") do { let data = try Data(contentsOf: dataFile) return try JSONDecoder().decode(WidgetData.self, from: data) } catch { return nil } }
在TimelineProvider的getTimeline方法中调用上述方法获取数据,生成渲染用的Timeline即可。
方式2:Widget侧直接拉取API数据
如果需要组件独立更新,可直接在getTimeline中发起网络请求:
func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) { let currentDate = Date() let refreshDate = Calendar.current.date(byAdding: .minute, value: 15, to: currentDate)! // 15分钟后自动刷新 // 发起API请求 URLSession.shared.dataTask(with: URL(string: "你的API地址")!) { data, response, error in guard let data = data, error == nil else { // 异常处理,返回占位数据 let entry = WidgetEntry(date: currentDate, data: placeholderData) let timeline = Timeline(entries: [entry], policy: .after(refreshDate)) completion(timeline) return } if let apiData = try? JSONDecoder().decode(WidgetData.self, from: data) { let entry = WidgetEntry(date: currentDate, data: apiData) let timeline = Timeline(entries: [entry], policy: .after(refreshDate)) completion(timeline) } }.resume() }
三、应用内自定义小组件实现
Flutter侧搭建自定义配置页面,支持用户选择组件样式、展示内容等,将配置参数序列化后和业务数据一起存入共享容器,调用刷新方法后,Widget侧读取配置参数渲染对应样式即可。
注意事项
- Widget Extension最低支持iOS 14,仅需将Widget target的
iOS Deployment Target设为14.0及以上即可,不影响主应用的最低兼容版本 - 小组件内存上限为30M,不要在组件内执行过重逻辑,网络请求超时时间建议不超过5秒
- 系统对小组件刷新频率有限制,每日自动刷新上限约为40-70次,不要高频调用刷新接口避免被系统限流
内容的提问来源于stack exchange,提问作者Genti
相关产品推荐
相关产品推荐

