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

如何在SwiftUI Widget中访问Flutter存储的shared_preferences?

Flutter与SwiftUI Widget共享shared_preferences数据解决方案

核心问题说明

Flutter默认的shared_preferences会将数据存储在**主应用私有UserDefaults**中,而WidgetKit的Widget是独立进程,无法直接访问主应用的私有存储。必须通过App Group实现跨进程数据共享,同时需要修改Flutter端的存储逻辑,将数据写入App Group的UserDefaults。


第一步:配置App Group(主应用+Widget)

  1. 在Xcode中打开Flutter项目的iOS工程,分别给主应用Runner target和Widget target添加App Groups能力:
    • 进入Signing & Capabilities页面,点击+ Capability,选择App Groups
    • 创建或勾选同一个App Group ID(格式如group.com.yourcompany.yourapp)
  2. 在主应用的ios/Runner/Info.plist中添加App Group ID配置(方便后续代码读取):
<key>AppGroupID</key>
<string>group.com.yourcompany.yourapp</string>
  1. 同样在Widget的Info.plist中添加相同的AppGroupID配置。

第二步:修改Flutter端,将数据写入App Group

默认的shared_preferences不支持写入App Group,需要通过MethodChannel调用原生代码实现:

Flutter端代码

创建工具类封装调用:

import 'package:flutter/services.dart';

class AppGroupPrefs {
  static const _channel = MethodChannel('app_group_prefs');

  static Future<void> saveString(String key, String value) async {
    await _channel.invokeMethod('saveString', {'key': key, 'value': value});
  }

  static Future<String?> getString(String key) async {
    return await _channel.invokeMethod('getString', {'key': key});
  }
}

之后替换原有的shared_preferences调用,用AppGroupPrefs.saveString()来存储需要共享的数据。

iOS原生端代码

在Runner/AppDelegate.swift中注册MethodChannel处理逻辑:

import UIKit
import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    let controller = window?.rootViewController as! FlutterViewController
    let channel = FlutterMethodChannel(name: "app_group_prefs", binaryMessenger: controller.binaryMessenger)
    
    channel.setMethodCallHandler { [weak self] call, result in
      guard let groupID = Bundle.main.object(forInfoDictionaryKey: "AppGroupID") as? String else {
        result(FlutterError(code: "NO_GROUP", message: "App Group ID未配置", details: nil))
        return
      }
      let defaults = UserDefaults(suiteName: groupID)
      
      switch call.method {
      case "saveString":
        guard let args = call.arguments as? [String: Any],
              let key = args["key"] as? String,
              let value = args["value"] as? String else {
          result(FlutterError(code: "INVALID_ARGS", message: "参数错误", details: nil))
          return
        }
        defaults?.set(value, forKey: key)
        result(nil)
      case "getString":
        guard let args = call.arguments as? [String: Any],
              let key = args["key"] as? String else {
          result(FlutterError(code: "INVALID_ARGS", message: "参数错误", details: nil))
          return
        }
        result(defaults?.string(forKey: key))
      default:
        result(FlutterMethodNotImplemented)
      }
    }
    
    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

第三步:Widget端读取App Group数据

在Widget的TimelineProvider中修改读取逻辑:

struct MyWidgetProvider: TimelineProvider {
  // ... 其他实现代码 ...
  
  func getTimeline(in context: Context, completion: @escaping (Timeline<MyWidgetEntry>) -> Void) {
    guard let groupID = Bundle.main.object(forInfoDictionaryKey: "AppGroupID") as? String else {
      let entry = MyWidgetEntry(date: Date(), displayValue: "配置错误")
      let timeline = Timeline(entries: [entry], policy: .atEnd)
      completion(timeline)
      return
    }
    
    let defaults = UserDefaults(suiteName: groupID)
    // 注意这里的key要和Flutter端存储的完全一致,如果是原shared_preferences的key,会带"flutter."前缀
    let targetValue = defaults?.string(forKey: "flutter.KEY") ?? "无数据"
    
    let entry = MyWidgetEntry(date: Date(), displayValue: targetValue)
    // 设置刷新间隔,比如1分钟
    let timeline = Timeline(entries: [entry], policy: .after(Date().addingTimeInterval(60)))
    completion(timeline)
  }
}

排查要点

  • 检查App Group ID是否完全一致:主应用和Widget的App Group ID必须完全相同,大小写、拼写都不能错
  • 验证数据是否写入App Group:可以用Xcode的调试工具查看App Group的UserDefaults,或者在Flutter调用存储后,用lldb命令po UserDefaults(suiteName: "你的group ID")?.dictionaryRepresentation()确认数据存在
  • 避免混用私有存储:不要尝试用默认UserDefaults读取主应用数据,Widget无法访问主应用私有存储
  • 确认key匹配:如果是迁移原有shared_preferences的数据,注意原key带有flutter.前缀,自定义存储则用你传入的key

内容的提问来源于stack exchange,提问作者Taha Malik

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 21:45:45