feat(share_extension): integrate share_handler package and implement share functionality with updated permissions and entitlements

This commit is contained in:
cogwheel0
2025-08-28 12:59:48 +05:30
parent fc4b10b1f2
commit 4a524d404e
15 changed files with 676 additions and 26 deletions

View File

@@ -3,7 +3,9 @@
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28"/>
<!-- Background streaming permissions -->
@@ -44,6 +46,28 @@
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<!-- Receive shares: text, single/multiple files, images -->
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="*/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="*/*" />
</intent-filter>
</activity>
<!-- Background streaming service -->
@@ -61,4 +85,4 @@
</application>
<!-- Queries for speech recognition removed; using server transcription -->
</manifest>
</manifest>

View File

@@ -1,9 +1,12 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '13.0'
platform :ios, '13.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
# Work around Xcode cycle false-positives by letting CocoaPods not declare IO paths
install! 'cocoapods', :disable_input_output_paths => true
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
@@ -28,9 +31,19 @@ require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelpe
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
# Prefer static frameworks to avoid Xcode embed-cycles with app extensions
use_frameworks! :linkage => :static
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
# share_handler addition start
target 'ShareExtension' do
inherit! :search_paths
# Only models are needed in extension to avoid app-only APIs
pod "share_handler_ios_models", :path => ".symlinks/plugins/share_handler_ios/ios/Models"
end
# share_handler addition end
target 'RunnerTests' do
inherit! :search_paths
end
@@ -40,4 +53,15 @@ post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
# Reduce chances of embed/cycle: don't embed Swift stdlib explicitly
installer.aggregate_targets.each do |aggregate_target|
aggregate_target.user_project.native_targets.each do |native_target|
if ['Runner', 'ShareExtension'].include?(native_target.name)
native_target.build_configurations.each do |config|
config.build_settings['ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES'] = 'NO'
end
end
end
end
end

View File

@@ -50,6 +50,14 @@ PODS:
- SDWebImage (5.21.1):
- SDWebImage/Core (= 5.21.1)
- SDWebImage/Core (5.21.1)
- share_handler_ios (0.0.14):
- Flutter
- share_handler_ios/share_handler_ios_models (= 0.0.14)
- share_handler_ios_models
- share_handler_ios/share_handler_ios_models (0.0.14):
- Flutter
- share_handler_ios_models
- share_handler_ios_models (0.0.9)
- share_plus (0.0.1):
- Flutter
- shared_preferences_foundation (0.0.1):
@@ -75,6 +83,8 @@ DEPENDENCIES:
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- record_ios (from `.symlinks/plugins/record_ios/ios`)
- share_handler_ios (from `.symlinks/plugins/share_handler_ios/ios`)
- share_handler_ios_models (from `.symlinks/plugins/share_handler_ios/ios/Models`)
- share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
@@ -106,6 +116,10 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
record_ios:
:path: ".symlinks/plugins/record_ios/ios"
share_handler_ios:
:path: ".symlinks/plugins/share_handler_ios/ios"
share_handler_ios_models:
:path: ".symlinks/plugins/share_handler_ios/ios/Models"
share_plus:
:path: ".symlinks/plugins/share_plus/ios"
shared_preferences_foundation:
@@ -131,6 +145,8 @@ SPEC CHECKSUMS:
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
record_ios: fee1c924aa4879b882ebca2b4bce6011bcfc3d8b
SDWebImage: f29024626962457f3470184232766516dee8dfea
share_handler_ios: e2244e990f826b2c8eaa291ac3831569438ba0fb
share_handler_ios_models: fc638c9b4330dc7f082586c92aee9dfa0b87b871
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
@@ -139,6 +155,6 @@ SPEC CHECKSUMS:
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556
PODFILE CHECKSUM: 3c63482e143d1b91d2d2560aee9fb04ecc74ac7e
PODFILE CHECKSUM: df88575cf61e98a1a3edf2f8c887dad2c18c2079
COCOAPODS: 1.16.2

View File

@@ -11,11 +11,13 @@
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
4A7698E95402502ED3A27D07 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E21C32C282E5A003D094A6E9 /* Pods_RunnerTests.framework */; };
5318C41BD6012F73B0A81F8D /* Pods_ShareExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 15F5EAC162214F437B9D5305 /* Pods_ShareExtension.framework */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
B26D75F4FE6FFABCFAC07E43 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0BA04FBA3F37D0A688CFEFA7 /* Pods_Runner.framework */; };
F1DBCF1D2E601A39004C2540 /* ShareExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = F1DBCF132E601A39004C2540 /* ShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -26,6 +28,13 @@
remoteGlobalIDString = 97C146ED1CF9000F007C117D;
remoteInfo = Runner;
};
F1DBCF1B2E601A39004C2540 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 97C146E61CF9000F007C117D /* Project object */;
proxyType = 1;
remoteGlobalIDString = F1DBCF122E601A39004C2540;
remoteInfo = ShareExtension;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -39,19 +48,34 @@
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
F1DBCF1E2E601A39004C2540 /* Embed Foundation Extensions */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 13;
files = (
F1DBCF1D2E601A39004C2540 /* ShareExtension.appex in Embed Foundation Extensions */,
);
name = "Embed Foundation Extensions";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
0BA04FBA3F37D0A688CFEFA7 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
15F5EAC162214F437B9D5305 /* Pods_ShareExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ShareExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; };
1EB0E6D3737F83B935118E8E /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
20A55D0FD9E7CFD1B9BBBEED /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
69671B6B1959FCF16D21E200 /* Pods-ShareExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.release.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.release.xcconfig"; sourceTree = "<group>"; };
71EEBC62F9F81EB3FD345FA2 /* Pods-ShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.debug.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.debug.xcconfig"; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
F1DBCF292E602000004C2540 /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
837A0EFB28C7ACD7FF14EC67 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
838ED4921F9222D50ECF498B /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
@@ -64,9 +88,37 @@
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
B60F6CBA44A303DE5389649E /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
B6CA85D4EC683E94C2FB2A88 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
C9F5BA8D70297B37D581AE50 /* Pods-ShareExtension.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.profile.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.profile.xcconfig"; sourceTree = "<group>"; };
E21C32C282E5A003D094A6E9 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
F1DBCF132E601A39004C2540 /* ShareExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ShareExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
F1DBCF242E601A7C004C2540 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
F1DBCF222E601A39004C2540 /* Exceptions for "ShareExtension" folder in "ShareExtension" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
Info.plist,
);
target = F1DBCF122E601A39004C2540 /* ShareExtension */;
};
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
/* Begin PBXFileSystemSynchronizedRootGroup section */
F1DBCF142E601A39004C2540 /* ShareExtension */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
F1DBCF222E601A39004C2540 /* Exceptions for "ShareExtension" folder in "ShareExtension" target */,
);
explicitFileTypes = {
};
explicitFolders = (
);
path = ShareExtension;
sourceTree = "<group>";
};
/* End PBXFileSystemSynchronizedRootGroup section */
/* Begin PBXFrameworksBuildPhase section */
3F0B71E2AB4D863902C09B3E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
@@ -84,6 +136,14 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
F1DBCF102E601A39004C2540 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5318C41BD6012F73B0A81F8D /* Pods_ShareExtension.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
@@ -100,6 +160,7 @@
children = (
0BA04FBA3F37D0A688CFEFA7 /* Pods_Runner.framework */,
E21C32C282E5A003D094A6E9 /* Pods_RunnerTests.framework */,
15F5EAC162214F437B9D5305 /* Pods_ShareExtension.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@@ -113,6 +174,9 @@
B6CA85D4EC683E94C2FB2A88 /* Pods-RunnerTests.debug.xcconfig */,
837A0EFB28C7ACD7FF14EC67 /* Pods-RunnerTests.release.xcconfig */,
1EB0E6D3737F83B935118E8E /* Pods-RunnerTests.profile.xcconfig */,
71EEBC62F9F81EB3FD345FA2 /* Pods-ShareExtension.debug.xcconfig */,
69671B6B1959FCF16D21E200 /* Pods-ShareExtension.release.xcconfig */,
C9F5BA8D70297B37D581AE50 /* Pods-ShareExtension.profile.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
@@ -133,6 +197,7 @@
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
F1DBCF142E601A39004C2540 /* ShareExtension */,
97C146EF1CF9000F007C117D /* Products */,
331C8082294A63A400263BE5 /* RunnerTests */,
8C43905FA2E52A883F49D605 /* Pods */,
@@ -145,6 +210,7 @@
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
331C8081294A63A400263BE5 /* RunnerTests.xctest */,
F1DBCF132E601A39004C2540 /* ShareExtension.appex */,
);
name = Products;
sourceTree = "<group>";
@@ -152,6 +218,7 @@
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
F1DBCF242E601A7C004C2540 /* Runner.entitlements */,
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
@@ -194,20 +261,43 @@
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
4F34859EAFDCA9A40F09ECFF /* [CP] Embed Pods Frameworks */,
F1DBCF1E2E601A39004C2540 /* Embed Foundation Extensions */,
AA915538CD255B5C833E6AB7 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
dependencies = (
F1DBCF1C2E601A39004C2540 /* PBXTargetDependency */,
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
F1DBCF122E601A39004C2540 /* ShareExtension */ = {
isa = PBXNativeTarget;
buildConfigurationList = F1DBCF232E601A39004C2540 /* Build configuration list for PBXNativeTarget "ShareExtension" */;
buildPhases = (
5A0A281EDF7C33D1547603FC /* [CP] Check Pods Manifest.lock */,
F1DBCF0F2E601A39004C2540 /* Sources */,
F1DBCF102E601A39004C2540 /* Frameworks */,
F1DBCF112E601A39004C2540 /* Resources */,
);
buildRules = (
);
dependencies = (
);
fileSystemSynchronizedGroups = (
F1DBCF142E601A39004C2540 /* ShareExtension */,
);
name = ShareExtension;
productName = ShareExtension;
productReference = F1DBCF132E601A39004C2540 /* ShareExtension.appex */;
productType = "com.apple.product-type.app-extension";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@@ -215,6 +305,7 @@
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
LastSwiftUpdateCheck = 1640;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
@@ -226,6 +317,9 @@
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
};
F1DBCF122E601A39004C2540 = {
CreatedOnToolsVersion = 16.4;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
@@ -243,6 +337,7 @@
targets = (
97C146ED1CF9000F007C117D /* Runner */,
331C8080294A63A400263BE5 /* RunnerTests */,
F1DBCF122E601A39004C2540 /* ShareExtension */,
);
};
/* End PBXProject section */
@@ -260,12 +355,18 @@
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
F1DBCF112E601A39004C2540 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
@@ -276,7 +377,7 @@
files = (
);
inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (
@@ -285,21 +386,26 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
4F34859EAFDCA9A40F09ECFF /* [CP] Embed Pods Frameworks */ = {
5A0A281EDF7C33D1547603FC /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-ShareExtension-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = {
@@ -317,6 +423,21 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
AA915538CD255B5C833E6AB7 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
B2C308F843502D753F643BC6 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -381,6 +502,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
F1DBCF0F2E601A39004C2540 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
@@ -389,6 +517,11 @@
target = 97C146ED1CF9000F007C117D /* Runner */;
targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
};
F1DBCF1C2E601A39004C2540 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = F1DBCF122E601A39004C2540 /* ShareExtension */;
targetProxy = F1DBCF1B2E601A39004C2540 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
@@ -467,8 +600,10 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
@@ -655,8 +790,10 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
@@ -683,8 +820,10 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
@@ -706,6 +845,129 @@
};
name = Release;
};
F1DBCF1F2E601A39004C2540 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 71EEBC62F9F81EB3FD345FA2 /* Pods-ShareExtension.debug.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = X2662V5DT2;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = ShareExtension/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = app.cogwheel.conduit.ShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
F1DBCF202E601A39004C2540 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 69671B6B1959FCF16D21E200 /* Pods-ShareExtension.release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = X2662V5DT2;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = ShareExtension/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = app.cogwheel.conduit.ShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
F1DBCF212E601A39004C2540 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = C9F5BA8D70297B37D581AE50 /* Pods-ShareExtension.profile.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = X2662V5DT2;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = ShareExtension/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = app.cogwheel.conduit.ShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Profile;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@@ -739,6 +1001,16 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
F1DBCF232E601A39004C2540 /* Build configuration list for PBXNativeTarget "ShareExtension" */ = {
isa = XCConfigurationList;
buildConfigurations = (
F1DBCF1F2E601A39004C2540 /* Debug */,
F1DBCF202E601A39004C2540 /* Release */,
F1DBCF212E601A39004C2540 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;

View File

@@ -73,5 +73,18 @@
<true/>
<key>UIStatusBarHidden</key>
<false/>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>app.cogwheel.conduit.share</string>
<key>CFBundleURLSchemes</key>
<array>
<string>ShareMedia-app.cogwheel.conduit</string>
</array>
</dict>
</array>
</dict>
</plist>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.app.cogwheel.conduit</string>
</array>
</dict>
</plist>

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- Uncomment below lines if you want to use a custom group id rather than the default. Set it in Build Settings -> User-Defined -->
<!-- <key>AppGroupId</key>
<string>$(CUSTOM_GROUP_ID)</string> -->
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<!-- Add supported message intent if you support sharing to a specific conversation - start -->
<key>IntentsSupported</key>
<array>
<string>INSendMessageIntent</string>
</array>
<!-- Add supported message intent if you support sharing to a specific conversation (registered via the recordSentMessage api call) - end -->
<key>NSExtensionActivationRule</key>
<!-- Comment or delete the TRUEPREDICATE NSExtensionActivationRule that only works in development mode -->
<!-- <string>TRUEPREDICATE</string> -->
<!-- Add a new NSExtensionActivationRule. The rule below will allow sharing one or more file of any type, url, or text content, You can modify these rules to your liking for which types of share content, as well as how many your app can handle -->
<dict>
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
<integer>10</integer>
<key>NSExtensionActivationSupportsMovieWithMaxCount</key>
<integer>10</integer>
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
<integer>10</integer>
<key>NSExtensionActivationSupportsText</key>
<true/>
</dict>
<key>PHSupportedMediaTypes</key>
<array>
<string>Video</string>
<string>Image</string>
</array>
</dict>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).ShareViewController</string>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.share-services</string>
</dict>
</dict>
</plist>

View File

@@ -3,6 +3,8 @@
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array/>
<array>
<string>group.app.cogwheel.conduit</string>
</array>
</dict>
</plist>

View File

@@ -0,0 +1,3 @@
import share_handler_ios_models
class ShareViewController: ShareHandlerIosViewController {}

View File

@@ -0,0 +1,172 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:share_handler/share_handler.dart' as sh;
import '../../features/auth/providers/unified_auth_providers.dart';
import '../../features/chat/providers/chat_providers.dart';
import '../../features/chat/services/file_attachment_service.dart';
import '../../core/providers/app_providers.dart';
/// Lightweight payload for a share event
class SharedPayload {
final String? text;
final List<String> filePaths;
const SharedPayload({this.text, this.filePaths = const []});
bool get hasAnything =>
(text != null && text!.trim().isNotEmpty) || filePaths.isNotEmpty;
}
/// Holds a pending shared payload until the app is ready (e.g., authed + model loaded)
final pendingSharedPayloadProvider = StateProvider<SharedPayload?>((_) => null);
/// Initializes listening to OS share intents and handles them
final shareReceiverInitializerProvider = Provider<void>((ref) {
// Do nothing on web/desktop
if (kIsWeb) return;
final sub = StreamController<SharedPayload>.broadcast();
// Listen for app readiness: authenticated and model available
void maybeProcessPending() {
final navState = ref.read(authNavigationStateProvider);
final model = ref.read(selectedModelProvider);
final pending = ref.read(pendingSharedPayloadProvider);
if (pending != null &&
pending.hasAnything &&
navState == AuthNavigationState.authenticated &&
model != null) {
_processPayload(ref, pending);
ref.read(pendingSharedPayloadProvider.notifier).state = null;
}
}
// React when auth/model changes to process a queued share
ref.listen<AuthNavigationState>(
authNavigationStateProvider,
(_, __) => maybeProcessPending(),
);
ref.listen(selectedModelProvider, (_, __) => maybeProcessPending());
// Hook into share_handler
final handler = sh.ShareHandler.instance;
// Handle initial share when app is cold-started via Share
Future.microtask(() async {
try {
final dynamic media = await handler.getInitialSharedMedia();
final payload = _toPayload(media);
if (payload.hasAnything) {
ref.read(pendingSharedPayloadProvider.notifier).state = payload;
maybeProcessPending();
}
} catch (e) {
debugPrint('ShareReceiver: failed to get initial shared media: $e');
}
});
// Handle subsequent shares while app is alive
final streamSub = handler.sharedMediaStream.listen((dynamic media) {
try {
final payload = _toPayload(media);
if (payload.hasAnything) {
ref.read(pendingSharedPayloadProvider.notifier).state = payload;
maybeProcessPending();
}
} catch (e) {
debugPrint('ShareReceiver: failed to parse shared media: $e');
}
});
// Ensure cleanup
ref.onDispose(() async {
await streamSub.cancel();
await sub.close();
});
});
SharedPayload _toPayload(dynamic media) {
if (media == null) return const SharedPayload();
String? text;
final filePaths = <String>[];
try {
// Common field in share_handler: `content` (String?)
text = (media as dynamic).content as String?;
} catch (_) {
try {
// Some plugins use `text`
text = (media as dynamic).text as String?;
} catch (_) {}
}
try {
final list = (media as dynamic).attachments as List<dynamic>?;
if (list != null) {
for (final att in list) {
try {
final p = (att as dynamic).path as String?;
if (p != null && p.isNotEmpty) filePaths.add(p);
} catch (_) {
// Ignore a malformed entry
}
}
}
} catch (_) {
// Older plugins may call it files
try {
final list = (media as dynamic).files as List<dynamic>?;
if (list != null) {
for (final att in list) {
try {
final p = (att as dynamic).path as String?;
if (p != null && p.isNotEmpty) filePaths.add(p);
} catch (_) {}
}
}
} catch (_) {}
}
return SharedPayload(text: text, filePaths: filePaths);
}
Future<void> _processPayload(Ref ref, SharedPayload payload) async {
try {
// Start a fresh chat context but do NOT auto-send
startNewChat(ref);
// Prefer attaching files to the composer so user can add text before sending
if (payload.filePaths.isNotEmpty) {
final svc = ref.read(fileAttachmentServiceProvider);
if (svc != null) {
// Add files to attachment list and kick off uploads, mirroring UI flow
final files = payload.filePaths.map((p) => File(p)).toList();
if (files.isNotEmpty) {
ref.read(attachedFilesProvider.notifier).addFiles(files);
for (final file in files) {
final uploadStream = svc.uploadFile(file);
uploadStream.listen((state) {
ref
.read(attachedFilesProvider.notifier)
.updateFileState(file.path, state);
}, onError: (_) {});
}
}
}
}
// Prefill text in the composer (do not auto-send)
final text = payload.text?.trim();
if (text != null && text.isNotEmpty) {
ref.read(prefilledInputTextProvider.notifier).state = text;
}
// This allows the user to add a caption before sending
} catch (e) {
debugPrint('ShareReceiver: failed to process payload: $e');
}
}

View File

@@ -21,6 +21,9 @@ final chatMessagesProvider =
// Loading state for conversation (used to show chat skeletons during fetch)
final isLoadingConversationProvider = StateProvider<bool>((ref) => false);
// Prefilled input text (e.g., when sharing text from other apps)
final prefilledInputTextProvider = StateProvider<String?>((ref) => null);
class ChatMessagesNotifier extends StateNotifier<List<ChatMessage>> {
final Ref _ref;
StreamSubscription? _messageStream;
@@ -453,6 +456,16 @@ Future<void> sendMessage(
await _sendMessageInternal(ref, message, attachments, toolIds);
}
// Service-friendly wrapper (accepts generic Ref)
Future<void> sendMessageFromService(
Ref ref,
String message,
List<String>? attachments, [
List<String>? toolIds,
]) async {
await _sendMessageInternal(ref, message, attachments, toolIds);
}
// Internal send message implementation
Future<void> _sendMessageInternal(
dynamic ref,

View File

@@ -185,6 +185,20 @@ class _ModernChatInputState extends ConsumerState<ModernChatInput>
vsync: this,
);
// Listen for prefilled text updates (e.g., from share intent)
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!mounted) return;
final text = ref.read(prefilledInputTextProvider);
if (text != null && text.isNotEmpty) {
_controller.text = text;
_controller.selection = TextSelection.collapsed(offset: text.length);
// Clear after applying so it doesn't re-apply on rebuilds
ref.read(prefilledInputTextProvider.notifier).state = null;
_ensureFocusedIfEnabled();
if (!_isExpanded) _setExpanded(true);
}
});
// Listen for text changes and update only when emptiness flips
_controller.addListener(() {
final has = _controller.text.trim().isNotEmpty;
@@ -878,8 +892,8 @@ class _ModernChatInputState extends ConsumerState<ModernChatInput>
color: isActive
? context.conduitTheme.buttonPrimary
: showBackground
? context.conduitTheme.cardBorder
: Colors.transparent,
? context.conduitTheme.cardBorder
: Colors.transparent,
width: BorderWidth.regular,
),
),
@@ -898,8 +912,8 @@ class _ModernChatInputState extends ConsumerState<ModernChatInput>
color: isActive
? context.conduitTheme.buttonPrimary
: showBackground
? context.conduitTheme.cardBackground
: Colors.transparent,
? context.conduitTheme.cardBackground
: Colors.transparent,
borderRadius: BorderRadius.circular(AppBorderRadius.xl),
boxShadow: (isActive || showBackground)
? ConduitShadows.button
@@ -910,11 +924,13 @@ class _ModernChatInputState extends ConsumerState<ModernChatInput>
size: iconSize ?? IconSize.medium,
color: widget.enabled
? (isActive
? context.conduitTheme.buttonPrimaryText
: context.conduitTheme.textPrimary
.withValues(alpha: Alpha.strong))
: context.conduitTheme.textPrimary
.withValues(alpha: Alpha.disabled),
? context.conduitTheme.buttonPrimaryText
: context.conduitTheme.textPrimary.withValues(
alpha: Alpha.strong,
))
: context.conduitTheme.textPrimary.withValues(
alpha: Alpha.disabled,
),
),
),
),
@@ -954,8 +970,9 @@ class _ModernChatInputState extends ConsumerState<ModernChatInput>
decoration: BoxDecoration(
// Subtle primary tint when active for clearer affordance
color: isActive
? context.conduitTheme.buttonPrimary
.withValues(alpha: Alpha.buttonHover + 0.04)
? context.conduitTheme.buttonPrimary.withValues(
alpha: Alpha.buttonHover + 0.04,
)
: context.conduitTheme.cardBackground,
borderRadius: BorderRadius.circular(AppBorderRadius.xl),
// No elevation to match modal chips

View File

@@ -18,6 +18,7 @@ import 'features/onboarding/views/onboarding_sheet.dart';
import 'package:conduit/l10n/app_localizations.dart';
import 'features/chat/views/chat_page.dart';
import 'features/navigation/views/splash_launcher_page.dart';
import 'core/services/share_receiver_service.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
@@ -66,6 +67,9 @@ class _ConduitAppState extends ConsumerState<ConduitApp> {
// Ensure API service auth integration is active
ref.read(authApiIntegrationProvider);
// Initialize OS share receiver so users can share text/files to Conduit
ref.read(shareReceiverInitializerProvider);
}
@override

View File

@@ -973,6 +973,38 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.1"
share_handler:
dependency: "direct main"
description:
name: share_handler
sha256: "0a6d007f0e44fbee27164adcd159ecbc88238864313f4e5c58161cae2180328d"
url: "https://pub.dev"
source: hosted
version: "0.0.25"
share_handler_android:
dependency: transitive
description:
name: share_handler_android
sha256: caf555b933dc72783aa37fef75688c7b86bd6f7bc17d80fbf585bc42f123cc8d
url: "https://pub.dev"
source: hosted
version: "0.0.11"
share_handler_ios:
dependency: transitive
description:
name: share_handler_ios
sha256: cdc21f88f336a944157a8e9ceb191525cee3b082d6eb6c2082488e4f09dc3ece
url: "https://pub.dev"
source: hosted
version: "0.0.15"
share_handler_platform_interface:
dependency: transitive
description:
name: share_handler_platform_interface
sha256: "7a4df95a87b326b2f07458d937f2281874567c364b7b7ebe4e7d50efaae5f106"
url: "https://pub.dev"
source: hosted
version: "0.0.6"
share_plus:
dependency: "direct main"
description:

View File

@@ -55,6 +55,7 @@ dependencies:
freezed_annotation: ^3.0.0
wakelock_plus: ^1.2.10
share_plus: ^11.1.0
share_handler: ^0.0.19
# Clipboard functionality is available through flutter/services (part of Flutter SDK)