diff --git a/CHANGELOG.md b/CHANGELOG.md index b6d1848a..e7b53275 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +## 0.8.0 +* fixed [#90](https://github.com/flutter/ai/issues/90): Input box + shrinks unexpectedly when clicking file attachment button – customization not + supported by Flutter AI Toolkit. Moved from a menu that moves the text input + to a pop-up menu. Added a `menuColor` setting to `LlmChatViewStyle`. Moved + the `tooltip` related styles to `text` related styles, as some action buttons + have icons (with tooltips) and some are menu items (with icon + text). + +* fixed [#92](https://github.com/flutter/ai/issues/92): Cupertino render doesn't + center text input inside text input area + +* fixed an issue with missing localizations under Cupertino + ## 0.7.1 * fixed [#74](https://github.com/flutter/ai/issues/74): Suggestions overlapping diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index ef8d2f7b..430744af 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -7,47 +7,47 @@ PODS: - Flutter - file_selector_ios (0.0.1): - Flutter - - Firebase/Auth (11.8.0): + - Firebase/Auth (11.10.0): - Firebase/CoreOnly - - FirebaseAuth (~> 11.8.0) - - Firebase/CoreOnly (11.8.0): - - FirebaseCore (~> 11.8.0) - - firebase_app_check (0.3.2-4): - - Firebase/CoreOnly (~> 11.8.0) + - FirebaseAuth (~> 11.10.0) + - Firebase/CoreOnly (11.10.0): + - FirebaseCore (~> 11.10.0) + - firebase_app_check (0.3.2-5): + - Firebase/CoreOnly (~> 11.10.0) - firebase_core - - FirebaseAppCheck (~> 11.8.0) + - FirebaseAppCheck (~> 11.10.0) - Flutter - - firebase_auth (5.5.1): - - Firebase/Auth (= 11.8.0) + - firebase_auth (5.5.2): + - Firebase/Auth (= 11.10.0) - firebase_core - Flutter - - firebase_core (3.12.1): - - Firebase/CoreOnly (= 11.8.0) + - firebase_core (3.13.0): + - Firebase/CoreOnly (= 11.10.0) - Flutter - - FirebaseAppCheck (11.8.0): + - FirebaseAppCheck (11.10.0): - AppCheckCore (~> 11.0) - FirebaseAppCheckInterop (~> 11.0) - - FirebaseCore (~> 11.8.0) + - FirebaseCore (~> 11.10.0) - GoogleUtilities/Environment (~> 8.0) - GoogleUtilities/UserDefaults (~> 8.0) - - FirebaseAppCheckInterop (11.9.0) - - FirebaseAuth (11.8.1): + - FirebaseAppCheckInterop (11.12.0) + - FirebaseAuth (11.10.0): - FirebaseAppCheckInterop (~> 11.0) - FirebaseAuthInterop (~> 11.0) - - FirebaseCore (~> 11.8.0) - - FirebaseCoreExtension (~> 11.8.0) + - FirebaseCore (~> 11.10.0) + - FirebaseCoreExtension (~> 11.10.0) - GoogleUtilities/AppDelegateSwizzler (~> 8.0) - GoogleUtilities/Environment (~> 8.0) - GTMSessionFetcher/Core (< 5.0, >= 3.4) - - RecaptchaInterop (~> 100.0) - - FirebaseAuthInterop (11.9.0) - - FirebaseCore (11.8.1): - - FirebaseCoreInternal (~> 11.8.0) + - RecaptchaInterop (~> 101.0) + - FirebaseAuthInterop (11.12.0) + - FirebaseCore (11.10.0): + - FirebaseCoreInternal (~> 11.10.0) - GoogleUtilities/Environment (~> 8.0) - GoogleUtilities/Logger (~> 8.0) - - FirebaseCoreExtension (11.8.0): - - FirebaseCore (~> 11.8.0) - - FirebaseCoreInternal (11.8.0): + - FirebaseCoreExtension (11.10.0): + - FirebaseCore (~> 11.10.0) + - FirebaseCoreInternal (11.10.0): - "GoogleUtilities/NSData+zlib (~> 8.0)" - Flutter (1.0.0) - GoogleUtilities/AppDelegateSwizzler (8.0.2): @@ -81,8 +81,8 @@ PODS: - Flutter - FlutterMacOS - PromisesObjC (2.4.0) - - RecaptchaInterop (100.0.0) - - record_darwin (1.0.0): + - RecaptchaInterop (101.0.0) + - record_ios (1.0.0): - Flutter - shared_preferences_foundation (0.0.1): - Flutter @@ -99,7 +99,7 @@ DEPENDENCIES: - Flutter (from `Flutter`) - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - - record_darwin (from `.symlinks/plugins/record_darwin/ios`) + - record_ios (from `.symlinks/plugins/record_ios/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) @@ -136,8 +136,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/image_picker_ios/ios" path_provider_foundation: :path: ".symlinks/plugins/path_provider_foundation/darwin" - record_darwin: - :path: ".symlinks/plugins/record_darwin/ios" + record_ios: + :path: ".symlinks/plugins/record_ios/ios" shared_preferences_foundation: :path: ".symlinks/plugins/shared_preferences_foundation/darwin" url_launcher_ios: @@ -145,27 +145,27 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: AppCheckCore: cc8fd0a3a230ddd401f326489c99990b013f0c4f - camera_avfoundation: dd002b0330f4981e1bbcb46ae9b62829237459a4 + camera_avfoundation: adb0207d868b2d873e895371d88448399ab78d87 file_selector_ios: f0670c1064a8c8450e38145d8043160105d0b97c - Firebase: d80354ed7f6df5f9aca55e9eb47cc4b634735eaf - firebase_app_check: 41ab298dc5440c44fba8b12a8f949062c049f3fa - firebase_auth: 3d848b9b866b201e5c8e0c06d8b2cec272fd8825 - firebase_core: ac395f994af4e28f6a38b59e05a88ca57abeb874 - FirebaseAppCheck: f6648d6d2b321ecf94cf72f6737fc68d4fddc010 - FirebaseAppCheckInterop: 9226f7217b43e99dfa0bc9f674ad8108cef89feb - FirebaseAuth: ad59a1a7b161e75f74c39f70179d2482d40e2737 - FirebaseAuthInterop: 2a26ee1bea6d47df8048683cfa071e7da657798f - FirebaseCore: 99fe0c4b44a39f37d99e6404e02009d2db5d718d - FirebaseCoreExtension: 3d3f2017a00d06e09ab4ebe065391b0bb642565e - FirebaseCoreInternal: df24ce5af28864660ecbd13596fc8dd3a8c34629 + Firebase: 1fe1c0a7d9aaea32efe01fbea5f0ebd8d70e53a2 + firebase_app_check: f24ffe14416429535e3ba82c181e2a04be2430e0 + firebase_auth: e37065f3f80ff90580c13ad0e5a48e3bb8d2ad77 + firebase_core: 432718558359a8c08762151b5f49bb0f093eb6e0 + FirebaseAppCheck: 9687ebd909702469bc09d2d58008697b83f4ac27 + FirebaseAppCheckInterop: 73b173e5ec45192e2d522ad43f526a82ad10b852 + FirebaseAuth: c4146bdfdc87329f9962babd24dae89373f49a32 + FirebaseAuthInterop: b583210c039a60ed3f1e48865e1f3da44a796595 + FirebaseCore: 8344daef5e2661eb004b177488d6f9f0f24251b7 + FirebaseCoreExtension: 6f357679327f3614e995dc7cf3f2d600bdc774ac + FirebaseCoreInternal: ef4505d2afb1d0ebbc33162cb3795382904b5679 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d GTMSessionFetcher: 75b671f9e551e4c49153d4c4f8659ef4f559b970 image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 - RecaptchaInterop: 7d1a4a01a6b2cb1610a47ef3f85f0c411434cb21 - record_darwin: 3b1a8e7d5c0cbf45ad6165b4d83a6ca643d929c3 + RecaptchaInterop: 11e0b637842dfb48308d242afc3f448062325aba + record_ios: 1bbc430ab8406174d70332f23e06a3dc751239b4 shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 30013659..55f92ff3 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -7,15 +7,15 @@ objects = { /* Begin PBXBuildFile section */ + 12D42D50DD4A509DCDBD1AE8 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 00907B8AC1679C03DF3ECF3B /* Pods_RunnerTests.framework */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 4909C4F06E583DBA6911A0EB /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 24EBA54F15A5BC1E5EECBA0D /* Pods_Runner.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 */; }; - 9825ACE21694785BABA062E4 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2CB4A73665629C533D362F69 /* Pods_RunnerTests.framework */; }; + B138116870EFFBDD29006F94 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F29D7074F67D6C5BAF435134 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -42,15 +42,14 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 00907B8AC1679C03DF3ECF3B /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 24EBA54F15A5BC1E5EECBA0D /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 2CB4A73665629C533D362F69 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 1D6994B85815BA0993EDD5A4 /* 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 = ""; }; 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 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 = ""; }; - 4A0A71B277BAFAA0D4B1617D /* 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 = ""; }; - 4EE7C7992C2F54CDA0578747 /* 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 = ""; }; + 60E9238D99D2FCB469F24BB9 /* 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 = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; @@ -61,10 +60,11 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - A56804F896E7CDF7D7FD3ECD /* 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 = ""; }; - CECCE38016BCA88CAEEF0C05 /* 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 = ""; }; - D3EEBCE625413CCA1F6B1A6C /* 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 = ""; }; - D72871F43FD8BA4BDE88379C /* 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 = ""; }; + A1A2A049E8361A461BA04832 /* 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 = ""; }; + B46F1E201939CDBE9A570C66 /* 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 = ""; }; + BE6369502A25C93B721F7BE3 /* 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 = ""; }; + EAD24AE1D4943D199870F41C /* 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 = ""; }; + F29D7074F67D6C5BAF435134 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -72,7 +72,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9825ACE21694785BABA062E4 /* Pods_RunnerTests.framework in Frameworks */, + 12D42D50DD4A509DCDBD1AE8 /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -80,7 +80,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 4909C4F06E583DBA6911A0EB /* Pods_Runner.framework in Frameworks */, + B138116870EFFBDD29006F94 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -95,15 +95,15 @@ path = RunnerTests; sourceTree = ""; }; - 8C210AB4EC8B0D26935557A3 /* Pods */ = { + 67F7FFA188394B87CE9E2A89 /* Pods */ = { isa = PBXGroup; children = ( - D3EEBCE625413CCA1F6B1A6C /* Pods-Runner.debug.xcconfig */, - CECCE38016BCA88CAEEF0C05 /* Pods-Runner.release.xcconfig */, - 4A0A71B277BAFAA0D4B1617D /* Pods-Runner.profile.xcconfig */, - 4EE7C7992C2F54CDA0578747 /* Pods-RunnerTests.debug.xcconfig */, - A56804F896E7CDF7D7FD3ECD /* Pods-RunnerTests.release.xcconfig */, - D72871F43FD8BA4BDE88379C /* Pods-RunnerTests.profile.xcconfig */, + A1A2A049E8361A461BA04832 /* Pods-Runner.debug.xcconfig */, + 60E9238D99D2FCB469F24BB9 /* Pods-Runner.release.xcconfig */, + EAD24AE1D4943D199870F41C /* Pods-Runner.profile.xcconfig */, + 1D6994B85815BA0993EDD5A4 /* Pods-RunnerTests.debug.xcconfig */, + BE6369502A25C93B721F7BE3 /* Pods-RunnerTests.release.xcconfig */, + B46F1E201939CDBE9A570C66 /* Pods-RunnerTests.profile.xcconfig */, ); name = Pods; path = Pods; @@ -127,8 +127,8 @@ 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, 331C8082294A63A400263BE5 /* RunnerTests */, - 8C210AB4EC8B0D26935557A3 /* Pods */, - B383250BB4BAE8778BE974CF /* Frameworks */, + 67F7FFA188394B87CE9E2A89 /* Pods */, + F15FFF544ED27837A1711D1A /* Frameworks */, ); sourceTree = ""; }; @@ -156,11 +156,11 @@ path = Runner; sourceTree = ""; }; - B383250BB4BAE8778BE974CF /* Frameworks */ = { + F15FFF544ED27837A1711D1A /* Frameworks */ = { isa = PBXGroup; children = ( - 24EBA54F15A5BC1E5EECBA0D /* Pods_Runner.framework */, - 2CB4A73665629C533D362F69 /* Pods_RunnerTests.framework */, + F29D7074F67D6C5BAF435134 /* Pods_Runner.framework */, + 00907B8AC1679C03DF3ECF3B /* Pods_RunnerTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -172,7 +172,7 @@ isa = PBXNativeTarget; buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( - FC1BD4EE70D007B183F3F191 /* [CP] Check Pods Manifest.lock */, + 42BBE5AF3FE79424BFE373FC /* [CP] Check Pods Manifest.lock */, 331C807D294A63A400263BE5 /* Sources */, 331C807F294A63A400263BE5 /* Resources */, 86401DABBB1072A23662B554 /* Frameworks */, @@ -191,14 +191,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 96D2A68DF9A9237CF8051B2D /* [CP] Check Pods Manifest.lock */, + 006265E1621D9A75C1D352D1 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 69D636092F178280D89F94DC /* [CP] Embed Pods Frameworks */, + 2101CC1F931599A2701CABD6 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -270,23 +270,29 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + 006265E1621D9A75C1D352D1 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( - "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( ); - name = "Thin Binary"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + 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; }; - 69D636092F178280D89F94DC /* [CP] Embed Pods Frameworks */ = { + 2101CC1F931599A2701CABD6 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -303,7 +309,23 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 96D2A68DF9A9237CF8051B2D /* [CP] Check Pods Manifest.lock */ = { + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 42BBE5AF3FE79424BFE373FC /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -318,7 +340,7 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -340,28 +362,6 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; - FC1BD4EE70D007B183F3F191 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - 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; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -488,7 +488,7 @@ }; 331C8088294A63A400263BE5 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 4EE7C7992C2F54CDA0578747 /* Pods-RunnerTests.debug.xcconfig */; + baseConfigurationReference = 1D6994B85815BA0993EDD5A4 /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -506,7 +506,7 @@ }; 331C8089294A63A400263BE5 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A56804F896E7CDF7D7FD3ECD /* Pods-RunnerTests.release.xcconfig */; + baseConfigurationReference = BE6369502A25C93B721F7BE3 /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -522,7 +522,7 @@ }; 331C808A294A63A400263BE5 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D72871F43FD8BA4BDE88379C /* Pods-RunnerTests.profile.xcconfig */; + baseConfigurationReference = B46F1E201939CDBE9A570C66 /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; diff --git a/example/lib/custom_styles/custom_styles.dart b/example/lib/custom_styles/custom_styles.dart index 2ed5f0e7..660363a8 100644 --- a/example/lib/custom_styles/custom_styles.dart +++ b/example/lib/custom_styles/custom_styles.dart @@ -120,7 +120,7 @@ class _ChatPageState extends State ); final halloweenActionButtonStyle = ActionButtonStyle( - tooltipTextStyle: halloweenTextStyle, + textStyle: halloweenTextStyle, iconColor: Colors.black, iconDecoration: BoxDecoration( color: Colors.orange, @@ -129,7 +129,7 @@ class _ChatPageState extends State ); final halloweenMenuButtonStyle = ActionButtonStyle( - tooltipTextStyle: halloweenTextStyle, + textStyle: halloweenTextStyle, iconColor: Colors.orange, iconDecoration: BoxDecoration( color: Colors.black, @@ -140,6 +140,7 @@ class _ChatPageState extends State return LlmChatViewStyle( backgroundColor: Colors.transparent, + menuColor: Colors.grey, progressIndicatorColor: Colors.purple, suggestionStyle: SuggestionStyle( textStyle: halloweenTextStyle.copyWith(color: Colors.black), diff --git a/example/lib/dark_style.dart b/example/lib/dark_style.dart index 1ca219cf..f52cec94 100644 --- a/example/lib/dark_style.dart +++ b/example/lib/dark_style.dart @@ -11,6 +11,7 @@ LlmChatViewStyle darkChatViewStyle() { final style = LlmChatViewStyle.defaultStyle(); return LlmChatViewStyle( backgroundColor: _invertColor(style.backgroundColor), + menuColor: Colors.grey.shade800, progressIndicatorColor: _invertColor(style.progressIndicatorColor), userMessageStyle: _darkUserMessageStyle(), llmMessageStyle: _darkLlmMessageStyle(), @@ -94,9 +95,8 @@ ActionButtonStyle _darkActionButtonStyle(ActionButtonType type) { ), _ => _invertDecoration(style.iconDecoration), }, - tooltip: style.tooltip, - tooltipTextStyle: _invertTextStyle(style.tooltipTextStyle), - tooltipDecoration: _invertDecoration(style.tooltipDecoration), + text: style.text, + textStyle: _invertTextStyle(style.textStyle), ); } diff --git a/example/lib/demo/demo.dart b/example/lib/demo/demo.dart index b4657b9c..13e9a9f4 100644 --- a/example/lib/demo/demo.dart +++ b/example/lib/demo/demo.dart @@ -212,8 +212,13 @@ class _ChatPageState extends State fontSize: 24, ); + final TextStyle halloweenMenuItemTextStyle = GoogleFonts.hennyPenny( + color: Colors.white, + fontSize: 24, + ); + final halloweenActionButtonStyle = ActionButtonStyle( - tooltipTextStyle: halloweenTextStyle, + textStyle: halloweenTextStyle, iconColor: Colors.black, iconDecoration: BoxDecoration( color: Colors.orange, @@ -221,8 +226,13 @@ class _ChatPageState extends State ), ); + final halloweenMenuItemStyle = ActionButtonStyle( + textStyle: halloweenMenuItemTextStyle, + iconColor: Colors.white, + ); + final halloweenMenuButtonStyle = ActionButtonStyle( - tooltipTextStyle: halloweenTextStyle, + textStyle: halloweenTextStyle, iconColor: Colors.orange, iconDecoration: BoxDecoration( color: Colors.black, @@ -233,6 +243,7 @@ class _ChatPageState extends State return LlmChatViewStyle( backgroundColor: Colors.transparent, + menuColor: Colors.grey.shade600, progressIndicatorColor: Colors.purple, suggestionStyle: SuggestionStyle( textStyle: halloweenTextStyle.copyWith(color: Colors.black), @@ -320,14 +331,14 @@ class _ChatPageState extends State stopButtonStyle: halloweenActionButtonStyle, submitButtonStyle: halloweenActionButtonStyle, addButtonStyle: halloweenActionButtonStyle, - attachFileButtonStyle: halloweenMenuButtonStyle, - cameraButtonStyle: halloweenMenuButtonStyle, + attachFileButtonStyle: halloweenMenuItemStyle, + cameraButtonStyle: halloweenMenuItemStyle, closeButtonStyle: halloweenActionButtonStyle, cancelButtonStyle: halloweenActionButtonStyle, closeMenuButtonStyle: halloweenActionButtonStyle, copyButtonStyle: halloweenMenuButtonStyle, editButtonStyle: halloweenMenuButtonStyle, - galleryButtonStyle: halloweenMenuButtonStyle, + galleryButtonStyle: halloweenMenuItemStyle, actionButtonBarDecoration: BoxDecoration( color: Colors.orange, borderRadius: BorderRadius.circular(8), diff --git a/example/lib/styles/styles.dart b/example/lib/styles/styles.dart index 3597e93c..79a5783c 100644 --- a/example/lib/styles/styles.dart +++ b/example/lib/styles/styles.dart @@ -72,7 +72,7 @@ class _ChatPageState extends State ); final halloweenActionButtonStyle = ActionButtonStyle( - tooltipTextStyle: halloweenTextStyle, + textStyle: halloweenTextStyle, iconColor: Colors.black, iconDecoration: BoxDecoration( color: Colors.orange, @@ -81,7 +81,7 @@ class _ChatPageState extends State ); final halloweenMenuButtonStyle = ActionButtonStyle( - tooltipTextStyle: halloweenTextStyle, + textStyle: halloweenTextStyle, iconColor: Colors.orange, iconDecoration: BoxDecoration( color: Colors.black, diff --git a/example/macos/Flutter/GeneratedPluginRegistrant.swift b/example/macos/Flutter/GeneratedPluginRegistrant.swift index 388dbf4d..20c23bad 100644 --- a/example/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -10,7 +10,7 @@ import firebase_app_check import firebase_auth import firebase_core import path_provider_foundation -import record_darwin +import record_macos import shared_preferences_foundation import url_launcher_macos @@ -20,7 +20,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin")) FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) - RecordPlugin.register(with: registry.registrar(forPlugin: "RecordPlugin")) + RecordMacOsPlugin.register(with: registry.registrar(forPlugin: "RecordMacOsPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) } diff --git a/example/macos/Podfile.lock b/example/macos/Podfile.lock index ea28f7ac..edcac46e 100644 --- a/example/macos/Podfile.lock +++ b/example/macos/Podfile.lock @@ -5,51 +5,51 @@ PODS: - PromisesObjC (~> 2.4) - file_selector_macos (0.0.1): - FlutterMacOS - - Firebase/AppCheck (11.8.1): + - Firebase/AppCheck (11.10.0): - Firebase/CoreOnly - - FirebaseAppCheck (~> 11.8.0) - - Firebase/Auth (11.8.1): + - FirebaseAppCheck (~> 11.10.0) + - Firebase/Auth (11.10.0): - Firebase/CoreOnly - - FirebaseAuth (~> 11.8.1) - - Firebase/CoreOnly (11.8.1): - - FirebaseCore (~> 11.8.1) - - firebase_app_check (0.3.2-4): - - Firebase/AppCheck (~> 11.8.0) - - Firebase/CoreOnly (~> 11.8.0) + - FirebaseAuth (~> 11.10.0) + - Firebase/CoreOnly (11.10.0): + - FirebaseCore (~> 11.10.0) + - firebase_app_check (0.3.2-5): + - Firebase/AppCheck (~> 11.10.0) + - Firebase/CoreOnly (~> 11.10.0) - firebase_core - FlutterMacOS - - firebase_auth (5.5.1): - - Firebase/Auth (~> 11.8.0) - - Firebase/CoreOnly (~> 11.8.0) + - firebase_auth (5.5.2): + - Firebase/Auth (~> 11.10.0) + - Firebase/CoreOnly (~> 11.10.0) - firebase_core - FlutterMacOS - - firebase_core (3.12.1): - - Firebase/CoreOnly (~> 11.8.0) + - firebase_core (3.13.0): + - Firebase/CoreOnly (~> 11.10.0) - FlutterMacOS - - FirebaseAppCheck (11.8.0): + - FirebaseAppCheck (11.10.0): - AppCheckCore (~> 11.0) - FirebaseAppCheckInterop (~> 11.0) - - FirebaseCore (~> 11.8.0) + - FirebaseCore (~> 11.10.0) - GoogleUtilities/Environment (~> 8.0) - GoogleUtilities/UserDefaults (~> 8.0) - - FirebaseAppCheckInterop (11.9.0) - - FirebaseAuth (11.8.1): + - FirebaseAppCheckInterop (11.12.0) + - FirebaseAuth (11.10.0): - FirebaseAppCheckInterop (~> 11.0) - FirebaseAuthInterop (~> 11.0) - - FirebaseCore (~> 11.8.0) - - FirebaseCoreExtension (~> 11.8.0) + - FirebaseCore (~> 11.10.0) + - FirebaseCoreExtension (~> 11.10.0) - GoogleUtilities/AppDelegateSwizzler (~> 8.0) - GoogleUtilities/Environment (~> 8.0) - GTMSessionFetcher/Core (< 5.0, >= 3.4) - - RecaptchaInterop (~> 100.0) - - FirebaseAuthInterop (11.9.0) - - FirebaseCore (11.8.1): - - FirebaseCoreInternal (~> 11.8.0) + - RecaptchaInterop (~> 101.0) + - FirebaseAuthInterop (11.12.0) + - FirebaseCore (11.10.0): + - FirebaseCoreInternal (~> 11.10.0) - GoogleUtilities/Environment (~> 8.0) - GoogleUtilities/Logger (~> 8.0) - - FirebaseCoreExtension (11.8.0): - - FirebaseCore (~> 11.8.0) - - FirebaseCoreInternal (11.8.0): + - FirebaseCoreExtension (11.10.0): + - FirebaseCore (~> 11.10.0) + - FirebaseCoreInternal (11.10.0): - "GoogleUtilities/NSData+zlib (~> 8.0)" - FlutterMacOS (1.0.0) - GoogleUtilities/AppDelegateSwizzler (8.0.2): @@ -81,7 +81,7 @@ PODS: - Flutter - FlutterMacOS - PromisesObjC (2.4.0) - - record_darwin (1.0.0): + - record_macos (1.0.0): - FlutterMacOS - shared_preferences_foundation (0.0.1): - Flutter @@ -96,7 +96,7 @@ DEPENDENCIES: - firebase_core (from `Flutter/ephemeral/.symlinks/plugins/firebase_core/macos`) - FlutterMacOS (from `Flutter/ephemeral`) - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) - - record_darwin (from `Flutter/ephemeral/.symlinks/plugins/record_darwin/macos`) + - record_macos (from `Flutter/ephemeral/.symlinks/plugins/record_macos/macos`) - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) @@ -128,8 +128,8 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral path_provider_foundation: :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin - record_darwin: - :path: Flutter/ephemeral/.symlinks/plugins/record_darwin/macos + record_macos: + :path: Flutter/ephemeral/.symlinks/plugins/record_macos/macos shared_preferences_foundation: :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin url_launcher_macos: @@ -138,23 +138,23 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: AppCheckCore: cc8fd0a3a230ddd401f326489c99990b013f0c4f file_selector_macos: cc3858c981fe6889f364731200d6232dac1d812d - Firebase: 973c28133496aab0223e9ea99f0abafb9f91ad58 - firebase_app_check: 5f0a67ce108f92bfab91be15487a608dea42ccb4 - firebase_auth: efb15103917fdd30befc12f4049ba8bc34cbc659 - firebase_core: 1b573eac37729348cdc472516991dd7e269ae37e - FirebaseAppCheck: f6648d6d2b321ecf94cf72f6737fc68d4fddc010 - FirebaseAppCheckInterop: 9226f7217b43e99dfa0bc9f674ad8108cef89feb - FirebaseAuth: ad59a1a7b161e75f74c39f70179d2482d40e2737 - FirebaseAuthInterop: 2a26ee1bea6d47df8048683cfa071e7da657798f - FirebaseCore: 99fe0c4b44a39f37d99e6404e02009d2db5d718d - FirebaseCoreExtension: 3d3f2017a00d06e09ab4ebe065391b0bb642565e - FirebaseCoreInternal: df24ce5af28864660ecbd13596fc8dd3a8c34629 + Firebase: 1fe1c0a7d9aaea32efe01fbea5f0ebd8d70e53a2 + firebase_app_check: 9498006ad0fd951c98efff5700e1b87d020675d3 + firebase_auth: 2271b9240513461dfcdcf7cfa0121893acdb2de7 + firebase_core: bb06473757206589a00a36920cbf0f33646e19cc + FirebaseAppCheck: 9687ebd909702469bc09d2d58008697b83f4ac27 + FirebaseAppCheckInterop: 73b173e5ec45192e2d522ad43f526a82ad10b852 + FirebaseAuth: c4146bdfdc87329f9962babd24dae89373f49a32 + FirebaseAuthInterop: b583210c039a60ed3f1e48865e1f3da44a796595 + FirebaseCore: 8344daef5e2661eb004b177488d6f9f0f24251b7 + FirebaseCoreExtension: 6f357679327f3614e995dc7cf3f2d600bdc774ac + FirebaseCoreInternal: ef4505d2afb1d0ebbc33162cb3795382904b5679 FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d GTMSessionFetcher: 75b671f9e551e4c49153d4c4f8659ef4f559b970 path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 - record_darwin: a0d515a0ef78c440c123ea3ac76184c9927a94d6 + record_macos: 3ead198d39fad25d10163780132a96b6fd162a1c shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 url_launcher_macos: c82c93949963e55b228a30115bd219499a6fe404 diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 43bb55f4..d5c96db8 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -2,6 +2,7 @@ name: flutter_ai_toolkit_example description: >- Sample apps showing off various features of the Flutter AI Toolkit. publish_to: none +version: 1.0.0+1 environment: sdk: ^3.7.0 @@ -14,8 +15,6 @@ dependencies: path: .. cupertino_icons: ^1.0.8 google_generative_ai: ^0.4.3 - firebase_core: ^3.4.0 - firebase_vertexai: ^1.0.1 shared_preferences: ^2.3.2 url_launcher: ^6.3.0 go_router: ^14.2.8 @@ -26,6 +25,8 @@ dependencies: google_fonts: ^6.2.1 future_builder_ex: ^5.0.0 split_view: ^3.2.1 + firebase_core: ^3.13.0 + firebase_vertexai: ^1.5.0 dev_dependencies: flutter_test: diff --git a/lib/src/styles/action_button_style.dart b/lib/src/styles/action_button_style.dart index 3662cfbd..01aa9727 100644 --- a/lib/src/styles/action_button_style.dart +++ b/lib/src/styles/action_button_style.dart @@ -17,9 +17,8 @@ class ActionButtonStyle { this.icon, this.iconColor, this.iconDecoration, - this.tooltip, - this.tooltipTextStyle, - this.tooltipDecoration, + this.text, + this.textStyle, }); /// Resolves the provided [style] with the [defaultStyle]. @@ -38,10 +37,8 @@ class ActionButtonStyle { icon: style?.icon ?? defaultStyle.icon, iconColor: style?.iconColor ?? defaultStyle.iconColor, iconDecoration: style?.iconDecoration ?? defaultStyle.iconDecoration, - tooltip: style?.tooltip ?? defaultStyle.tooltip, - tooltipTextStyle: style?.tooltipTextStyle ?? defaultStyle.tooltipTextStyle, - tooltipDecoration: - style?.tooltipDecoration ?? defaultStyle.tooltipDecoration, + text: style?.text ?? defaultStyle.text, + textStyle: style?.textStyle ?? defaultStyle.textStyle, ); /// Provides default style for icon buttons. @@ -53,82 +50,80 @@ class ActionButtonStyle { IconData icon; var color = ToolkitColors.darkIcon; var bgColor = ToolkitColors.lightButtonBackground; - String tooltip; - final tooltipTextStyle = ToolkitTextStyles.tooltip; - const tooltipDecoration = BoxDecoration( - color: ToolkitColors.tooltipBackground, - borderRadius: BorderRadius.all(Radius.circular(4)), - ); + String text; + TextStyle textStyle = ToolkitTextStyles.tooltip; switch (type) { case ActionButtonType.add: icon = ToolkitIcons.add; - tooltip = 'Add Attachment'; + text = 'Add Attachment'; case ActionButtonType.attachFile: icon = ToolkitIcons.attach_file; - color = ToolkitColors.whiteIcon; - bgColor = ToolkitColors.darkButtonBackground; - tooltip = 'Attach File'; + color = ToolkitColors.darkIcon; + bgColor = ToolkitColors.transparent; + text = 'Attach File'; + textStyle = ToolkitTextStyles.body2; case ActionButtonType.camera: icon = ToolkitIcons.camera_alt; - color = ToolkitColors.whiteIcon; - bgColor = ToolkitColors.darkButtonBackground; - tooltip = 'Take Photo'; + color = ToolkitColors.darkIcon; + bgColor = ToolkitColors.transparent; + text = 'Take Photo'; + textStyle = ToolkitTextStyles.body2; case ActionButtonType.stop: icon = ToolkitIcons.stop; - tooltip = 'Stop'; + text = 'Stop'; case ActionButtonType.close: icon = ToolkitIcons.close; color = ToolkitColors.whiteIcon; bgColor = ToolkitColors.darkButtonBackground; - tooltip = 'Close'; + text = 'Close'; case ActionButtonType.cancel: icon = ToolkitIcons.close; color = ToolkitColors.whiteIcon; bgColor = ToolkitColors.darkButtonBackground; - tooltip = 'Cancel'; + text = 'Cancel'; case ActionButtonType.copy: icon = ToolkitIcons.content_copy; color = ToolkitColors.whiteIcon; bgColor = ToolkitColors.darkButtonBackground; - tooltip = 'Copy to Clipboard'; + text = 'Copy to Clipboard'; case ActionButtonType.edit: icon = ToolkitIcons.edit; color = ToolkitColors.whiteIcon; bgColor = ToolkitColors.darkButtonBackground; - tooltip = 'Edit Message'; + text = 'Edit Message'; case ActionButtonType.gallery: icon = ToolkitIcons.image; - color = ToolkitColors.whiteIcon; - bgColor = ToolkitColors.darkButtonBackground; - tooltip = 'Image Gallery'; + color = ToolkitColors.darkIcon; + bgColor = ToolkitColors.transparent; + text = 'Attach Image'; + textStyle = ToolkitTextStyles.body2; case ActionButtonType.record: icon = ToolkitIcons.mic; - tooltip = 'Record Audio'; + text = 'Record Audio'; case ActionButtonType.submit: icon = ToolkitIcons.submit_icon; color = ToolkitColors.whiteIcon; bgColor = ToolkitColors.darkButtonBackground; - tooltip = 'Submit Message'; + text = 'Submit Message'; case ActionButtonType.disabled: icon = ToolkitIcons.submit_icon; color = ToolkitColors.darkIcon; bgColor = ToolkitColors.disabledButton; - tooltip = ''; + text = ''; case ActionButtonType.closeMenu: icon = ToolkitIcons.close; color = ToolkitColors.whiteIcon; bgColor = ToolkitColors.greyBackground; - tooltip = 'Close Menu'; + text = 'Close Menu'; } return ActionButtonStyle( icon: icon, iconColor: color, iconDecoration: BoxDecoration(color: bgColor, shape: BoxShape.circle), - tooltip: tooltip, - tooltipTextStyle: tooltipTextStyle, - tooltipDecoration: tooltipDecoration, + text: text, + textStyle: textStyle, ); } @@ -141,12 +136,9 @@ class ActionButtonStyle { /// The decoration for the icon. final Decoration? iconDecoration; - /// The tooltip for the icon button. - final String? tooltip; + /// The tooltip for the icon button (could be menu item text or a tooltip). + final String? text; /// The text style of the tooltip. - final TextStyle? tooltipTextStyle; - - /// The decoration of the tooltip. - final Decoration? tooltipDecoration; + final TextStyle? textStyle; } diff --git a/lib/src/styles/llm_chat_view_style.dart b/lib/src/styles/llm_chat_view_style.dart index 8210fd02..97f74894 100644 --- a/lib/src/styles/llm_chat_view_style.dart +++ b/lib/src/styles/llm_chat_view_style.dart @@ -19,6 +19,7 @@ class LlmChatViewStyle { /// Creates a style object for the chat widget. const LlmChatViewStyle({ this.backgroundColor, + this.menuColor, this.progressIndicatorColor, this.userMessageStyle, this.llmMessageStyle, @@ -57,6 +58,7 @@ class LlmChatViewStyle { defaultStyle ??= LlmChatViewStyle.defaultStyle(); return LlmChatViewStyle( backgroundColor: style?.backgroundColor ?? defaultStyle.backgroundColor, + menuColor: style?.menuColor ?? defaultStyle.menuColor, progressIndicatorColor: style?.progressIndicatorColor ?? defaultStyle.progressIndicatorColor, userMessageStyle: UserMessageStyle.resolve( @@ -143,6 +145,7 @@ class LlmChatViewStyle { /// Provides a default light style. factory LlmChatViewStyle._lightStyle() => LlmChatViewStyle( backgroundColor: ToolkitColors.containerBackground, + menuColor: ToolkitColors.containerBackground, progressIndicatorColor: ToolkitColors.black, userMessageStyle: UserMessageStyle.defaultStyle(), llmMessageStyle: LlmMessageStyle.defaultStyle(), @@ -176,6 +179,9 @@ class LlmChatViewStyle { /// Background color of the entire chat widget. final Color? backgroundColor; + /// The color of the menu. + final Color? menuColor; + /// The color of the progress indicator. final Color? progressIndicatorColor; diff --git a/lib/src/views/action_button/action_button.dart b/lib/src/views/action_button.dart similarity index 88% rename from lib/src/views/action_button/action_button.dart rename to lib/src/views/action_button.dart index 6ff31d37..bb26adf0 100644 --- a/lib/src/views/action_button/action_button.dart +++ b/lib/src/views/action_button.dart @@ -5,8 +5,8 @@ import 'package:flutter/material.dart' show Tooltip; import 'package:flutter/widgets.dart'; -import '../../styles/action_button_style.dart'; -import '../../utility.dart'; +import '../styles/action_button_style.dart'; +import '../utility.dart'; /// A button widget with an icon. /// @@ -48,9 +48,8 @@ class ActionButton extends StatelessWidget { isCupertinoApp(context) ? Icon(style.icon, color: style.iconColor, size: size * 0.6) : Tooltip( - message: style.tooltip, - textStyle: style.tooltipTextStyle, - decoration: style.tooltipDecoration, + message: style.text, + textStyle: style.textStyle, child: Icon( style.icon, color: style.iconColor, diff --git a/lib/src/views/action_button/action_button_bar.dart b/lib/src/views/action_button/action_button_bar.dart deleted file mode 100644 index b6776155..00000000 --- a/lib/src/views/action_button/action_button_bar.dart +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2024 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/widgets.dart'; - -import '../../styles/styles.dart'; -import 'action_button.dart'; - -/// A widget that displays a horizontal bar of [ActionButton]s. -/// -/// This widget creates a container with rounded corners that houses a series of -/// [ActionButton]s. The buttons are laid out horizontally and can overflow if -/// there's not enough space. -@immutable -class ActionButtonBar extends StatelessWidget { - /// Creates a [ActionButtonBar]. - /// - /// The [buttons] parameter is required and specifies the list of - /// [ActionButton]s to be displayed in the bar. - const ActionButtonBar(this.buttons, {required this.style, super.key}); - - /// The list of [ActionButton]s to be displayed in the bar. - final List buttons; - - /// The style of the action button bar. - final LlmChatViewStyle style; - - @override - Widget build(BuildContext context) => DecoratedBox( - decoration: style.actionButtonBarDecoration!, - child: OverflowBar(children: buttons), - ); -} diff --git a/lib/src/views/chat_input/attachments_action_bar.dart b/lib/src/views/chat_input/attachments_action_bar.dart index fb4066fa..692a8803 100644 --- a/lib/src/views/chat_input/attachments_action_bar.dart +++ b/lib/src/views/chat_input/attachments_action_bar.dart @@ -5,6 +5,8 @@ import 'dart:async'; import 'package:file_selector/file_selector.dart'; +import 'package:flutter/material.dart' + show MenuAnchor, MenuItemButton, MenuStyle; import 'package:flutter/widgets.dart'; import 'package:image_picker/image_picker.dart'; @@ -13,8 +15,7 @@ import '../../dialogs/adaptive_snack_bar/adaptive_snack_bar.dart'; import '../../platform_helper/platform_helper.dart'; import '../../providers/interface/attachments.dart'; import '../../styles/llm_chat_view_style.dart'; -import '../action_button/action_button.dart'; -import '../action_button/action_button_bar.dart'; +import '../action_button.dart'; /// A widget that provides an action bar for attaching files or images. @immutable @@ -35,7 +36,6 @@ class AttachmentActionBar extends StatefulWidget { } class _AttachmentActionBarState extends State { - var _expanded = false; late final bool _canCamera; @override @@ -48,40 +48,60 @@ class _AttachmentActionBarState extends State { Widget build(BuildContext context) => ChatViewModelClient( builder: (context, viewModel, child) { final chatStyle = LlmChatViewStyle.resolve(viewModel.style); - return _expanded - ? ActionButtonBar(style: chatStyle, [ - ActionButton( - onPressed: _onToggleMenu, - style: chatStyle.closeMenuButtonStyle!, + return MenuAnchor( + style: MenuStyle( + backgroundColor: WidgetStateProperty.all(chatStyle.menuColor), + ), + consumeOutsideTap: true, + builder: + (_, controller, _) => ActionButton( + onPressed: controller.isOpen ? controller.close : controller.open, + style: chatStyle.addButtonStyle!, ), - if (_canCamera) - ActionButton( - onPressed: _onCamera, - style: chatStyle.cameraButtonStyle!, + menuChildren: [ + if (_canCamera) + MenuItemButton( + leadingIcon: Icon( + chatStyle.cameraButtonStyle!.icon!, + color: chatStyle.cameraButtonStyle!.iconColor, ), - ActionButton( - onPressed: _onGallery, - style: chatStyle.galleryButtonStyle!, + onPressed: () => _onCamera(), + child: Text( + chatStyle.cameraButtonStyle!.text!, + style: chatStyle.cameraButtonStyle!.textStyle, + ), + ), + MenuItemButton( + leadingIcon: Icon( + chatStyle.galleryButtonStyle!.icon!, + color: chatStyle.galleryButtonStyle!.iconColor, + ), + onPressed: () => _onGallery(), + child: Text( + chatStyle.galleryButtonStyle!.text!, + style: chatStyle.galleryButtonStyle!.textStyle, ), - ActionButton( - onPressed: _onFile, - style: chatStyle.attachFileButtonStyle!, + ), + MenuItemButton( + leadingIcon: Icon( + chatStyle.attachFileButtonStyle!.icon!, + color: chatStyle.attachFileButtonStyle!.iconColor, ), - ]) - : ActionButton( - onPressed: _onToggleMenu, - style: chatStyle.addButtonStyle!, - ); + onPressed: () => _onFile(), + child: Text( + chatStyle.attachFileButtonStyle!.text!, + style: chatStyle.attachFileButtonStyle!.textStyle, + ), + ), + ], + ); }, ); - void _onToggleMenu() => setState(() => _expanded = !_expanded); void _onCamera() => unawaited(_pickImage(ImageSource.camera)); void _onGallery() => unawaited(_pickImage(ImageSource.gallery)); Future _pickImage(ImageSource source) async { - _onToggleMenu(); // close the menu - assert( source == ImageSource.camera || source == ImageSource.gallery, 'Unsupported image source: $source', @@ -110,8 +130,6 @@ class _AttachmentActionBarState extends State { } Future _onFile() async { - _onToggleMenu(); // close the menu - try { final files = await openFiles(); final attachments = await Future.wait(files.map(FileAttachment.fromFile)); diff --git a/lib/src/views/chat_input/chat_input.dart b/lib/src/views/chat_input/chat_input.dart index 1accbca5..2683fb1c 100644 --- a/lib/src/views/chat_input/chat_input.dart +++ b/lib/src/views/chat_input/chat_input.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'package:file_selector/file_selector.dart'; -import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:image_picker/image_picker.dart'; import 'package:waveform_recorder/waveform_recorder.dart'; @@ -13,15 +12,12 @@ import '../../chat_view_model/chat_view_model_provider.dart'; import '../../dialogs/adaptive_snack_bar/adaptive_snack_bar.dart'; import '../../providers/interface/attachments.dart'; import '../../providers/interface/chat_message.dart'; -import '../../styles/chat_input_style.dart'; -import '../../styles/llm_chat_view_style.dart'; -import '../../utility.dart'; -import '../chat_text_field.dart'; +import '../../styles/styles.dart'; import 'attachments_action_bar.dart'; import 'attachments_view.dart'; -import 'editing_indicator.dart'; import 'input_button.dart'; import 'input_state.dart'; +import 'text_or_audio_input.dart'; /// A widget that provides a chat input interface with support for text input, /// speech-to-text, and attachments. @@ -115,8 +111,6 @@ class _ChatInputState extends State { final _textController = TextEditingController(); final _waveController = WaveformRecorderController(); final _attachments = []; - static const _minInputHeight = 48.0; - static const _maxInputHeight = 144.0; ChatViewModel? _viewModel; ChatInputStyle? _inputStyle; @@ -176,78 +170,17 @@ class _ChatInputState extends State { ), ), Expanded( - child: Stack( - children: [ - Padding( - padding: EdgeInsets.only( - left: 16, - right: 16, - top: widget.onCancelEdit != null ? 24 : 8, - bottom: 8, - ), - child: DecoratedBox( - decoration: _inputStyle!.decoration!, - child: ConstrainedBox( - constraints: const BoxConstraints( - minHeight: _minInputHeight, - maxHeight: _maxInputHeight, - ), - child: - _waveController.isRecording - ? WaveformRecorder( - controller: _waveController, - height: _minInputHeight, - onRecordingStopped: - onRecordingStopped, - ) - : SingleChildScrollView( - child: ChatTextField( - minLines: 1, - maxLines: 1024, - controller: _textController, - autofocus: widget.autofocus, - focusNode: _focusNode, - textInputAction: - isMobile - ? TextInputAction - .newline - : TextInputAction.done, - onSubmitted: - _inputState == - InputState - .canSubmitPrompt - ? (_) => - onSubmitPrompt() - : (_) => - _focusNode - .requestFocus(), - style: _inputStyle!.textStyle!, - hintText: - _inputStyle!.hintText!, - hintStyle: - _inputStyle!.hintStyle!, - hintPadding: - const EdgeInsets.symmetric( - horizontal: 12, - vertical: 8, - ), - ), - ), - ), - ), - ), - Align( - alignment: Alignment.topRight, - child: - widget.onCancelEdit != null - ? EditingIndicator( - onCancelEdit: widget.onCancelEdit!, - cancelButtonStyle: - _chatStyle!.cancelButtonStyle!, - ) - : const SizedBox(), - ), - ], + child: TextOrAudioInput( + inputStyle: _inputStyle!, + waveController: _waveController, + onCancelEdit: widget.onCancelEdit, + onRecordingStopped: onRecordingStopped, + onSubmitPrompt: onSubmitPrompt, + textController: _textController, + focusNode: _focusNode, + autofocus: widget.autofocus, + inputState: _inputState, + cancelButtonStyle: _chatStyle!.cancelButtonStyle!, ), ), Padding( diff --git a/lib/src/views/chat_input/editing_indicator.dart b/lib/src/views/chat_input/editing_indicator.dart index 8beaa14e..a9560bdf 100644 --- a/lib/src/views/chat_input/editing_indicator.dart +++ b/lib/src/views/chat_input/editing_indicator.dart @@ -3,7 +3,7 @@ import 'package:flutter/widgets.dart'; import '../../styles/action_button_style.dart'; import '../../styles/toolkit_text_styles.dart'; import '../../utility.dart'; -import '../action_button/action_button.dart'; +import '../action_button.dart'; /// A widget that displays an editing indicator with a cancel button. /// diff --git a/lib/src/views/chat_input/input_button.dart b/lib/src/views/chat_input/input_button.dart index f2e0fa2a..174a5bf1 100644 --- a/lib/src/views/chat_input/input_button.dart +++ b/lib/src/views/chat_input/input_button.dart @@ -5,7 +5,7 @@ import 'package:flutter/widgets.dart'; import '../../styles/llm_chat_view_style.dart'; -import '../action_button/action_button.dart'; +import '../action_button.dart'; import '../adaptive_progress_indicator.dart'; import 'input_state.dart'; diff --git a/lib/src/views/chat_input/removable_attachment.dart b/lib/src/views/chat_input/removable_attachment.dart index c837c304..214cc7b6 100644 --- a/lib/src/views/chat_input/removable_attachment.dart +++ b/lib/src/views/chat_input/removable_attachment.dart @@ -11,7 +11,7 @@ import '../../dialogs/adaptive_dialog.dart'; import '../../dialogs/image_preview_dialog.dart'; import '../../providers/interface/attachments.dart'; import '../../styles/llm_chat_view_style.dart'; -import '../action_button/action_button.dart'; +import '../action_button.dart'; import '../attachment_view/attachment_view.dart'; /// A widget that displays an attachment with a remove button. diff --git a/lib/src/views/chat_input/text_or_audio_input.dart b/lib/src/views/chat_input/text_or_audio_input.dart new file mode 100644 index 00000000..50b3f4e9 --- /dev/null +++ b/lib/src/views/chat_input/text_or_audio_input.dart @@ -0,0 +1,100 @@ +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; +import 'package:waveform_recorder/waveform_recorder.dart'; + +import '../../styles/styles.dart'; +import '../../utility.dart'; +import '../chat_text_field.dart'; +import 'editing_indicator.dart'; +import 'input_state.dart'; + +class TextOrAudioInput extends StatelessWidget { + const TextOrAudioInput({ + super.key, + required this.inputStyle, + required this.waveController, + required this.onCancelEdit, + required this.onRecordingStopped, + required this.onSubmitPrompt, + required this.textController, + required this.focusNode, + required this.autofocus, + required this.inputState, + required this.cancelButtonStyle, + }); + + final ChatInputStyle inputStyle; + final WaveformRecorderController waveController; + final void Function()? onCancelEdit; + final void Function() onRecordingStopped; + final void Function() onSubmitPrompt; + final TextEditingController textController; + final FocusNode focusNode; + final bool autofocus; + final InputState inputState; + final ActionButtonStyle cancelButtonStyle; + static const _minInputHeight = 48.0; + static const _maxInputHeight = 144.0; + + @override + Widget build(BuildContext context) => Stack( + children: [ + Padding( + padding: EdgeInsets.only( + left: 16, + right: 16, + top: onCancelEdit != null ? 24 : 8, + bottom: 8, + ), + child: DecoratedBox( + decoration: inputStyle.decoration!, + child: ConstrainedBox( + constraints: const BoxConstraints( + minHeight: _minInputHeight, + maxHeight: _maxInputHeight, + ), + child: + waveController.isRecording + ? WaveformRecorder( + controller: waveController, + height: _minInputHeight, + onRecordingStopped: onRecordingStopped, + ) + : ChatTextField( + minLines: 1, + maxLines: 1024, + controller: textController, + autofocus: autofocus, + focusNode: focusNode, + textInputAction: + isMobile + ? TextInputAction.newline + : TextInputAction.done, + onSubmitted: + inputState == InputState.canSubmitPrompt + ? (_) => onSubmitPrompt() + : (_) => focusNode.requestFocus(), + style: inputStyle.textStyle!, + hintText: inputStyle.hintText!, + hintStyle: inputStyle.hintStyle!, + hintPadding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 8, + ), + ), + ), + ), + ), + Align( + alignment: Alignment.topRight, + child: + onCancelEdit != null + ? EditingIndicator( + onCancelEdit: onCancelEdit!, + cancelButtonStyle: cancelButtonStyle, + ) + : const SizedBox(), + ), + ], + ); +} diff --git a/lib/src/views/chat_message_view/adaptive_copy_text.dart b/lib/src/views/chat_message_view/adaptive_copy_text.dart index 3e86c6c2..6acdabb1 100644 --- a/lib/src/views/chat_message_view/adaptive_copy_text.dart +++ b/lib/src/views/chat_message_view/adaptive_copy_text.dart @@ -1,6 +1,11 @@ import 'dart:async'; -import 'package:flutter/material.dart' show SelectionArea; +import 'package:flutter/cupertino.dart' show DefaultCupertinoLocalizations; +import 'package:flutter/material.dart' + show + DefaultMaterialLocalizations, + SelectionArea, + DefaultWidgetsLocalizations; import 'package:flutter/widgets.dart'; import 'package:flutter_context_menu/flutter_context_menu.dart'; @@ -63,6 +68,17 @@ class AdaptiveCopyText extends StatelessWidget { // on desktop and web, show the selection area for mouse-driven selection. return isMobile ? ContextMenuRegion(contextMenu: contextMenu, child: child) + : isCupertinoApp(context) + // Ensure MaterialLocalizations is available for SelectionArea + ? Localizations( + locale: Localizations.localeOf(context), + delegates: const [ + DefaultMaterialLocalizations.delegate, + DefaultWidgetsLocalizations.delegate, + DefaultCupertinoLocalizations.delegate, + ], + child: SelectionArea(child: child), + ) : SelectionArea(child: child); } } diff --git a/pubspec.yaml b/pubspec.yaml index bd0b99fe..c8137d9d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: flutter_ai_toolkit description: >- A set of AI chat-related widgets for Flutter apps targeting mobile, desktop, and web. -version: 0.7.1 +version: 0.8.0 repository: https://github.com/flutter/ai topics: