You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Flutter开发的iOS应用中创建可自定义的iOS 14小组件?

Flutter iOS 应用接入WidgetKit(支持API数据拉取)实现方案

一、前期基础配置

  • 打开Xcode中的Flutter iOS项目,分别给主应用Runner target和后续创建的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 19:09:01