在Flutter项目中开发IOS桌面组件(WidgetExtension)

具体的WidgetExtension的开发流程这里就不细说了,可以参考文末的链接。

在Flutter项目开发IOSWidget的过程中,主要的问题有:

  • App和Widget的数据共享
  • 点击Widget跳转App的指定界面
  • 在App界面编辑并更新Widget数据
App和Widget数据共享

数据共享使用的是UserDefaults,前提是需要为WidgetExtension和Runner添加相同的AppGroup。添加AppGroup的方法为:

Runner -> Target -> Runner -> Signing&Capabilities -> AppGroups -> +

这里如果没有AppGroups可以XCode点击右上角的+号来添加AppGroups。


WidgetExtension添加方法同上,其中AppGroup要和Runner的相同。

UserDefaults的使用

这里以实际的例子为大家展示UserDefaults的使用。为了方便演示,在App启动时保存相关数据,以供小组件进行读取。

// 以下代码在AppDelegate.swift中的Application方法中
// suitName: 为上面添加的AppGroup
let userDefaults = UserDefaults.init(suiteName: "group.com.cc.ToDo")
userDefaults!.setValue("defaultID", forKey: "id")
userDefaults!.setValue("defauleName", forKey: "name")

在小组件中读取

func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> Void) {print("start getTimeline")let userDefaults = UserDefaults(suiteName: "group.com.cc.ToDo")let id = userDefaults?.string(forKey: "id")let name = userDefaults?.string(forKey: "name")print("timeline:  \(id!) \(name!)")// ... 这里省略了后续的completion
}
点击Widget跳转App的指定界面

在小组件中处理点击跳转主要有两种方法:

  • widgetURL:作用于整个小组件,且一个小组件只能有一个
  • Link:作用于Link包裹的组件的大小,在小尺寸[systemSmall]组件中无法使用Link

可以根据实际情况选择合适的组件。

URL Schemes

URL Schemes主要负责处理跳转逻辑,通过配置URL Schemes,在App中捕获对应的url和参数来实现跳转指定页。
注册URL Schemes主要包含以下几步:

Runner -> Info -> URL Types -> 添加+ -> 编辑URL Schemes

完成之后可以再widgetURL中添加url(以上述配置的URL Schemes开头),代码如下:

var body: some View{VStack{Text("ToDoList")Text(entry.userid)Text(entry.author)}// URL以配置的URL Schemes开头,可以拼接参数.widgetURL(URL(string: "dynamictheme://user?userid=\(entry.userid)&author=\(entry.author)"))
}
// 在Link中配置的URL同此

Flutter端则使用uni_links库来进行链接捕获和跳转,具体实现如下:

import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:uni_links/uni_links.dart';
import 'pages/UserPage.dart';GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();class MyApp extends StatefulWidget {@override_MyAppState createState() => _MyAppState();
}class _MyAppState extends State<MyApp> {StreamSubscription<String> _sub;@overridevoid initState() {super.initState();initPlatformStateForStringUniLinks();}Future<void> initPlatformStateForStringUniLinks() async {String initialLink;// App未打开的状态在这个地方捕获schemetry {initialLink = await getInitialLink();print('跳转地址: $initialLink');if (initialLink != null) {print('跳转地址不为null --$initialLink');//  跳转到指定页面schemeJump(context, initialLink);}} on PlatformException {initialLink = 'Failed to get initial link.';} on FormatException {initialLink = 'Failed to parse the initial link as Uri.';}// App打开的状态监听scheme_sub = getLinksStream().listen((String link) {if (!mounted || link == null) return;print('link--$link');//  跳转到指定页面schemeJump(context, link);}, onError: (Object err) {if (!mounted) return;});}void schemeJump(BuildContext context, String schemeUrl) {final Uri _jumpUri = Uri.parse(schemeUrl.replaceFirst('dynamictheme://','http://path/',));switch (_jumpUri.path) {case '/user':print("接收到的参数为:");String userid = _jumpUri.queryParameters["userid"];print(userid);String author = _jumpUri.queryParameters["author"];print(author);Navigator.of(navigatorKey.currentContext).push(CupertinoPageRoute(builder: (context) => UserPage(userid: userid,author: author,)));break;default:break;}}@overridevoid dispose() {super.dispose();_sub.cancel();}@overrideWidget build(BuildContext context) {return MaterialApp(navigatorKey: navigatorKey,title: 'Flutter 与 IOS',theme:ThemeData(primarySwatch: Colors.blue, platform: TargetPlatform.iOS),home: HomePage(),);}
}class HomePage extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("HomePage")),body: Center(child: Text("Home page"),),);}
}
在App界面编辑并更新Widget数据

在App编辑数据并更新widget功能中,通过MenthodChannel来实现。当编辑完数据,需要更新时,通过MethodChannel来调用原生方法,在原生方法中更新UserDefaults的数据,并返回结果给Flutter端。

数据更新完成并不会刷新Widget,因为Widget中使用的是前一Timeline的快照,在下一个Timeline之前并不会刷新数据,因此需要主动调用相关方法来更新数据。

在原生端想要主动来更新小组件的Timeline,主要有两种方法:

  • WidgetCenter.shared.reloadAllTimelines(): 更新App下所有组件的Timelines
  • WidgetCenter.shared.reloadTimelines(ofKind: kind): 更新指定kind类型组件的Timelines

具体的实现如下可参考以下代码

Flutter端代码如下:

MethodChannel channel = MethodChannel("com.cc.ToDo.widgets");
var res = await channel.invokeMethod("updateWidgetData", {"userid":idController.text,"author":nameController.text
});print(res);
print(res.runtimeType);

Swift端代码如下:

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! FlutterViewControllerlet userDefaults = UserDefaults.init(suiteName: "group.com.cc.ToDo")userDefaults!.setValue("defaultID", forKey: "userid")userDefaults!.setValue("defauleName", forKey: "author")// 初始化MethodChannel,设置监听WidgetMenthod.init(messger: controller.binaryMessenger)GeneratedPluginRegistrant.register(with: self)return super.application(application, didFinishLaunchingWithOptions: launchOptions)}
}
// 处理Flutter调用
class WidgetMenthod{init(messger:FlutterBinaryMessenger){let channel = FlutterMethodChannel(name: "com.cc.ToDo.widgets", binaryMessenger: messger)channel.setMethodCallHandler{(call:FlutterMethodCall, result: @escaping FlutterResult) in// 通过call.method来判断要调用的方法if(call.method == "updateWidgetData"){// 通过call.arguments来获取参数if let dict = call.arguments as? Dictionary<String,Any>{let userid = dict["userid"] as? Stringlet author = dict["author"] as? Stringprint("\(userid) ==== \(author)")let userDefaults = UserDefaults.init(suiteName: "group.com.cc.ToDo")userDefaults!.setValue(userid, forKey: "userid")userDefaults!.setValue(author, forKey: "author")if #available(iOS 14.0, *) {print("reload timelines")WidgetCenter.shared.reloadTimelines(ofKind: "todo_list")print("reload complete!")result(["code":1,"msg":"success"])} else {result(["code":0,"msg":"系统版本过低"])}}else{result(["code":0,"msg":"参数异常"])}}}}
}

至此,在Flutter项目中开发IOS桌面组件就全部完成了。

完整案例源码点此下载

参考文章

网易云音乐 iOS 14 小组件实战手册

[【Flutter 混合开发】与原生通信-MethodChannel](

在Flutter项目中开发IOS桌面组件(WidgetExtension)相关推荐

  1. flutter ios打包_使用 Travis CI 为 Flutter 项目打包 Android/iOS 应用

    Travis CI 构建 Building Flutter APKs and IPAs on Travis 这篇文章详细介绍了如何在 Travis CI 上为 Flutter 项目打包 Android ...

  2. vue项目中element-ui的分页器(组件封装)

    vue项目中element-ui的分页器组件 <template><el-col :span="24" class="toolbar" sty ...

  3. 【Flutter】Flutter 项目中使用 Flutter 插件 ( Flutter 插件管理平台 | 搜索 Flutter 插件 | 安装 Flutter 插件 | 使用 Flutter 插件 )

    文章目录 一.Flutter 包和插件管理平台 二.Flutter 插件搜索示例 三.Flutter 插件装示例 1.添加 Dart 包依赖 2.获取 Dart 包 3.使用 Dart 包 4.官方的 ...

  4. Effective Dart 文档注释在Flutter项目中的实践

    前言 什么是注释? 在编程语言中,注释就是对代码的解释和说明,其目的是让人们能够更加轻松地了解代码. 也有一句话是这样说的:程序员都讨厌两件事,1.别人不写注释 2.自己写注释 在开发者社区里,我不止 ...

  5. java项目中可以封装的组件_封装属于自己的组件库

    封装属于自己的组件库 - VUE 「适合人群:一起聊聊组件这件事」 「观看时间:30min」 「说明:一些经验分享,欢迎探讨」 前言 现目前,vue 社区下大大小小的组件库是越来越多,优秀的开源组件也 ...

  6. 不用mac环境,如何使用Visual Studio在Windows中开发iOS应用

    Xamarin允许开发者使用C#来开发Android和iOS应用.在Xamarin的基础上,微软又进一步推出了MAUI.早期的时候,使用Visual Studio开发iOS应用必须远程连接mac,也就 ...

  7. uni-app项目中引入Vant UI组件库(完美避坑!!!)纯净版

    文章目录 第一步,HBuilder X中新建一个uni-app的项目 第二步,HBuilder X提示项目创建成功,在HBuilder X用命令行窗口打开创建的项目(或直接在创建的项目文件夹内用cmd ...

  8. mpvue项目中使用第三方UI组件库

    原文链接 最新声明 本文编写时间较早,随着时间的推移,mpvue及其周边生态越来越完善,文中的案例已经不适合在项目使用.希望大家参考其他更新的文章~ 简介 微信小程序上线已有一年多时间啦,自美团的mp ...

  9. vue3.0项目中使用mitt进行组件传值

    一.安装依赖mitt npm install mitt 二.导出模块mitt 在src目录下创建utils文件夹,在该文件夹下创建mybus.ts文件 import mitt from 'mitt'; ...

最新文章

  1. 使用alterMIME实现添加message footer功能
  2. 一文搞懂 CountDownLatch 用法和源码!
  3. Python 异常处理--raise函数用法
  4. Visual Studio 2005 Beta 2 will ship on April 25th
  5. linux基础命令_Linux命令基础大全
  6. 8.3 直接插入排序
  7. 一些 Google 搜索词
  8. jQuery-对Select的操作集合
  9. 简单的FTP应用程序
  10. FZU 1894 志愿者选拔
  11. 三分钟看懂一致性哈希算法
  12. 一个空格引发的“惨案“
  13. javascript绘制静态或者动态的图表、关系表、流程图-JointJS
  14. 根据后台返回地址实现图片展示_微信小程序 - 前端接入七牛云上传图片和视频...
  15. 原生js--类、原型、构造函数
  16. 微信公众号软件安装管家所有软件插件打包
  17. Typora中插入分页符
  18. minio权限之IAM policy配置及用户赋权
  19. C语言:利用指针编写程序,将一个一维数组a[10]中的最大元素与第一个元素互换、将最小元素与最后一个元素互换
  20. Linux 2.6 CFS 调度算法内幕

热门文章

  1. 信息收集之cms指纹识别
  2. 打造生产数据闭环,提高质量管控能力
  3. Burpsuite 工具(pikachu测试暴力破解)
  4. Apple iPhone热销的背后
  5. (附源码)计算机毕业设计ssm党建系统
  6. 无线风力报警仪塔吊安全助手使用的意义
  7. 神州信息农民合作社管理解决方案
  8. 虚拟服务器 规则名称,虚拟主机伪静态规则怎么添加
  9. 多屏互动——H5中级进阶 - 修复动态图片版本
  10. 人工智能赢了,人类输了