IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 在Flutter项目中开发IOS桌面组件(WidgetExtension) -> 正文阅读

[移动开发]在Flutter项目中开发IOS桌面组件(WidgetExtension)

作者:AppWidget_194

在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。

AppGroup.jpg
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的指定界面

在小组件点击跳转App这块,本次使用的技术为widgetURL以及URL Schemes。

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

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

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

URL Schemes

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

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

URLSchemes.jpg

完成之后可以再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;
  @override
  void initState() {
    super.initState();
    initPlatformStateForStringUniLinks();
  }

  Future<void> initPlatformStateForStringUniLinks() async {
    String initialLink;
    // App未打开的状态在这个地方捕获scheme
    try {
      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;
    }
  }

  @override
  void dispose() {
    super.dispose();
    _sub.cancel();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      navigatorKey: navigatorKey,
      title: 'Flutter 与 IOS',
      theme:
      ThemeData(primarySwatch: Colors.blue, platform: TargetPlatform.iOS),
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget 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! FlutterViewController
    
    let 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? String
                    let author = dict["author"] as? String
                    print("\(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](

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-07-24 11:36:36  更:2021-07-24 11:38:01 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/1 22:28:40-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码