From 06e385fcb8f65c8cda64f5ba9a5ba4846baf08c6 Mon Sep 17 00:00:00 2001 From: Zubii Date: Thu, 9 Jul 2026 23:21:38 +0300 Subject: [PATCH 1/4] feat(quick_actions): add support for iOS SF Symbols icons --- .../QuickActionsPlugin.swift | 7 +- .../quick_actions_ios/messages.g.swift | 133 ++++++++++---- .../quick_actions_ios/lib/messages.g.dart | 163 +++++++++++------- .../lib/quick_actions_ios.dart | 1 + .../quick_actions_ios/pigeons/messages.dart | 11 +- .../method_channel_quick_actions.dart | 1 + .../lib/types/shortcut_item.dart | 4 + 7 files changed, 225 insertions(+), 95 deletions(-) diff --git a/packages/quick_actions/quick_actions_ios/ios/quick_actions_ios/Sources/quick_actions_ios/QuickActionsPlugin.swift b/packages/quick_actions/quick_actions_ios/ios/quick_actions_ios/Sources/quick_actions_ios/QuickActionsPlugin.swift index 0ff0980c81d5..4125cbaa4774 100644 --- a/packages/quick_actions/quick_actions_ios/ios/quick_actions_ios/Sources/quick_actions_ios/QuickActionsPlugin.swift +++ b/packages/quick_actions/quick_actions_ios/ios/quick_actions_ios/Sources/quick_actions_ios/QuickActionsPlugin.swift @@ -133,9 +133,12 @@ public final class QuickActionsPlugin: NSObject, FlutterPlugin, IOSQuickActionsA ) -> UIApplicationShortcutItem? { + var icon: UIApplicationShortcutIcon? = nil - let icon = (shortcut.icon).map { - UIApplicationShortcutIcon(templateImageName: $0) + if let systemImageName = shortcut.iosSystemIcon { + icon = UIApplicationShortcutIcon(systemImageName: systemImageName) + } else if let templateImageName = shortcut.icon { + icon = UIApplicationShortcutIcon(templateImageName: templateImageName) } // type and localizedTitle are required. diff --git a/packages/quick_actions/quick_actions_ios/ios/quick_actions_ios/Sources/quick_actions_ios/messages.g.swift b/packages/quick_actions/quick_actions_ios/ios/quick_actions_ios/Sources/quick_actions_ios/messages.g.swift index 932da049f3f7..704033ba4b60 100644 --- a/packages/quick_actions/quick_actions_ios/ios/quick_actions_ios/Sources/quick_actions_ios/messages.g.swift +++ b/packages/quick_actions/quick_actions_ios/ios/quick_actions_ios/Sources/quick_actions_ios/messages.g.swift @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v26.1.0), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon import Foundation @@ -53,7 +53,7 @@ private func wrapError(_ error: Any) -> [Any?] { } return [ "\(error)", - "\(type(of: error))", + "\(Swift.type(of: error))", "Stacktrace: \(Thread.callStackSymbols)", ] } @@ -73,6 +73,19 @@ private func nilOrValue(_ value: Any?) -> T? { return value as! T? } +private func doubleEqualsmessages(_ lhs: Double, _ rhs: Double) -> Bool { + return (lhs.isNaN && rhs.isNaN) || lhs == rhs +} + +private func doubleHashmessages(_ value: Double, _ hasher: inout Hasher) { + if value.isNaN { + hasher.combine(0x7FF8_0000_0000_0000) + } else { + // Normalize -0.0 to 0.0 + hasher.combine(value == 0 ? 0 : value) + } +} + func deepEqualsmessages(_ lhs: Any?, _ rhs: Any?) -> Bool { let cleanLhs = nilOrValue(lhs) as Any? let cleanRhs = nilOrValue(rhs) as Any? @@ -83,56 +96,90 @@ func deepEqualsmessages(_ lhs: Any?, _ rhs: Any?) -> Bool { case (nil, _), (_, nil): return false - case is (Void, Void): + case (let lhs as AnyObject, let rhs as AnyObject) where lhs === rhs: return true - case let (cleanLhsHashable, cleanRhsHashable) as (AnyHashable, AnyHashable): - return cleanLhsHashable == cleanRhsHashable + case is (Void, Void): + return true - case let (cleanLhsArray, cleanRhsArray) as ([Any?], [Any?]): - guard cleanLhsArray.count == cleanRhsArray.count else { return false } - for (index, element) in cleanLhsArray.enumerated() { - if !deepEqualsmessages(element, cleanRhsArray[index]) { + case (let lhsArray, let rhsArray) as ([Any?], [Any?]): + guard lhsArray.count == rhsArray.count else { return false } + for (index, element) in lhsArray.enumerated() { + if !deepEqualsmessages(element, rhsArray[index]) { return false } } return true - case let (cleanLhsDictionary, cleanRhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]): - guard cleanLhsDictionary.count == cleanRhsDictionary.count else { return false } - for (key, cleanLhsValue) in cleanLhsDictionary { - guard cleanRhsDictionary.index(forKey: key) != nil else { return false } - if !deepEqualsmessages(cleanLhsValue, cleanRhsDictionary[key]!) { + case (let lhsArray, let rhsArray) as ([Double], [Double]): + guard lhsArray.count == rhsArray.count else { return false } + for (index, element) in lhsArray.enumerated() { + if !doubleEqualsmessages(element, rhsArray[index]) { return false } } return true + case (let lhsDictionary, let rhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]): + guard lhsDictionary.count == rhsDictionary.count else { return false } + for (lhsKey, lhsValue) in lhsDictionary { + var found = false + for (rhsKey, rhsValue) in rhsDictionary { + if deepEqualsmessages(lhsKey, rhsKey) { + if deepEqualsmessages(lhsValue, rhsValue) { + found = true + break + } else { + return false + } + } + } + if !found { return false } + } + return true + + case (let lhs as Double, let rhs as Double): + return doubleEqualsmessages(lhs, rhs) + + case (let lhsHashable, let rhsHashable) as (AnyHashable, AnyHashable): + return lhsHashable == rhsHashable + default: - // Any other type shouldn't be able to be used with pigeon. File an issue if you find this to be untrue. return false } } func deepHashmessages(value: Any?, hasher: inout Hasher) { - if let valueList = value as? [AnyHashable] { - for item in valueList { deepHashmessages(value: item, hasher: &hasher) } - return - } - - if let valueDict = value as? [AnyHashable: AnyHashable] { - for key in valueDict.keys { - hasher.combine(key) - deepHashmessages(value: valueDict[key]!, hasher: &hasher) + let cleanValue = nilOrValue(value) as Any? + if let cleanValue = cleanValue { + if let doubleValue = cleanValue as? Double { + doubleHashmessages(doubleValue, &hasher) + } else if let valueList = cleanValue as? [Any?] { + for item in valueList { + deepHashmessages(value: item, hasher: &hasher) + } + } else if let valueList = cleanValue as? [Double] { + for item in valueList { + doubleHashmessages(item, &hasher) + } + } else if let valueDict = cleanValue as? [AnyHashable: Any?] { + var result = 0 + for (key, value) in valueDict { + var entryKeyHasher = Hasher() + deepHashmessages(value: key, hasher: &entryKeyHasher) + var entryValueHasher = Hasher() + deepHashmessages(value: value, hasher: &entryValueHasher) + result = result &+ ((entryKeyHasher.finalize() &* 31) ^ entryValueHasher.finalize()) + } + hasher.combine(result) + } else if let hashableValue = cleanValue as? AnyHashable { + hasher.combine(hashableValue) + } else { + hasher.combine(String(describing: cleanValue)) } - return - } - - if let hashableValue = value as? AnyHashable { - hasher.combine(hashableValue.hashValue) + } else { + hasher.combine(0) } - - return hasher.combine(String(describing: value)) } /// Home screen quick-action shortcut item. @@ -147,6 +194,8 @@ struct ShortcutItemMessage: Hashable { var localizedSubtitle: String? = nil /// Name of native resource to be displayed as the icon for this item. var icon: String? = nil + /// The name of the SF Symbol to be displayed as the icon for this item on iOS. + var iosSystemIcon: String? = nil // swift-format-ignore: AlwaysUseLowerCamelCase static func fromList(_ pigeonVar_list: [Any?]) -> ShortcutItemMessage? { @@ -154,12 +203,14 @@ struct ShortcutItemMessage: Hashable { let localizedTitle = pigeonVar_list[1] as! String let localizedSubtitle: String? = nilOrValue(pigeonVar_list[2]) let icon: String? = nilOrValue(pigeonVar_list[3]) + let iosSystemIcon: String? = nilOrValue(pigeonVar_list[4]) return ShortcutItemMessage( type: type, localizedTitle: localizedTitle, localizedSubtitle: localizedSubtitle, - icon: icon + icon: icon, + iosSystemIcon: iosSystemIcon ) } func toList() -> [Any?] { @@ -168,13 +219,27 @@ struct ShortcutItemMessage: Hashable { localizedTitle, localizedSubtitle, icon, + iosSystemIcon, ] } static func == (lhs: ShortcutItemMessage, rhs: ShortcutItemMessage) -> Bool { - return deepEqualsmessages(lhs.toList(), rhs.toList()) + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsmessages(lhs.type, rhs.type) + && deepEqualsmessages(lhs.localizedTitle, rhs.localizedTitle) + && deepEqualsmessages(lhs.localizedSubtitle, rhs.localizedSubtitle) + && deepEqualsmessages(lhs.icon, rhs.icon) + && deepEqualsmessages(lhs.iosSystemIcon, rhs.iosSystemIcon) } + func hash(into hasher: inout Hasher) { - deepHashmessages(value: toList(), hasher: &hasher) + hasher.combine("ShortcutItemMessage") + deepHashmessages(value: type, hasher: &hasher) + deepHashmessages(value: localizedTitle, hasher: &hasher) + deepHashmessages(value: localizedSubtitle, hasher: &hasher) + deepHashmessages(value: icon, hasher: &hasher) + deepHashmessages(value: iosSystemIcon, hasher: &hasher) } } diff --git a/packages/quick_actions/quick_actions_ios/lib/messages.g.dart b/packages/quick_actions/quick_actions_ios/lib/messages.g.dart index bc3dc1d6a663..15772d8b666a 100644 --- a/packages/quick_actions/quick_actions_ios/lib/messages.g.dart +++ b/packages/quick_actions/quick_actions_ios/lib/messages.g.dart @@ -1,21 +1,40 @@ // Copyright 2013 The Flutter Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v26.1.0), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers +// ignore_for_file: unused_import, unused_shown_name +// ignore_for_file: type=lint import 'dart:async'; -import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; +import 'dart:typed_data' show Float64List, Int32List, Int64List; -import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; import 'package:flutter/services.dart'; +import 'package:meta/meta.dart' show immutable, protected, visibleForTesting; -PlatformException _createConnectionError(String channelName) { - return PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel: "$channelName".', - ); +Object? _extractReplyValueOrThrow( + List? replyList, + String channelName, { + required bool isNullValid, +}) { + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (!isNullValid && (replyList.isNotEmpty && replyList[0] == null)) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } + return replyList.firstOrNull; } List wrapResponse({Object? result, PlatformException? error, bool empty = false}) { @@ -29,21 +48,66 @@ List wrapResponse({Object? result, PlatformException? error, bool empty } bool _deepEquals(Object? a, Object? b) { + if (identical(a, b)) { + return true; + } + if (a is double && b is double) { + if (a.isNaN && b.isNaN) { + return true; + } + return a == b; + } if (a is List && b is List) { return a.length == b.length && a.indexed.every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); } if (a is Map && b is Map) { - return a.length == b.length && - a.entries.every( - (MapEntry entry) => - (b as Map).containsKey(entry.key) && - _deepEquals(entry.value, b[entry.key]), - ); + if (a.length != b.length) { + return false; + } + for (final MapEntry entryA in a.entries) { + bool found = false; + for (final MapEntry entryB in b.entries) { + if (_deepEquals(entryA.key, entryB.key)) { + if (_deepEquals(entryA.value, entryB.value)) { + found = true; + break; + } else { + return false; + } + } + } + if (!found) { + return false; + } + } + return true; } return a == b; } +int _deepHash(Object? value) { + if (value is List) { + return Object.hashAll(value.map(_deepHash)); + } + if (value is Map) { + int result = 0; + for (final MapEntry entry in value.entries) { + result += (_deepHash(entry.key) * 31) ^ _deepHash(entry.value); + } + return result; + } + if (value is double && value.isNaN) { + // Normalize NaN to a consistent hash. + return 0x7FF8000000000000.hashCode; + } + if (value is double && value == 0.0) { + // Normalize -0.0 to 0.0 so they have the same hash code. + return 0.0.hashCode; + } + return value.hashCode; +} + /// Home screen quick-action shortcut item. class ShortcutItemMessage { ShortcutItemMessage({ @@ -51,6 +115,7 @@ class ShortcutItemMessage { required this.localizedTitle, this.localizedSubtitle, this.icon, + this.iosSystemIcon, }); /// The identifier of this item; should be unique within the app. @@ -65,8 +130,11 @@ class ShortcutItemMessage { /// Name of native resource to be displayed as the icon for this item. String? icon; + /// The name of the SF Symbol to be displayed as the icon for this item on iOS. + String? iosSystemIcon; + List _toList() { - return [type, localizedTitle, localizedSubtitle, icon]; + return [type, localizedTitle, localizedSubtitle, icon, iosSystemIcon]; } Object encode() { @@ -80,6 +148,7 @@ class ShortcutItemMessage { localizedTitle: result[1]! as String, localizedSubtitle: result[2] as String?, icon: result[3] as String?, + iosSystemIcon: result[4] as String?, ); } @@ -92,12 +161,16 @@ class ShortcutItemMessage { if (identical(this, other)) { return true; } - return _deepEquals(encode(), other.encode()); + return _deepEquals(type, other.type) && + _deepEquals(localizedTitle, other.localizedTitle) && + _deepEquals(localizedSubtitle, other.localizedSubtitle) && + _deepEquals(icon, other.icon) && + _deepEquals(iosSystemIcon, other.iosSystemIcon); } @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => _deepHash([runtimeType, ..._toList()]); } class _PigeonCodec extends StandardMessageCodec { @@ -143,50 +216,32 @@ class IOSQuickActionsApi { /// Sets the dynamic shortcuts for the app. Future setShortcutItems(List itemsList) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.quick_actions_ios.IOSQuickActionsApi.setShortcutItems$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([itemsList]); - final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow(pigeonVar_replyList, pigeonVar_channelName, isNullValid: true); } /// Removes all dynamic shortcuts. Future clearShortcutItems() async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.quick_actions_ios.IOSQuickActionsApi.clearShortcutItems$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); - final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow(pigeonVar_replyList, pigeonVar_channelName, isNullValid: true); } } @@ -203,7 +258,7 @@ abstract class IOSQuickActionsFlutterApi { }) { messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.quick_actions_ios.IOSQuickActionsFlutterApi.launchAction$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger, @@ -212,18 +267,10 @@ abstract class IOSQuickActionsFlutterApi { pigeonVar_channel.setMessageHandler(null); } else { pigeonVar_channel.setMessageHandler((Object? message) async { - assert( - message != null, - 'Argument for dev.flutter.pigeon.quick_actions_ios.IOSQuickActionsFlutterApi.launchAction was null.', - ); - final List args = (message as List?)!; - final String? arg_action = (args[0] as String?); - assert( - arg_action != null, - 'Argument for dev.flutter.pigeon.quick_actions_ios.IOSQuickActionsFlutterApi.launchAction was null, expected non-null String.', - ); + final List args = message! as List; + final String arg_action = args[0]! as String; try { - api.launchAction(arg_action!); + api.launchAction(arg_action); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); diff --git a/packages/quick_actions/quick_actions_ios/lib/quick_actions_ios.dart b/packages/quick_actions/quick_actions_ios/lib/quick_actions_ios.dart index 0092a026c26d..4f771f9e2c07 100644 --- a/packages/quick_actions/quick_actions_ios/lib/quick_actions_ios.dart +++ b/packages/quick_actions/quick_actions_ios/lib/quick_actions_ios.dart @@ -45,6 +45,7 @@ class QuickActionsIos extends QuickActionsPlatform { localizedTitle: item.localizedTitle, localizedSubtitle: item.localizedSubtitle, icon: item.icon, + iosSystemIcon: item.iosSystemIcon, ); } } diff --git a/packages/quick_actions/quick_actions_ios/pigeons/messages.dart b/packages/quick_actions/quick_actions_ios/pigeons/messages.dart index 08554e795933..2dd4fd594dba 100644 --- a/packages/quick_actions/quick_actions_ios/pigeons/messages.dart +++ b/packages/quick_actions/quick_actions_ios/pigeons/messages.dart @@ -13,7 +13,13 @@ import 'package:pigeon/pigeon.dart'; ) /// Home screen quick-action shortcut item. class ShortcutItemMessage { - ShortcutItemMessage(this.type, this.localizedTitle, this.localizedSubtitle, this.icon); + ShortcutItemMessage( + this.type, + this.localizedTitle, + this.localizedSubtitle, + this.icon, + this.iosSystemIcon, + ); /// The identifier of this item; should be unique within the app. String type; @@ -26,6 +32,9 @@ class ShortcutItemMessage { /// Name of native resource to be displayed as the icon for this item. String? icon; + + /// The name of the SF Symbol to be displayed as the icon for this item on iOS. + String? iosSystemIcon; } @HostApi() diff --git a/packages/quick_actions/quick_actions_platform_interface/lib/method_channel/method_channel_quick_actions.dart b/packages/quick_actions/quick_actions_platform_interface/lib/method_channel/method_channel_quick_actions.dart index 4996ee54fbb5..fd579af50da5 100644 --- a/packages/quick_actions/quick_actions_platform_interface/lib/method_channel/method_channel_quick_actions.dart +++ b/packages/quick_actions/quick_actions_platform_interface/lib/method_channel/method_channel_quick_actions.dart @@ -43,6 +43,7 @@ class MethodChannelQuickActions extends QuickActionsPlatform { 'localizedTitle': item.localizedTitle, if (item.localizedSubtitle != null) 'localizedSubtitle': item.localizedSubtitle, 'icon': item.icon, + if (item.iosSystemIcon != null) 'iosSystemIcon': item.iosSystemIcon, }; } } diff --git a/packages/quick_actions/quick_actions_platform_interface/lib/types/shortcut_item.dart b/packages/quick_actions/quick_actions_platform_interface/lib/types/shortcut_item.dart index 145928a49a6d..ff6be5f57652 100644 --- a/packages/quick_actions/quick_actions_platform_interface/lib/types/shortcut_item.dart +++ b/packages/quick_actions/quick_actions_platform_interface/lib/types/shortcut_item.dart @@ -13,6 +13,7 @@ class ShortcutItem { required this.localizedTitle, this.localizedSubtitle, this.icon, + this.iosSystemIcon, }); /// The identifier of this item; should be unique within the app. @@ -29,4 +30,7 @@ class ShortcutItem { /// Name of native resource (xcassets etc; NOT a Flutter asset) to be /// displayed as the icon for this item. final String? icon; + + /// The name of the SF Symbol to be displayed as the icon for this item on iOS. + final String? iosSystemIcon; } From cf482b30be31cbfc93bb9dee459737a1233f382e Mon Sep 17 00:00:00 2001 From: Zubii Date: Thu, 9 Jul 2026 23:21:51 +0300 Subject: [PATCH 2/4] test(quick_actions): add tests --- .../method_channel_quick_actions_test.dart | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/quick_actions/quick_actions_platform_interface/test/method_channel_quick_actions_test.dart b/packages/quick_actions/quick_actions_platform_interface/test/method_channel_quick_actions_test.dart index 891a611c22d7..e5fd662bb1d8 100644 --- a/packages/quick_actions/quick_actions_platform_interface/test/method_channel_quick_actions_test.dart +++ b/packages/quick_actions/quick_actions_platform_interface/test/method_channel_quick_actions_test.dart @@ -55,6 +55,7 @@ void main() { localizedTitle: 'title', localizedSubtitle: 'subtitle', icon: 'icon.svg', + iosSystemIcon: 'some_icon', ), ]); @@ -68,6 +69,7 @@ void main() { 'localizedTitle': 'title', 'localizedSubtitle': 'subtitle', 'icon': 'icon.svg', + 'iosSystemIcon': 'some_icon', }, ], ), @@ -77,7 +79,12 @@ void main() { test('passes shortcutItem through channel with null localizedSubtitle', () { quickActions.initialize((String type) {}); quickActions.setShortcutItems([ - const ShortcutItem(type: 'test', localizedTitle: 'title', icon: 'icon.svg'), + const ShortcutItem( + type: 'test', + localizedTitle: 'title', + icon: 'icon.svg', + iosSystemIcon: 'some_icon', + ), ]); expect(log, [ @@ -85,7 +92,12 @@ void main() { isMethodCall( 'setShortcutItems', arguments: >[ - {'type': 'test', 'localizedTitle': 'title', 'icon': 'icon.svg'}, + { + 'type': 'test', + 'localizedTitle': 'title', + 'icon': 'icon.svg', + 'iosSystemIcon': 'some_icon', + }, ], ), ]); @@ -96,12 +108,14 @@ void main() { const localizedTitle = 'localizedTitle'; const localizedSubtitle = 'localizedSubtitle'; const icon = 'icon'; + const iosSystemIcon = 'some_icon'; await quickActions.setShortcutItems(const [ ShortcutItem( type: type, localizedTitle: localizedTitle, localizedSubtitle: localizedSubtitle, icon: icon, + iosSystemIcon: iosSystemIcon, ), ]); expect(log, [ @@ -113,6 +127,7 @@ void main() { 'localizedTitle': localizedTitle, 'localizedSubtitle': localizedSubtitle, 'icon': icon, + 'iosSystemIcon': iosSystemIcon, }, ], ), @@ -146,18 +161,21 @@ void main() { const localizedTitle = 'title'; const localizedSubtitle = 'subtitle'; const icon = 'foo'; + const iosSystemIcon = 'some_icon'; const item = ShortcutItem( type: type, localizedTitle: localizedTitle, localizedSubtitle: localizedSubtitle, icon: icon, + iosSystemIcon: iosSystemIcon, ); expect(item.type, type); expect(item.localizedTitle, localizedTitle); expect(item.localizedSubtitle, localizedSubtitle); expect(item.icon, icon); + expect(item.iosSystemIcon, iosSystemIcon); }); }); } From f26efdefc9c1e03547091f4b2999551612481431 Mon Sep 17 00:00:00 2001 From: Zubii Date: Thu, 9 Jul 2026 23:24:00 +0300 Subject: [PATCH 3/4] chore(quick_actions): bump package version and update changelogs --- packages/quick_actions/quick_actions_ios/CHANGELOG.md | 4 ++++ packages/quick_actions/quick_actions_ios/pubspec.yaml | 6 +++++- .../quick_actions_platform_interface/CHANGELOG.md | 3 ++- .../quick_actions_platform_interface/pubspec.yaml | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/quick_actions/quick_actions_ios/CHANGELOG.md b/packages/quick_actions/quick_actions_ios/CHANGELOG.md index 4f1cbc7cf8eb..5bab051214f0 100644 --- a/packages/quick_actions/quick_actions_ios/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.3.0 + +* Adds support for iOS SF symbols icons for quick actions. + ## 1.2.4 * Adds support for UIScene lifecycle. diff --git a/packages/quick_actions/quick_actions_ios/pubspec.yaml b/packages/quick_actions/quick_actions_ios/pubspec.yaml index e1f48024ccf0..24ec23b12697 100644 --- a/packages/quick_actions/quick_actions_ios/pubspec.yaml +++ b/packages/quick_actions/quick_actions_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: quick_actions_ios description: An implementation for the iOS platform of the Flutter `quick_actions` plugin. repository: https://github.com/flutter/packages/tree/main/packages/quick_actions/quick_actions_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 1.2.4 +version: 1.3.0 environment: sdk: ^3.10.0 @@ -30,3 +30,7 @@ dev_dependencies: topics: - quick-actions - os-integration +# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE. +# See https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changing-federated-plugins +dependency_overrides: + quick_actions_platform_interface: {path: ../../../packages/quick_actions/quick_actions_platform_interface} diff --git a/packages/quick_actions/quick_actions_platform_interface/CHANGELOG.md b/packages/quick_actions/quick_actions_platform_interface/CHANGELOG.md index e1f186a2cea9..cddf683a2f5b 100644 --- a/packages/quick_actions/quick_actions_platform_interface/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 1.2.0 +* Adds iosSystemIcon field for iOS quick actions. * Updates minimum supported SDK version to Flutter 3.38/Dart 3.10. ## 1.1.0 diff --git a/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml b/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml index 476a4bb6021d..7c1e76439778 100644 --- a/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml +++ b/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/packages/tree/main/packages/quick_actions issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+quick_actions%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 1.1.0 +version: 1.2.0 environment: sdk: ^3.10.0 From a6bc9340bb7dcf1f95bc60c06638f38a0f56e00b Mon Sep 17 00:00:00 2001 From: Zubii Date: Thu, 9 Jul 2026 23:24:41 +0300 Subject: [PATCH 4/4] chore(quick_actions): add temporary dependency_overrides --- .../quick_actions/quick_actions_ios/example/pubspec.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/quick_actions/quick_actions_ios/example/pubspec.yaml b/packages/quick_actions/quick_actions_ios/example/pubspec.yaml index 46a29f8dc9ee..688c48071031 100644 --- a/packages/quick_actions/quick_actions_ios/example/pubspec.yaml +++ b/packages/quick_actions/quick_actions_ios/example/pubspec.yaml @@ -25,3 +25,8 @@ dev_dependencies: flutter: uses-material-design: true +# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE. +# See https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changing-federated-plugins +dependency_overrides: + quick_actions_ios: {path: ../../../../packages/quick_actions/quick_actions_ios} + quick_actions_platform_interface: {path: ../../../../packages/quick_actions/quick_actions_platform_interface}