├── android ├── app │ ├── build_config │ │ └── README.md │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── drawable │ │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable-v21 │ │ │ │ │ └── launch_background.xml │ │ │ │ ├── values │ │ │ │ │ └── styles.xml │ │ │ │ └── values-night │ │ │ │ │ └── styles.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── somrit │ │ │ │ │ └── hypebard │ │ │ │ │ └── MainActivity.kt │ │ │ └── AndroidManifest.xml │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ └── profile │ │ │ └── AndroidManifest.xml │ └── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── .gitignore ├── settings.gradle └── build.gradle ├── ios ├── Runner │ ├── Runner-Bridging-Header.h │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── _ │ │ │ │ ├── 29.png │ │ │ │ ├── 40.png │ │ │ │ ├── 57.png │ │ │ │ ├── 58.png │ │ │ │ ├── 60.png │ │ │ │ ├── 80.png │ │ │ │ ├── 87.png │ │ │ │ ├── 1024.png │ │ │ │ ├── 114.png │ │ │ │ ├── 120.png │ │ │ │ └── 180.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ └── Contents.json │ │ └── LaunchImage.imageset │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ ├── README.md │ │ │ └── Contents.json │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard │ └── Info.plist ├── Flutter │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── AppFrameworkInfo.plist ├── Runner.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ ├── xcshareddata │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ └── project.pbxproj ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── WorkspaceSettings.xcsettings │ │ └── IDEWorkspaceChecks.plist ├── .gitignore ├── Podfile └── Podfile.lock ├── images ├── dev.png ├── you.png ├── bard.png ├── logo.png ├── key_icon.png ├── tip_icon.png ├── url_icon.png ├── arrow_icon.png ├── back_icon.png ├── close_icon.png ├── debug_icon.png ├── email_icon.png ├── gift_icon.png ├── share_icon.png ├── star_icon.png ├── submit_icon.png ├── user_icon.png ├── voice_icon.png ├── refresh_icon.png ├── chat_copy_icon.png ├── microphone_icon.png ├── share_message_icon.png ├── submit_active_icon.png ├── privacy_policy_icon.png └── microphone_active_icon.png ├── fonts ├── Poppins-Bold.ttf ├── Poppins-Regular.ttf ├── Poppins-SemiBold.ttf └── Poppins-ExtraLight.ttf ├── screenshots ├── screenshot1.png ├── screenshot2.png └── screenshot3.png ├── lib ├── utils │ ├── Time.dart │ ├── Config.dart │ ├── Utils.dart │ └── Chatgpt.dart ├── components │ ├── HideKeyboard.dart │ └── QuestionInput.dart ├── main.dart ├── page │ ├── AppOpenPage.dart │ ├── PrivacyPolicyPage.dart │ ├── ChatHistoryPage.dart │ ├── ChatPage.dart │ ├── SettingPage.dart │ └── HomePage.dart └── stores │ └── AIChatStore.dart ├── test └── widget_test.dart ├── .gitignore ├── LICENSE ├── pubspec.yaml ├── .github └── workflows │ └── dart.yml ├── analysis_options.yaml ├── .metadata └── README.md /android/app/build_config/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /images/dev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/images/dev.png -------------------------------------------------------------------------------- /images/you.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/images/you.png -------------------------------------------------------------------------------- /images/bard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/images/bard.png -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/images/logo.png -------------------------------------------------------------------------------- /images/key_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/images/key_icon.png -------------------------------------------------------------------------------- /images/tip_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/images/tip_icon.png -------------------------------------------------------------------------------- /images/url_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/images/url_icon.png -------------------------------------------------------------------------------- /fonts/Poppins-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/fonts/Poppins-Bold.ttf -------------------------------------------------------------------------------- /images/arrow_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/images/arrow_icon.png -------------------------------------------------------------------------------- /images/back_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/images/back_icon.png -------------------------------------------------------------------------------- /images/close_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/images/close_icon.png -------------------------------------------------------------------------------- /images/debug_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/images/debug_icon.png -------------------------------------------------------------------------------- /images/email_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/images/email_icon.png -------------------------------------------------------------------------------- /images/gift_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/images/gift_icon.png -------------------------------------------------------------------------------- /images/share_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/images/share_icon.png -------------------------------------------------------------------------------- /images/star_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/images/star_icon.png -------------------------------------------------------------------------------- /images/submit_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/images/submit_icon.png -------------------------------------------------------------------------------- /images/user_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/images/user_icon.png -------------------------------------------------------------------------------- /images/voice_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/images/voice_icon.png -------------------------------------------------------------------------------- /images/refresh_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/images/refresh_icon.png -------------------------------------------------------------------------------- /fonts/Poppins-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/fonts/Poppins-Regular.ttf -------------------------------------------------------------------------------- /fonts/Poppins-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/fonts/Poppins-SemiBold.ttf -------------------------------------------------------------------------------- /images/chat_copy_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/images/chat_copy_icon.png -------------------------------------------------------------------------------- /images/microphone_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/images/microphone_icon.png -------------------------------------------------------------------------------- /screenshots/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/screenshots/screenshot1.png -------------------------------------------------------------------------------- /screenshots/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/screenshots/screenshot2.png -------------------------------------------------------------------------------- /screenshots/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/screenshots/screenshot3.png -------------------------------------------------------------------------------- /fonts/Poppins-ExtraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/fonts/Poppins-ExtraLight.ttf -------------------------------------------------------------------------------- /images/share_message_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/images/share_message_icon.png -------------------------------------------------------------------------------- /images/submit_active_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/images/submit_active_icon.png -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /images/privacy_policy_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/images/privacy_policy_icon.png -------------------------------------------------------------------------------- /images/microphone_active_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/images/microphone_active_icon.png -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/_/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/_/29.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/_/40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/_/40.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/_/57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/_/57.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/_/58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/_/58.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/_/60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/_/60.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/_/80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/_/80.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/_/87.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/_/87.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/_/1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/_/1024.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/_/114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/_/114.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/_/120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/_/120.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/_/180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/_/180.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/somritdasgupta/hypebard/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/somrit/hypebard/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.somrit.hypebard 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity : FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip 6 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, 6 | selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "idiom": "universal", 5 | "filename": "LaunchImage.png", 6 | "scale": "1x" 7 | }, 8 | { 9 | "idiom": "universal", 10 | "filename": "LaunchImage@2x.png", 11 | "scale": "2x" 12 | }, 13 | { 14 | "idiom": "universal", 15 | "filename": "LaunchImage@3x.png", 16 | "scale": "3x" 17 | } 18 | ], 19 | "info": { 20 | "version": 1, 21 | "author": "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/utils/Time.dart: -------------------------------------------------------------------------------- 1 | import 'package:intl/intl.dart'; 2 | 3 | class TimeUtils { 4 | getCurrentDate({String format = 'yyyy-MM-dd HH:mm:ss'}) { 5 | DateTime now = DateTime.now(); 6 | String formattedDate = DateFormat(format).format(now); 7 | return formattedDate; 8 | } 9 | 10 | formatTime(int timestamp, {String format = 'yyyy-MM-dd HH:mm:ss'}) { 11 | DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(timestamp); 12 | String formattedDate = DateFormat(format).format(dateTime); 13 | return formattedDate; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/components/HideKeyboard.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class HideKeyboard extends StatelessWidget { 4 | final Widget child; 5 | 6 | const HideKeyboard({Key? key, required this.child}) : super(key: key); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return GestureDetector( 11 | child: child, 12 | onTap: () { 13 | FocusScopeNode currentFocus = FocusScope.of(context); 14 | if (!currentFocus.hasPrimaryFocus && 15 | currentFocus.focusedChild != null) { 16 | FocusManager.instance.primaryFocus?.unfocus(); 17 | } 18 | }, 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.8.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.1.2' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | mavenCentral() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | tasks.register("clean", Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | import 'package:hypebard/main.dart'; 4 | 5 | void main() { 6 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 7 | // Build our app and trigger a frame. 8 | await tester.pumpWidget(const MyApp()); 9 | 10 | // Verify that our counter starts at 0. 11 | expect(find.text('0'), findsOneWidget); 12 | expect(find.text('1'), findsNothing); 13 | 14 | // Tap the '+' icon and trigger a frame. 15 | await tester.tap(find.byIcon(Icons.add)); 16 | await tester.pump(); 17 | 18 | // Verify that our counter has incremented. 19 | expect(find.text('0'), findsNothing); 20 | expect(find.text('1'), findsOneWidget); 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 11.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | **/doc/api/ 26 | **/ios/Flutter/.last_build_id 27 | .dart_tool/ 28 | .flutter-plugins 29 | .flutter-plugins-dependencies 30 | .packages 31 | .pub-cache/ 32 | .pub/ 33 | /build/ 34 | # Symbolication related 35 | app.*.symbols 36 | 37 | # Obfuscation related 38 | app.*.map.json 39 | 40 | # Android Studio will place build artifacts here 41 | /android/app/debug 42 | /android/app/profile 43 | /android/app/release 44 | 45 | /.fvm 46 | 47 | .env 48 | /images/ 49 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Somrit 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: hypebard 2 | description: hypeBard is your intelligent buddy. 3 | 4 | publish_to: "none" 5 | 6 | version: 1.0.0+2 7 | 8 | environment: 9 | sdk: ">=2.18.6 <3.0.0" 10 | 11 | dependencies: 12 | flutter: 13 | sdk: flutter 14 | cupertino_icons: ^1.0.5 15 | flutter_markdown: ^0.6.14 16 | uuid: ^3.0.7 17 | http: ^0.13.5 18 | sp_util: ^2.0.3 19 | shared_preferences: ^2.0.20 20 | intl: ^0.18.0 21 | dart_openai: ^1.9.1 22 | get_storage: ^2.1.1 23 | flutter_easyloading: ^3.0.5 24 | url_launcher: ^6.1.10 25 | share_plus: ^6.3.1 26 | rating_dialog: ^2.0.4 27 | lottie: ^2.3.1 28 | provider: ^6.0.5 29 | flutter_tts: ^3.6.3 30 | speech_to_text: ^6.1.1 31 | vibration: ^1.7.7 32 | animated_text_kit: ^4.2.2 33 | 34 | dev_dependencies: 35 | flutter_test: 36 | sdk: flutter 37 | flutter_lints: ^2.0.1 38 | build_runner: ^2.3.3 39 | flutter_dotenv: ^5.0.2 40 | change_app_package_name: ^1.1.0 41 | 42 | flutter: 43 | uses-material-design: true 44 | assets: 45 | - images/ 46 | - .env 47 | fonts: 48 | - family: Poppins 49 | fonts: 50 | - asset: fonts/Poppins-ExtraLight.ttf 51 | - asset: fonts/Poppins-Regular.ttf 52 | -------------------------------------------------------------------------------- /lib/utils/Config.dart: -------------------------------------------------------------------------------- 1 | import 'package:hypebard/utils/Chatgpt.dart'; 2 | 3 | class Config { 4 | static late Config _instance = Config._(); 5 | 6 | factory Config() => _getInstance(); 7 | 8 | static Config get instance => _getInstance(); 9 | 10 | Config._() {} 11 | 12 | static Config _getInstance() { 13 | if (_instance == null) { 14 | _instance = Config._(); 15 | } 16 | return _instance; 17 | } 18 | 19 | static bool get isDebug => !const bool.fromEnvironment('dart.vm.product'); 20 | 21 | // static bool get isDebug => true; 22 | 23 | /// TODO VIP 24 | static bool isAdShow() { 25 | if (isInfiniteNumberVersion) { 26 | return false; 27 | } 28 | // If a custom key is set, no ads are displayed 29 | if (ChatGPT.getCacheOpenAIKey() != '') { 30 | return false; 31 | } 32 | return true; 33 | } 34 | 35 | static bool isInfiniteNumberVersion = 36 | true; // Unlimited frequency. Development and use 37 | static String appName = 'hypeBard'; 38 | static String contactEmail = 'somritdasgupta@outlook.com'; 39 | static int watchAdApiCount = 3; 40 | static int appUserAdCount = 20; // Do not actively display advertisements if the number of times exceeds (redemption page) 41 | } 42 | -------------------------------------------------------------------------------- /.github/workflows/dart.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | 6 | name: Dart 7 | 8 | on: 9 | push: 10 | branches: [ "main" ] 11 | pull_request: 12 | branches: [ "main" ] 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - uses: actions/checkout@v3 20 | 21 | # Note: This workflow uses the latest stable version of the Dart SDK. 22 | # You can specify other versions if desired, see documentation here: 23 | # https://github.com/dart-lang/setup-dart/blob/main/README.md 24 | # - uses: dart-lang/setup-dart@v1 25 | - uses: dart-lang/setup-dart@9a04e6d73cca37bd455e0608d7e5092f881fd603 26 | 27 | - name: Install dependencies 28 | run: dart pub get 29 | 30 | # Uncomment this step to verify the use of 'dart format' on each commit. 31 | # - name: Verify formatting 32 | # run: dart format --output=none --set-exit-if-changed . 33 | 34 | # Consider passing '--fatal-infos' for slightly stricter analysis. 35 | - name: Analyze project source 36 | run: dart analyze 37 | 38 | # Your project will need to have tests in test/ and a dependency on 39 | # package:test for this step to succeed. Note that Flutter projects will 40 | # want to change this to 'flutter test'. 41 | - name: Run tests 42 | run: dart test 43 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | platform :ios, '11.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - flutter_tts (0.0.1): 4 | - Flutter 5 | - path_provider_foundation (0.0.1): 6 | - Flutter 7 | - FlutterMacOS 8 | - share_plus (0.0.1): 9 | - Flutter 10 | - shared_preferences_foundation (0.0.1): 11 | - Flutter 12 | - FlutterMacOS 13 | - url_launcher_ios (0.0.1): 14 | - Flutter 15 | 16 | DEPENDENCIES: 17 | - Flutter (from `Flutter`) 18 | - flutter_tts (from `.symlinks/plugins/flutter_tts/ios`) 19 | - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`) 20 | - share_plus (from `.symlinks/plugins/share_plus/ios`) 21 | - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/ios`) 22 | - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) 23 | 24 | EXTERNAL SOURCES: 25 | Flutter: 26 | :path: Flutter 27 | flutter_tts: 28 | :path: ".symlinks/plugins/flutter_tts/ios" 29 | path_provider_foundation: 30 | :path: ".symlinks/plugins/path_provider_foundation/ios" 31 | share_plus: 32 | :path: ".symlinks/plugins/share_plus/ios" 33 | shared_preferences_foundation: 34 | :path: ".symlinks/plugins/shared_preferences_foundation/ios" 35 | url_launcher_ios: 36 | :path: ".symlinks/plugins/url_launcher_ios/ios" 37 | 38 | SPEC CHECKSUMS: 39 | Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 40 | flutter_tts: 0f492aab6accf87059b72354fcb4ba934304771d 41 | path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9 42 | share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68 43 | shared_preferences_foundation: 986fc17f3d3251412d18b0265f9c64113a8c2472 44 | url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4 45 | 46 | PODFILE CHECKSUM: 7368163408c647b7eb699d0d788ba6718e18fb8d 47 | 48 | COCOAPODS: 1.12.0 49 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled. 5 | 6 | version: 7 | revision: 2ad6cd72c040113b47ee9055e722606a490ef0da 8 | channel: stable 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da 17 | base_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da 18 | - platform: android 19 | create_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da 20 | base_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da 21 | - platform: ios 22 | create_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da 23 | base_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da 24 | - platform: linux 25 | create_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da 26 | base_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da 27 | - platform: macos 28 | create_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da 29 | base_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da 30 | - platform: web 31 | create_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da 32 | base_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da 33 | - platform: windows 34 | create_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da 35 | base_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da 36 | 37 | # User provided section 38 | 39 | # List of Local paths (relative to this file) that should be 40 | # ignored by the migrate tool. 41 | # 42 | # Files that are not part of the templates will be ignored by default. 43 | unmanaged_files: 44 | - 'lib/main.dart' 45 | - 'ios/Runner.xcodeproj/project.pbxproj' 46 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | hypeBard 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | hypeBard 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UIViewControllerBasedStatusBarAppearance 45 | 46 | CADisableMinimumFrameDurationOnPhone 47 | 48 | UIApplicationSupportsIndirectInputEvents 49 | 50 | NSAppTransportSecurity 51 | 52 | NSAllowsLocalNetworking 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /lib/utils/Utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:shared_preferences/shared_preferences.dart'; 3 | import 'package:sp_util/sp_util.dart'; 4 | import 'package:url_launcher/url_launcher.dart'; 5 | 6 | class Utils { 7 | static Future prefs = SharedPreferences.getInstance(); 8 | 9 | static Utils get instance => _getInstance(); 10 | static Utils? _instance; 11 | 12 | static Utils _getInstance() { 13 | if (_instance == null) { 14 | _instance = Utils(); 15 | } 16 | return _instance!; 17 | } 18 | 19 | static jumpPage(BuildContext context, Widget widget) { 20 | PageRoute builder = MaterialPageRoute(builder: (context) { 21 | return widget; 22 | }); 23 | 24 | Navigator.push(context, builder); 25 | } 26 | 27 | static pushReplacement(BuildContext context, Widget widget) { 28 | PageRoute builder = PageRouteBuilder( 29 | transitionDuration: const Duration(milliseconds: 0), 30 | pageBuilder: (BuildContext context, Animation animation, 31 | Animation secondaryAnimation) { 32 | return widget; 33 | }, 34 | ); 35 | Navigator.pushReplacement(context, builder); 36 | } 37 | 38 | /// Save whether to install for the first time 39 | static void saveInstall() async { 40 | await SpUtil.getInstance(); 41 | SpUtil.putBool("install_key", true); 42 | } 43 | 44 | /// Is it the first time to install 45 | static Future getInstall() async { 46 | await SpUtil.getInstance(); 47 | bool? isInstall = SpUtil.getBool("install_key"); 48 | if (isInstall == null) { 49 | return false; 50 | } 51 | return isInstall; 52 | } 53 | 54 | static launchURL( 55 | Uri url, { 56 | LaunchMode mode = LaunchMode.externalNonBrowserApplication, 57 | Function? onLaunchFail, 58 | }) async { 59 | if (await canLaunchUrl(url)) { 60 | await launchUrl( 61 | url, 62 | mode: mode, 63 | ); 64 | } else { 65 | if (onLaunchFail != null) { 66 | onLaunchFail(); 67 | } 68 | throw 'Could not launch $url'; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/services.dart'; 5 | import 'package:flutter_dotenv/flutter_dotenv.dart' show dotenv; 6 | import 'package:flutter_easyloading/flutter_easyloading.dart'; 7 | import 'package:get_storage/get_storage.dart'; 8 | import 'package:hypebard/components/HideKeyboard.dart'; 9 | import 'package:hypebard/page/AppOpenPage.dart'; 10 | import 'package:hypebard/stores/AIChatStore.dart'; 11 | import 'package:hypebard/utils/Chatgpt.dart'; 12 | import 'package:provider/provider.dart'; 13 | 14 | void main() async { 15 | SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( 16 | statusBarColor: Colors.transparent, 17 | )); 18 | await dotenv.load(fileName: ".env"); 19 | 20 | await GetStorage.init(); 21 | await ChatGPT.initChatGPT(); 22 | runApp( 23 | ChangeNotifierProvider( 24 | create: (context) => AIChatStore(), 25 | child: const MyApp(), 26 | ), 27 | ); 28 | configLoading(); 29 | } 30 | 31 | void enterFullScreenButKeepBottomOverlay() { 32 | SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); 33 | } 34 | 35 | class MyApp extends StatelessWidget { 36 | const MyApp({super.key}); 37 | 38 | @override 39 | Widget build(BuildContext context) { 40 | return HideKeyboard( 41 | child: MaterialApp( 42 | debugShowCheckedModeBanner: false, 43 | theme: ThemeData( 44 | useMaterial3: true, 45 | scaffoldBackgroundColor: const Color(0xFFF6F1F1), 46 | brightness: Brightness.light, 47 | pageTransitionsTheme: const PageTransitionsTheme( 48 | builders: { 49 | TargetPlatform.android: FadeUpwardsPageTransitionsBuilder(), 50 | TargetPlatform.iOS: CupertinoPageTransitionsBuilder(), 51 | }, 52 | ), 53 | fontFamily: 'Poppins', 54 | ), 55 | home: const SplashPage(), 56 | builder: EasyLoading.init(), 57 | ), 58 | ); 59 | } 60 | } 61 | 62 | Future configLoading() async { 63 | EasyLoading.instance 64 | ..maskType = EasyLoadingMaskType.none 65 | ..loadingStyle = EasyLoadingStyle.dark 66 | ..indicatorSize = 45.0 67 | ..radius = 10.0 68 | ..displayDuration = const Duration(milliseconds: 1000) 69 | ..userInteractions = false; 70 | } 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) 2 | 3 |
4 |

Hypebard is an AI-powered writing assistant that makes writing easier and more enjoyable than ever before. Built using Flutter and written in Dart, Hypebard uses OpenAI's GPT technology to provide you with natural language processing capabilities that are second to none.

5 |
6 | 7 | 8 | ## Features 9 | 10 | * Advanced AI-powered writing assistance 11 | * Beautiful and intuitive UI 12 | * Support for all formatting: bold, italics, links, code blocks, and bullets 13 | 14 | ## Screenshots 15 | 16 | | Screenshot 1 | Screenshot 2 | Screenshot 3 | 17 | |----------------------------------------------|----------------------------------------------|----------------------------------------------| 18 | | ![Home UI](screenshots/screenshot1.png) | ![Chat UI](screenshots/screenshot2.png) | ![Customize API](screenshots/screenshot3.png) | 19 | 20 | ## Installation 21 | 22 | You can download the APK file from the releases section of this repository or build the app from source using the 23 | instructions below: 24 | 25 | ```bash 26 | git clone https://github.com/somritdasgupta/hypebard.git 27 | cd hypebard 28 | flutter build apk 29 | ```` 30 | 31 | ## Usage 32 | 33 | To use Hypebard, simply type or dictate your text into the app, and the AI will provide you with natural language 34 | processing assistance. You can also format your text using the provided formatting options. 35 | 36 | ## Contributing 37 | 38 | Contributions are welcome! Please read the [contribution guidelines](CONTRIBUTING.md) before getting started. 39 | 40 | ## License 41 | 42 | Hypebard is licensed under the [MIT License](LICENSE). 43 | 44 | ## Acknowledgements 45 | 46 | Hypebard was built using the following open-source libraries and tools: 47 | 48 | * [Flutter](https://flutter.dev/) 49 | * [Dart](https://dart.dev/) 50 | * [OpenAI GPT](https://beta.openai.com/) 51 | * [Travis CI](https://travis-ci.org/) 52 | * [Google Fonts](https://fonts.google.com/) 53 | 54 | 55 |

Coffee?

56 |

somritdasgupta



57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 20 | 28 | 32 | 35 | 36 | 37 | 38 | 39 | 40 | 42 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 33 30 | ndkVersion flutter.ndkVersion 31 | 32 | compileOptions { 33 | sourceCompatibility JavaVersion.VERSION_1_8 34 | targetCompatibility JavaVersion.VERSION_1_8 35 | } 36 | 37 | kotlinOptions { 38 | jvmTarget = '1.8' 39 | } 40 | 41 | sourceSets { 42 | main.java.srcDirs += 'src/main/kotlin' 43 | } 44 | 45 | defaultConfig { 46 | applicationId "com.somrit.hypebard" 47 | minSdkVersion 21 48 | minSdkVersion 21 49 | targetSdkVersion 33 50 | versionCode flutterVersionCode.toInteger() 51 | versionName flutterVersionName 52 | } 53 | 54 | // signingConfigs { 55 | // release { 56 | // storeFile file("./build_config/build.jks") 57 | // storePassword "123456" 58 | // keyAlias "key" 59 | // keyPassword "123456" 60 | // } 61 | // debug { 62 | // storeFile file("./build_config/build.jks") 63 | // storePassword "123456" 64 | // keyAlias "key" 65 | // keyPassword "123456" 66 | // } 67 | // } 68 | buildTypes { 69 | // release { 70 | // signingConfig signingConfigs.release 71 | // minifyEnabled true 72 | // } 73 | // debug { 74 | // signingConfig signingConfigs.debug 75 | // minifyEnabled false 76 | // } 77 | release { 78 | signingConfig signingConfigs.debug 79 | } 80 | } 81 | } 82 | 83 | flutter { 84 | source '../..' 85 | } 86 | 87 | dependencies { 88 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 89 | } 90 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "size": "60x60", 5 | "expected-size": "180", 6 | "filename": "180.png", 7 | "folder": "Assets.xcassets/AppIcon.appiconset/", 8 | "idiom": "iphone", 9 | "scale": "3x" 10 | }, 11 | { 12 | "size": "40x40", 13 | "expected-size": "80", 14 | "filename": "80.png", 15 | "folder": "Assets.xcassets/AppIcon.appiconset/", 16 | "idiom": "iphone", 17 | "scale": "2x" 18 | }, 19 | { 20 | "size": "40x40", 21 | "expected-size": "120", 22 | "filename": "120.png", 23 | "folder": "Assets.xcassets/AppIcon.appiconset/", 24 | "idiom": "iphone", 25 | "scale": "3x" 26 | }, 27 | { 28 | "size": "60x60", 29 | "expected-size": "120", 30 | "filename": "120.png", 31 | "folder": "Assets.xcassets/AppIcon.appiconset/", 32 | "idiom": "iphone", 33 | "scale": "2x" 34 | }, 35 | { 36 | "size": "57x57", 37 | "expected-size": "57", 38 | "filename": "57.png", 39 | "folder": "Assets.xcassets/AppIcon.appiconset/", 40 | "idiom": "iphone", 41 | "scale": "1x" 42 | }, 43 | { 44 | "size": "29x29", 45 | "expected-size": "58", 46 | "filename": "58.png", 47 | "folder": "Assets.xcassets/AppIcon.appiconset/", 48 | "idiom": "iphone", 49 | "scale": "2x" 50 | }, 51 | { 52 | "size": "29x29", 53 | "expected-size": "29", 54 | "filename": "29.png", 55 | "folder": "Assets.xcassets/AppIcon.appiconset/", 56 | "idiom": "iphone", 57 | "scale": "1x" 58 | }, 59 | { 60 | "size": "29x29", 61 | "expected-size": "87", 62 | "filename": "87.png", 63 | "folder": "Assets.xcassets/AppIcon.appiconset/", 64 | "idiom": "iphone", 65 | "scale": "3x" 66 | }, 67 | { 68 | "size": "57x57", 69 | "expected-size": "114", 70 | "filename": "114.png", 71 | "folder": "Assets.xcassets/AppIcon.appiconset/", 72 | "idiom": "iphone", 73 | "scale": "2x" 74 | }, 75 | { 76 | "size": "20x20", 77 | "expected-size": "40", 78 | "filename": "40.png", 79 | "folder": "Assets.xcassets/AppIcon.appiconset/", 80 | "idiom": "iphone", 81 | "scale": "2x" 82 | }, 83 | { 84 | "size": "20x20", 85 | "expected-size": "60", 86 | "filename": "60.png", 87 | "folder": "Assets.xcassets/AppIcon.appiconset/", 88 | "idiom": "iphone", 89 | "scale": "3x" 90 | }, 91 | { 92 | "size": "1024x1024", 93 | "filename": "1024.png", 94 | "expected-size": "1024", 95 | "idiom": "ios-marketing", 96 | "folder": "Assets.xcassets/AppIcon.appiconset/", 97 | "scale": "1x" 98 | } 99 | ] 100 | } -------------------------------------------------------------------------------- /lib/page/AppOpenPage.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:hypebard/page/HomePage.dart'; 5 | import 'package:hypebard/utils/Utils.dart'; 6 | import 'package:lottie/lottie.dart'; 7 | 8 | import '../utils/Config.dart'; 9 | 10 | class SplashPage extends StatefulWidget { 11 | const SplashPage({super.key}); 12 | 13 | @override 14 | _SplashPageState createState() => _SplashPageState(); 15 | } 16 | 17 | class _SplashPageState extends State with TickerProviderStateMixin { 18 | late LottieBuilder _splashLottie; 19 | late AnimationController _lottieController; 20 | 21 | bool _showAppOpenAnimate = true; 22 | bool _isAnimateFileLoaded = false; // lottie json loaded state 23 | 24 | Timer? _timer; 25 | 26 | @override 27 | void initState() { 28 | super.initState(); 29 | 30 | _lottieInit(); 31 | } 32 | 33 | void _lottieInit() { 34 | _lottieController = AnimationController( 35 | duration: const Duration(seconds: 3), 36 | vsync: this, 37 | ); 38 | _lottieController.addStatusListener((status) async { 39 | if (status == AnimationStatus.completed) { 40 | _lottieController.stop(); 41 | _showAppOpenAnimate = false; 42 | Utils.pushReplacement(context, const HomePage()); 43 | } 44 | }); 45 | _splashLottie = Lottie.asset( 46 | 'images/splash.json', 47 | repeat: false, 48 | animate: true, 49 | width: double.maxFinite, 50 | height: double.maxFinite, 51 | controller: _lottieController, 52 | onLoaded: (composition) { 53 | _isAnimateFileLoaded = true; 54 | _lottieController.forward(from: 0); 55 | _lottieController.duration = composition.duration; 56 | setState(() {}); 57 | }, 58 | ); 59 | } 60 | 61 | @override 62 | Widget build(BuildContext context) { 63 | return Scaffold(body: renderContent()); 64 | } 65 | 66 | Widget renderContent() { 67 | if (!_showAppOpenAnimate) { 68 | return Container(); 69 | } 70 | return Stack( 71 | children: [ 72 | Container( 73 | color: const Color(0xFFF6F1F1), 74 | child: Column( 75 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 76 | children: [ 77 | SizedBox( 78 | height: MediaQuery.of(context).size.height * 0.3, 79 | child: _splashLottie, 80 | ), 81 | if (_isAnimateFileLoaded) 82 | Text( 83 | Config.appName, 84 | softWrap: true, 85 | style: const TextStyle( 86 | color: Colors.black87, 87 | fontSize: 50, 88 | height: 28 / 28, 89 | fontWeight: FontWeight.bold, 90 | ), 91 | ), 92 | if (!_isAnimateFileLoaded) const SizedBox(height: 28), 93 | const SizedBox(height: 120), 94 | ], 95 | ), 96 | ), 97 | ], 98 | ); 99 | } 100 | 101 | @override 102 | void dispose() { 103 | _lottieController.dispose(); 104 | 105 | if (_timer != null) { 106 | _timer?.cancel(); 107 | _timer = null; 108 | } 109 | 110 | super.dispose(); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 71 | 77 | 78 | 79 | 80 | 82 | 83 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /lib/stores/AIChatStore.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:hypebard/utils/Chatgpt.dart'; 3 | 4 | class AIChatStore extends ChangeNotifier { 5 | AIChatStore() { 6 | syncStorage(); 7 | } 8 | 9 | String chatListKey = 'chatList'; 10 | 11 | List chatList = []; 12 | 13 | get sortChatList { 14 | List sortList = chatList; 15 | sortList.sort((a, b) { 16 | return b['updatedTime'].compareTo(a['updatedTime']); 17 | }); 18 | return sortList; 19 | } 20 | 21 | get homeHistoryList { 22 | return sortChatList.take(2).toList(); 23 | } 24 | 25 | Map _createChat(String aiType, String chatId) { 26 | int timestamp = DateTime.now().millisecondsSinceEpoch; 27 | Map aiData = ChatGPT.getAiInfoByType(aiType); 28 | Map chat = { 29 | "id": chatId, 30 | "ai": { 31 | "type": aiData['type'], 32 | "name": aiData['name'], 33 | "isContinuous": aiData['isContinuous'], 34 | "continuesStartIndex": 0, 35 | }, 36 | "systemMessage": { 37 | "role": "system", 38 | "content": aiData['content'], 39 | }, 40 | "messages": [], 41 | "createdTime": timestamp, 42 | "updatedTime": timestamp, 43 | }; 44 | 45 | return chat; 46 | } 47 | 48 | Future deleteChatById(String chatId) async { 49 | Map? cacheChat = chatList.firstWhere( 50 | (v) => v['id'] == chatId, 51 | orElse: () => null, 52 | ); 53 | if (cacheChat != null) { 54 | chatList.removeWhere((v) => v['id'] == chatId); 55 | await ChatGPT.storage.write(chatListKey, chatList); 56 | notifyListeners(); 57 | } 58 | } 59 | 60 | void syncStorage() { 61 | chatList = ChatGPT.storage.read(chatListKey) ?? []; 62 | debugPrint('---syncStorage success---'); 63 | notifyListeners(); 64 | } 65 | 66 | void fixChatList() { 67 | for (int i = 0; i < chatList.length; i++) { 68 | Map chat = chatList[i]; 69 | for (int k = 0; k < chat['messages'].length; k++) { 70 | Map v = chat['messages'][k]; 71 | if (v['role'] == 'generating') { 72 | chatList[i]['messages'][k] = { 73 | 'role': 'error', 74 | 'content': 'Request timeout', 75 | }; 76 | } 77 | } 78 | } 79 | notifyListeners(); 80 | } 81 | 82 | /// Initialize the page to get data 83 | Map getChatById(String chatType, String chatId) { 84 | Map? chat = chatList.firstWhere( 85 | (v) => v['id'] == chatId, 86 | orElse: () => null, 87 | ); 88 | 89 | if (chat == null) { 90 | return _createChat(chatType, chatId); 91 | } 92 | 93 | return chat; 94 | } 95 | 96 | Future pushMessage(Map chat, Map message) async { 97 | Map? cacheHistory = chatList.firstWhere( 98 | (v) => v['id'] == chat['id'], 99 | orElse: () => null, 100 | ); 101 | int timestamp = DateTime.now().millisecondsSinceEpoch; 102 | if (cacheHistory != null) { 103 | chatList.removeWhere((v) => v['id'] == cacheHistory!['id']); 104 | cacheHistory['messages'].add(message); 105 | cacheHistory['updatedTime'] = timestamp; 106 | chatList.add(cacheHistory); 107 | await ChatGPT.storage.write(chatListKey, chatList); 108 | notifyListeners(); 109 | return cacheHistory; 110 | } 111 | cacheHistory = chat; 112 | cacheHistory['messages'].add(message); 113 | cacheHistory['updatedTime'] = timestamp; 114 | chatList.add(cacheHistory); 115 | await ChatGPT.storage.write(chatListKey, chatList); 116 | notifyListeners(); 117 | print('---cacheHistory---$cacheHistory'); 118 | return cacheHistory; 119 | } 120 | 121 | Future replaceMessage(String chatId, int messageIndex, Map message) async { 122 | Map? chat = chatList.firstWhere( 123 | (v) => v['id'] == chatId, 124 | orElse: () => null, 125 | ); 126 | if (chat != null) { 127 | for (var i = 0; i < chatList.length; ++i) { 128 | Map v = chatList[i]; 129 | if (v['id'] == chatId) { 130 | int timestamp = DateTime.now().millisecondsSinceEpoch; 131 | chatList[i]['messages'][messageIndex] = message; 132 | chatList[i]['updatedTime'] = timestamp; 133 | break; 134 | } 135 | } 136 | await ChatGPT.storage.write(chatListKey, chatList); 137 | notifyListeners(); 138 | } 139 | } 140 | 141 | Future pushStreamMessage(String chatId, int messageIndex, Map message) async { 142 | if (chatId != '' && 143 | message['content'] != '' && 144 | message['content'] != null) { 145 | final index = chatList.indexWhere((v) => v['id'] == chatId); 146 | Map current = chatList[index]['messages'][messageIndex]; 147 | 148 | if (current['role'] != message['role']) { 149 | chatList[index]['messages'][messageIndex] = message; 150 | } else { 151 | chatList[index]['messages'][messageIndex] = { 152 | "role": message['role'], 153 | "content": '${current['content']}${message['content']}', 154 | }; 155 | } 156 | 157 | int timestamp = DateTime.now().millisecondsSinceEpoch; 158 | chatList[index]['updatedTime'] = timestamp; 159 | 160 | ChatGPT.storage.write(chatListKey, chatList); 161 | notifyListeners(); 162 | } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /lib/page/PrivacyPolicyPage.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:url_launcher/url_launcher.dart'; 3 | import 'package:vibration/vibration.dart'; 4 | 5 | class PrivacyPolicyPage extends StatelessWidget { 6 | const PrivacyPolicyPage({Key? key}) : super(key: key); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return Scaffold( 11 | appBar: AppBar( 12 | backgroundColor: const Color(0xFFF6F1F1), 13 | elevation: 0, 14 | title: const Text( 15 | 'Privacy Policy', 16 | style: TextStyle( 17 | fontSize: 26, 18 | fontWeight: FontWeight.normal, 19 | color: Colors.black87, 20 | ), 21 | ), 22 | leading: IconButton( 23 | icon: const Icon( 24 | Icons.arrow_back_ios_rounded, 25 | color: Colors.black, 26 | size: 30, 27 | ), 28 | onPressed: () { 29 | Navigator.pop(context); 30 | }, 31 | ), 32 | ), 33 | body: SingleChildScrollView( 34 | padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 20), 35 | child: Column( 36 | crossAxisAlignment: CrossAxisAlignment.start, 37 | children: [ 38 | const Text( 39 | '⦾ Introduction', 40 | style: TextStyle( 41 | fontSize: 32, 42 | fontWeight: FontWeight.w900, 43 | ), 44 | ), 45 | const SizedBox(height: 10), 46 | const Text( 47 | 'This Privacy Policy explains how we collect, use, and disclose your information when you use our app "hypeBard". By using our app, you consent to the collection, use, and disclosure of your information as described in this Privacy Policy.', 48 | style: TextStyle(fontSize: 16), 49 | ), 50 | const SizedBox(height: 20), 51 | const Text( 52 | '➊ Information We Collect', 53 | style: TextStyle( 54 | fontSize: 20, 55 | fontWeight: FontWeight.bold, 56 | ), 57 | ), 58 | const SizedBox(height: 10), 59 | const Text( 60 | 'We do not collect or store any personal information or data in the cloud. All processing is done locally on your device to ensure speed and data privacy.', 61 | style: TextStyle(fontSize: 16), 62 | ), 63 | const SizedBox(height: 20), 64 | const Text( 65 | '➋ Permissions', 66 | style: TextStyle( 67 | fontSize: 20, 68 | fontWeight: FontWeight.bold, 69 | ), 70 | ), 71 | const SizedBox(height: 10), 72 | const Text( 73 | 'Our app requires the following permissions:', 74 | style: TextStyle(fontSize: 16), 75 | ), 76 | const SizedBox(height: 10), 77 | const Text( 78 | '⦿ Internet: This permission is required for the app to connect to the internet and utilize the GPT-3.5 Turbo engine from OpenAI.', 79 | style: TextStyle(fontSize: 16), 80 | ), 81 | const SizedBox(height: 10), 82 | const Text( 83 | '⦿ Bluetooth: This permission is used for accessing the connected bluetooth audio services, so that you can access speech-to-text function using that device.', 84 | style: TextStyle(fontSize: 16), 85 | ), 86 | const SizedBox(height: 10), 87 | const Text( 88 | '⦿ Nearby Devices: This permission is used for connecting the app to the bluetooth audio devices supporting the nearby device function for seamless integration.', 89 | style: TextStyle(fontSize: 16), 90 | ), 91 | const SizedBox(height: 10), 92 | const Text( 93 | '⦿ Microphone (Speech to Text): This permission is required to enable speech recognition and convert speech into text input.', 94 | style: TextStyle(fontSize: 16), 95 | ), 96 | const SizedBox(height: 20), 97 | const Text( 98 | "➌ Children's Privacy", 99 | style: TextStyle( 100 | fontSize: 20, 101 | fontWeight: FontWeight.bold, 102 | ), 103 | ), 104 | const SizedBox(height: 10), 105 | const Text( 106 | 'Our app does not knowingly collect personal information from children under the age of 16. If we learn that we have collected personal information of a child under 16, we will take steps to delete such information as soon as possible.', 107 | style: TextStyle(fontSize: 16), 108 | ), 109 | const SizedBox(height: 20), 110 | const Text( 111 | '➍ Changes to This Privacy Policy', 112 | style: TextStyle( 113 | fontSize: 20, 114 | fontWeight: FontWeight.bold, 115 | ), 116 | ), 117 | const SizedBox(height: 10), 118 | const Text( 119 | 'We may update our Privacy Policy from time to time. Any changes we make will be posted on this page, and the revised date will be indicated at the top of the page. We encourage you to review this Privacy Policy periodically for any updates or changes.', 120 | style: TextStyle(fontSize: 16), 121 | ), 122 | const SizedBox(height: 20), 123 | const Text( 124 | '➎ Contact Us', 125 | style: TextStyle( 126 | fontSize: 20, 127 | fontWeight: FontWeight.bold, 128 | ), 129 | ), 130 | const SizedBox(height: 10), 131 | const Text( 132 | 'If you have any questions or concerns about our Privacy Policy, please contact us:', 133 | style: TextStyle(fontSize: 16), 134 | ), 135 | const SizedBox(height: 10), 136 | const Text( 137 | '⦿ Email: somritdasgupta@outlook.com', 138 | style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), 139 | ), 140 | const SizedBox(height: 20), 141 | const Text( 142 | '➏ Conclusion', 143 | style: TextStyle( 144 | fontSize: 20, 145 | fontWeight: FontWeight.bold, 146 | ), 147 | ), 148 | const SizedBox(height: 10), 149 | const Text( 150 | 'By using our hypeBard, you acknowledge that you have read and understood this Privacy Policy and agree to its terms and conditions.', 151 | style: TextStyle(fontSize: 16), 152 | ), 153 | const SizedBox(height: 20), 154 | const Text( 155 | 'By using our hypeBard, you acknowledge that you have read and understood this Privacy Policy and agree to its terms and conditions.', 156 | style: TextStyle(fontSize: 16), 157 | ), 158 | const SizedBox(height: 20), 159 | ElevatedButton( 160 | onPressed: () { 161 | Vibration.vibrate(duration: 50); 162 | launchadd ('https://www.openai.com/privacy-policy/'); 163 | }, 164 | style: ElevatedButton.styleFrom( 165 | elevation: 10, 166 | foregroundColor: Colors.white, backgroundColor: Colors.black87, // Set the button's text color 167 | textStyle: const TextStyle(fontSize: 18), 168 | padding: const EdgeInsets.symmetric( 169 | horizontal: 20, vertical: 12), // Set the button's padding 170 | shape: RoundedRectangleBorder( 171 | borderRadius: BorderRadius.circular(15), // Set the button's border radius 172 | ), 173 | ), 174 | child: const Text('OpenAI ChatGPT Privacy Policy'), 175 | ), 176 | ], 177 | ), 178 | ), 179 | ); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /lib/page/ChatHistoryPage.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/services.dart'; 4 | import 'package:hypebard/page/ChatPage.dart'; 5 | import 'package:hypebard/stores/AIChatStore.dart'; 6 | import 'package:hypebard/utils/Time.dart'; 7 | import 'package:hypebard/utils/Utils.dart'; 8 | import 'package:provider/provider.dart'; 9 | import 'package:vibration/vibration.dart'; 10 | 11 | class ChatHistoryPage extends StatefulWidget { 12 | const ChatHistoryPage({Key? key}) : super(key: key); 13 | 14 | @override 15 | _ChatHistoryPageState createState() => _ChatHistoryPageState(); 16 | } 17 | 18 | class _ChatHistoryPageState extends State { 19 | @override 20 | void didChangeDependencies() { 21 | super.didChangeDependencies(); 22 | } 23 | 24 | @override 25 | void initState() { 26 | super.initState(); 27 | } 28 | 29 | @override 30 | void dispose() { 31 | super.dispose(); 32 | } 33 | 34 | @override 35 | Widget build(BuildContext context) { 36 | final store = Provider.of(context, listen: true); 37 | 38 | return Scaffold( 39 | appBar: AppBar( 40 | systemOverlayStyle: SystemUiOverlayStyle.dark, 41 | toolbarHeight: 60, 42 | automaticallyImplyLeading: false, 43 | titleSpacing: 0, 44 | title: Row( 45 | crossAxisAlignment: CrossAxisAlignment.center, 46 | children: [ 47 | InkWell( 48 | splashColor: Colors.transparent, 49 | highlightColor: Colors.transparent, 50 | onTap: () { 51 | Navigator.pop(context); 52 | }, 53 | child: const SizedBox( 54 | height: 60, 55 | child: Row( 56 | children: [ 57 | SizedBox(width: 24), 58 | Icon( 59 | Icons.arrow_back_ios_rounded, 60 | size: 30, 61 | weight: 100, 62 | color: Colors.black, 63 | ), 64 | SizedBox(width: 12), 65 | Text( 66 | "Your Rewinds", 67 | style: TextStyle( 68 | color: Color.fromRGBO(0, 0, 0, 1), 69 | fontSize: 26, 70 | height: 0, 71 | fontWeight: FontWeight.w500, 72 | ), 73 | ), 74 | SizedBox(width: 24), 75 | ], 76 | ), 77 | ), 78 | ), 79 | ], 80 | ), 81 | backgroundColor: const Color(0xFFF6F1F1), 82 | elevation: 0, 83 | actions: const [ 84 | SizedBox(width: 8), 85 | ], 86 | ), 87 | body: SafeArea( 88 | child: Column( 89 | children: [ 90 | Expanded( 91 | child: _renderChatListWidget( 92 | store.sortChatList, 93 | ), 94 | ), 95 | ], 96 | ), 97 | ), 98 | ); 99 | } 100 | 101 | Widget _genChatItemWidget(Map chat) { 102 | return Dismissible( 103 | key: Key(chat['id']), 104 | background: Container( 105 | decoration: BoxDecoration( 106 | borderRadius: BorderRadius.circular(20), 107 | gradient: LinearGradient( 108 | begin: Alignment.centerLeft, 109 | end: Alignment.centerRight, 110 | colors: [Colors.red.withOpacity(0.8), Colors.red.withOpacity(0.2)], 111 | ), 112 | ), 113 | margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), 114 | padding: const EdgeInsets.only(right: 16.0), 115 | alignment: Alignment.centerRight, 116 | child: const Icon( 117 | Icons.delete_outline_rounded, 118 | color: Colors.pinkAccent, 119 | size: 30, 120 | ), 121 | ), 122 | direction: DismissDirection.endToStart, 123 | onDismissed: (direction) { 124 | Vibration.vibrate(duration: 50); 125 | final store = Provider.of(context, listen: false); 126 | store.deleteChatById(chat['id']); 127 | }, 128 | child: Card( 129 | color: Colors.black38, 130 | elevation: 5, 131 | shape: RoundedRectangleBorder( 132 | borderRadius: BorderRadius.circular(20), 133 | ), 134 | margin: const EdgeInsets.symmetric(horizontal: 4, vertical: 5), 135 | child: InkWell( 136 | highlightColor: Colors.white, 137 | splashColor: Colors.white, 138 | onTap: () { 139 | final store = Provider.of(context, listen: false); 140 | store.fixChatList(); 141 | Utils.jumpPage( 142 | context, 143 | ChatPage( 144 | chatId: chat['id'], 145 | autofocus: false, 146 | chatType: chat['ai']['type'], 147 | ), 148 | ); 149 | }, 150 | child: Padding( 151 | padding: const EdgeInsets.all(16.0), 152 | child: Column( 153 | crossAxisAlignment: CrossAxisAlignment.start, 154 | children: [ 155 | Text( 156 | TimeUtils().formatTime( 157 | chat['updatedTime'], 158 | format: 'dd/MM/yyyy ➜ HH:mm', 159 | ), 160 | style: const TextStyle( 161 | color: Colors.tealAccent, 162 | fontSize: 16, 163 | fontWeight: FontWeight.w600, 164 | ), 165 | ), 166 | const SizedBox(height: 8), 167 | Text( 168 | chat['messages'][0]['content'], 169 | style: const TextStyle( 170 | color: Colors.tealAccent, 171 | fontSize: 16, 172 | fontWeight: FontWeight.normal, 173 | ), 174 | ), 175 | Row( 176 | mainAxisAlignment: MainAxisAlignment.end, 177 | children: [ 178 | IconButton( 179 | icon: const Icon( 180 | Icons.playlist_remove_rounded, 181 | size: 30, 182 | ), 183 | color: Colors.tealAccent, 184 | onPressed: () { 185 | Vibration.vibrate(duration: 50); 186 | _showDeleteConfirmationDialog(context, chat['id']); 187 | }, 188 | ), 189 | ], 190 | ), 191 | ], 192 | ), 193 | ), 194 | ), 195 | ), 196 | ); 197 | } 198 | 199 | Widget _renderChatListWidget(List chatList) { 200 | return ListView.builder( 201 | padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), 202 | itemCount: chatList.length, 203 | itemBuilder: (BuildContext context, int index) { 204 | final chat = chatList[index]; 205 | return _genChatItemWidget(chat); 206 | }, 207 | ); 208 | } 209 | 210 | Future _showDeleteConfirmationDialog( 211 | BuildContext context, String chatId) async { 212 | final store = Provider.of(context, listen: false); 213 | await showDialog( 214 | context: context, 215 | builder: (BuildContext context) { 216 | return Dialog( 217 | elevation: 90, 218 | shadowColor: Colors.black, 219 | shape: RoundedRectangleBorder( 220 | borderRadius: BorderRadius.circular(24.0), 221 | ), 222 | backgroundColor: Colors.white, 223 | child: Container( 224 | padding: const EdgeInsets.all(24.0), 225 | child: Column( 226 | mainAxisSize: MainAxisSize.min, 227 | children: [ 228 | const Text( 229 | 'Confirm deletion?', 230 | style: TextStyle( 231 | fontSize: 18, 232 | fontWeight: FontWeight.bold, 233 | ), 234 | ), 235 | const SizedBox(height: 24), 236 | Row( 237 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 238 | children: [ 239 | CupertinoButton( 240 | color: Colors.grey.withOpacity(0.2), 241 | borderRadius: BorderRadius.circular(12.0), 242 | padding: const EdgeInsets.symmetric( 243 | vertical: 12.0, 244 | horizontal: 24.0, 245 | ), 246 | child: const Text( 247 | 'Cancel', 248 | style: TextStyle( 249 | fontSize: 16, 250 | color: Colors.black, 251 | ), 252 | ), 253 | onPressed: () { 254 | Navigator.of(context).pop(false); 255 | }, 256 | ), 257 | CupertinoButton( 258 | color: Colors.red, 259 | borderRadius: BorderRadius.circular(12.0), 260 | padding: const EdgeInsets.symmetric( 261 | vertical: 12.0, 262 | horizontal: 24.0, 263 | ), 264 | child: const Text( 265 | 'Confirm', 266 | style: TextStyle( 267 | fontSize: 16, 268 | color: Colors.white, 269 | ), 270 | ), 271 | onPressed: () async { 272 | await store.deleteChatById(chatId); 273 | Navigator.of(context).pop(true); 274 | }, 275 | ), 276 | ], 277 | ), 278 | ], 279 | ), 280 | ), 281 | ); 282 | }, 283 | ); 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /lib/components/QuestionInput.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:hypebard/stores/AIChatStore.dart'; 3 | import 'package:hypebard/utils/Chatgpt.dart'; 4 | import 'package:provider/provider.dart'; 5 | import 'package:speech_to_text/speech_to_text.dart' as stt; 6 | import 'package:vibration/vibration.dart'; 7 | 8 | GlobalKey<_QuestionInputState> globalQuestionInputKey = GlobalKey(); 9 | 10 | class QuestionInput extends StatefulWidget { 11 | final Map chat; 12 | final bool autofocus; 13 | final bool enabled; 14 | final Function? scrollToBottom; 15 | final Function? onGeneratingStatusChange; 16 | 17 | const QuestionInput({ 18 | Key? key, 19 | required this.chat, 20 | required this.autofocus, 21 | required this.enabled, 22 | this.scrollToBottom, 23 | this.onGeneratingStatusChange, 24 | }) : super(key: key); 25 | 26 | @override 27 | _QuestionInputState createState() => _QuestionInputState(); 28 | } 29 | 30 | class _QuestionInputState extends State { 31 | final FocusNode focusNode = FocusNode(); 32 | TextEditingController questionController = TextEditingController(); 33 | bool _isGenerating = false; 34 | String myQuestion = ''; 35 | 36 | final stt.SpeechToText _speechToText = stt.SpeechToText(); // Add this line 37 | bool _isListening = false; 38 | String _transcription = ''; 39 | 40 | @override 41 | void initState() { 42 | super.initState(); 43 | _initializeSpeechToText(); 44 | } 45 | 46 | void _initializeSpeechToText() async { 47 | bool available = await _speechToText.initialize(); 48 | if (available) { 49 | _speechToText.errorListener = (error) { 50 | print('Speech recognition error: $error'); 51 | setState(() { 52 | _isListening = false; 53 | }); 54 | }; 55 | } else { 56 | print('The user has denied the use of speech recognition.'); 57 | } 58 | } 59 | 60 | void _startListening() async { 61 | if (!_isListening) { 62 | bool available = await _speechToText.initialize(); 63 | if (available) { 64 | setState(() { 65 | _isListening = true; 66 | _transcription = ''; 67 | }); 68 | _speechToText.listen( 69 | onResult: (result) { 70 | setState(() { 71 | _transcription = result.recognizedWords; 72 | questionController.text = _transcription; 73 | }); 74 | if (result.finalResult) { 75 | _stopListening(); 76 | onQuestionChange(_transcription); 77 | onSubmit(); 78 | } 79 | }, 80 | ); 81 | } 82 | } 83 | } 84 | 85 | void _stopListening() { 86 | if (_isListening) { 87 | _speechToText.stop(); 88 | setState(() { 89 | _isListening = false; 90 | }); 91 | } 92 | } 93 | 94 | @override 95 | void dispose() { 96 | questionController.dispose(); 97 | super.dispose(); 98 | } 99 | 100 | @override 101 | void didChangeDependencies() { 102 | precacheImage( 103 | const AssetImage('images/submit_active_icon.png'), 104 | context, 105 | ); 106 | super.didChangeDependencies(); 107 | } 108 | 109 | void _updateGeneratingStatus(bool value) { 110 | _isGenerating = value; 111 | 112 | if (widget.onGeneratingStatusChange != null) { 113 | widget.onGeneratingStatusChange!(value); 114 | } 115 | 116 | setState(() {}); 117 | } 118 | 119 | void reGenerate(int messageIndex) async { 120 | _updateGeneratingStatus(true); 121 | final store = Provider.of(context, listen: false); 122 | Map chat = widget.chat; 123 | List messages = []; 124 | Map ai = chat['ai']; 125 | 126 | /// If it is a continuous conversation, check whether it is related 127 | if (ai['isContinuous']) { 128 | messages = [ 129 | chat['systemMessage'], 130 | ...chat['messages'].take(messageIndex), 131 | ]; 132 | } else { 133 | messages = [ 134 | chat['systemMessage'], 135 | chat['messages'][messageIndex], 136 | ]; 137 | } 138 | 139 | /// This is the state in formation 140 | await store.replaceMessage(chat['id'], messageIndex, { 141 | 'role': 'generating', 142 | 'content': '', 143 | }); 144 | try { 145 | final response = await ChatGPT.sendMessage(messages); 146 | final firstCompletionChoice = response.choices.first; 147 | await store.replaceMessage(chat['id'], messageIndex, { 148 | 'role': 'assistant', 149 | 'content': firstCompletionChoice.message.content, 150 | }); 151 | 152 | _updateGeneratingStatus(false); 153 | } catch (error) { 154 | print(error); 155 | _updateGeneratingStatus(false); 156 | await store.replaceMessage(chat['id'], messageIndex, { 157 | 'role': 'error', 158 | 'content': error.toString(), 159 | }); 160 | } 161 | 162 | print(messages); 163 | } 164 | 165 | void onSubmit() async { 166 | final store = Provider.of(context, listen: false); 167 | if (myQuestion == '') { 168 | return; 169 | } 170 | if (_isGenerating) { 171 | print('---_isGenerating---'); 172 | return; 173 | } 174 | final text = myQuestion; 175 | 176 | _updateGeneratingStatus(true); 177 | 178 | setState(() { 179 | questionController.clear(); 180 | myQuestion = ''; 181 | }); 182 | 183 | Map message = { 184 | 'role': 'user', 185 | 'content': text, 186 | }; 187 | 188 | bool isFirstMessage = widget.chat['messages'].length == 0; 189 | debugPrint('--- $isFirstMessage---'); 190 | Map chat = await store.pushMessage(widget.chat, message); 191 | 192 | List messages = [ 193 | chat['systemMessage'], 194 | message, 195 | ]; 196 | Map ai = chat['ai']; 197 | 198 | /// If it is a continuous conversation, check whether it is related 199 | if (!isFirstMessage && ai['isContinuous']) { 200 | messages = [ 201 | chat['systemMessage'], 202 | ...chat['messages'], 203 | ]; 204 | } 205 | 206 | /// This is the state in formation 207 | Map generatingChat = await store.pushMessage(widget.chat, { 208 | 'role': 'generating', 209 | 'content': '', 210 | }); 211 | int messageIndex = generatingChat['messages'].length - 1; 212 | if (widget.scrollToBottom != null) { 213 | widget.scrollToBottom!(); 214 | } 215 | try { 216 | final response = await ChatGPT.sendMessage(messages); 217 | final firstCompletionChoice = response.choices.first; 218 | await store.replaceMessage(chat['id'], messageIndex, { 219 | 'role': 'assistant', 220 | 'content': firstCompletionChoice.message.content, 221 | }); 222 | _updateGeneratingStatus(false); 223 | } catch (error) { 224 | print(error); 225 | _updateGeneratingStatus(false); 226 | await store.replaceMessage(chat['id'], messageIndex, { 227 | 'role': 'error', 228 | 'content': error.toString(), 229 | }); 230 | if (widget.scrollToBottom != null) { 231 | widget.scrollToBottom!(); 232 | } 233 | } 234 | } 235 | 236 | void onQuestionChange(String value) { 237 | setState(() { 238 | myQuestion = value; 239 | if (_isListening) { 240 | questionController.text = _transcription; 241 | onSubmit(); // Update the text field with the transcription 242 | } 243 | }); 244 | } 245 | 246 | Image _getMicrophoneImage() { 247 | String imagePath = 248 | _isListening ? 'microphone_active_icon.png' : 'microphone_icon.png'; 249 | return Image.asset( 250 | 'images/$imagePath', 251 | width: 40, 252 | height: 30, 253 | ); 254 | } 255 | 256 | @override 257 | Widget build(BuildContext context) { 258 | return Container( 259 | width: MediaQuery.of(context).size.width, 260 | padding: const EdgeInsets.fromLTRB(18, 10, 18, 10), 261 | decoration: const BoxDecoration( 262 | border: Border( 263 | top: BorderSide( 264 | width: 0, 265 | color: Colors.transparent, 266 | ), 267 | ), 268 | ), 269 | child: Row( 270 | crossAxisAlignment: CrossAxisAlignment.center, 271 | children: [ 272 | Expanded( 273 | child: Container( 274 | padding: const EdgeInsets.fromLTRB(20, 4, 8, 4), 275 | decoration: BoxDecoration( 276 | color: Colors.white.withAlpha(500), 277 | borderRadius: BorderRadius.circular(50.0), 278 | border: Border.all( 279 | color: const Color.fromRGBO(70, 20, 75, 0.9254901960784314), 280 | width: 5), 281 | boxShadow: [ 282 | BoxShadow( 283 | color: Colors.blueGrey.withOpacity(0.5), 284 | spreadRadius: 0, 285 | blurRadius: 20, 286 | offset: const Offset(0, 0), 287 | blurStyle: BlurStyle.outer, 288 | ), 289 | ], 290 | ), 291 | child: Row( 292 | children: [ 293 | Expanded( 294 | child: TextField( 295 | focusNode: focusNode, 296 | enabled: widget.enabled, 297 | controller: questionController, 298 | minLines: 1, 299 | maxLines: 2, 300 | cursorRadius: Radius.zero, 301 | decoration: const InputDecoration.collapsed( 302 | hintText: "Let's start..."), 303 | autofocus: widget.autofocus, 304 | style: const TextStyle( 305 | color: Colors.black87, 306 | fontSize: 20, 307 | height: 24 / 18, 308 | textBaseline: TextBaseline.alphabetic, 309 | ), 310 | onChanged: onQuestionChange, 311 | textInputAction: TextInputAction.search, 312 | textCapitalization: TextCapitalization.words, 313 | enableInteractiveSelection: true, 314 | onSubmitted: (String value) { 315 | onSubmit(); 316 | }, 317 | onTap: () { 318 | if (widget.scrollToBottom != null) { 319 | widget.scrollToBottom!(); 320 | } 321 | }, 322 | ), 323 | ), 324 | widget.enabled 325 | ? renderSubmitBtnWidget() 326 | : IgnorePointer( 327 | child: renderSubmitBtnWidget(), 328 | ), 329 | ], 330 | ), 331 | ), 332 | ), 333 | ], 334 | ), 335 | ); 336 | } 337 | 338 | Widget renderSubmitBtnWidget() { 339 | bool isActive = 340 | _transcription.isNotEmpty || !_isGenerating && !_isListening; 341 | return Row( 342 | children: [ 343 | GestureDetector( 344 | behavior: HitTestBehavior.translucent, 345 | onTap: () { 346 | if (_isListening) { 347 | _stopListening(); 348 | } else { 349 | _startListening(); 350 | } 351 | Vibration.vibrate( 352 | duration: 50); // Vibration effect of 50 milliseconds 353 | }, 354 | child: Container( 355 | padding: const EdgeInsets.all(8), 356 | child: _getMicrophoneImage(), 357 | ), 358 | ), 359 | GestureDetector( 360 | behavior: HitTestBehavior.translucent, 361 | onTap: () { 362 | onSubmit(); 363 | }, 364 | child: Container( 365 | padding: const EdgeInsets.all(8), 366 | child: Image.asset( 367 | 'images/${isActive ? 'submit_active_icon.png' : 'submit_icon.png'}', 368 | width: 40, 369 | height: 30, 370 | ), 371 | ), 372 | ), 373 | ], 374 | ); 375 | } 376 | } 377 | -------------------------------------------------------------------------------- /lib/utils/Chatgpt.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_openai/openai.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter_dotenv/flutter_dotenv.dart'; 4 | import 'package:get_storage/get_storage.dart'; 5 | 6 | class ChatGPT { 7 | static final ChatGPT _instance = ChatGPT._(); 8 | 9 | factory ChatGPT() => _getInstance(); 10 | 11 | static ChatGPT get instance => _getInstance(); 12 | 13 | ChatGPT._(); 14 | 15 | static ChatGPT _getInstance() { 16 | return _instance; 17 | } 18 | 19 | static GetStorage storage = GetStorage(); 20 | 21 | static String chatGptToken = 22 | dotenv.env['OPENAI_CHATGPT_TOKEN'] ?? ''; // token 23 | static String defaultModel = 'gpt-3.5-turbo'; 24 | static List defaultRoles = [ 25 | 'system', 26 | 'user', 27 | 'assistant' 28 | ]; // generating | error 29 | 30 | static List chatModelList = [ 31 | { 32 | "type": "chat", 33 | "name": "AI Chat", 34 | "desc": "Natural language chat, continuous conversation mode", 35 | "isContinuous": true, 36 | "content": "\nInstructions:" 37 | "\nYou are ChatGPT. The answer to each question should be as concise as possible. If you're making a list, don't have too many entries." 38 | " If possible, please format it in a friendly markdown format." 39 | '\n', 40 | "tips": [ 41 | "Write me a wholesome quote", 42 | "I need a sarcastic joke?", 43 | "Help me plan a trip", 44 | "I need a pickup line!" 45 | ], 46 | }, 47 | { 48 | "type": "translationLanguage", 49 | "name": "Bard, the Translator", 50 | "desc": "Translate A language to B language", 51 | "isContinuous": false, 52 | "content": '\nnInstructions:\n' 53 | 'I want you to act as a translator. You will recognize the language, translate it into the specified language and answer me. Please do not use an interpreter accent when translating, but to translate naturally, smoothly and authentically, using beautiful and elegant expressions. I will give you the format of "Translate A to B". If the format I gave is wrong, please tell me that the format of "Translate A to B" should be used. Please only answer the translation part, do not write the explanation.' 54 | " If possible, please format it in a friendly markdown format." 55 | '\n', 56 | "tips": [ 57 | "Translate love to spanish", 58 | "Translate beautiful to bengali", 59 | "Translate How are you to german", 60 | ], 61 | }, 62 | { 63 | "type": "frontEndHelper", 64 | "name": "Bard, as Engineer", 65 | "desc": "Bard, the front-end guide", 66 | "isContinuous": false, 67 | "content": '\nnInstructions:\n' 68 | "I want you to be an expert in front-end development. I'm going to provide some specific information about front-end code issues with Js, Node, etc., and your job is to come up with a strategy to solve the problem for me. This may include suggesting code, strategies for logical thinking about code." 69 | " If possible, please format it in a friendly markdown format." 70 | '\n', 71 | "tips": [ 72 | "JavaScript recursive binary tree", 73 | ], 74 | }, 75 | { 76 | "type": "linuxTerminal", 77 | "name": "Bard, as Linux Terminal", 78 | "desc": 79 | "AI linux terminal. Enter the command and the AI will reply with what the terminal should display", 80 | "isContinuous": false, 81 | "content": "\nInstructions:" 82 | "\nI want you to act as a linux terminal. I will type commands and you will reply with what the terminal should show. I want you to only reply with the terminal output inside one unique code block, and nothing else. do not write explanations. do not type commands unless I instruct you to do so. When I need to tell you something in English, I will do so by putting text inside curly brackets {like this}." 83 | " If possible, please format it in a friendly markdown format." 84 | '\n', 85 | "tips": [ 86 | "rm -rf", 87 | "ls -a", 88 | ], 89 | }, 90 | { 91 | "type": "positionInterviewer", 92 | "name": "Bard, the Interviewer", 93 | "desc": 94 | "AI interviewer. As a candidate, AI will ask you interview questions for the position", 95 | "isContinuous": false, 96 | "content": "\nInstructions:" 97 | "\nI want you to act as an interviewer. I will be the candidate and you will ask me the interview questions for the position position. I want you to only reply as the interviewer. Do not write all the conservation at once. I want you to only do the interview with me. Ask me the questions and wait for my answers. Do not write explanations. Ask me the questions one by one like an interviewer does and wait for my answers." 98 | " If possible, please format it in a friendly markdown format." 99 | '\n', 100 | "tips": [ 101 | "Hello, I'm a full stack javascript engineer", 102 | "Hello, I'm a marketing genius", 103 | "Hello, I'm a financial officer", 104 | ], 105 | }, 106 | { 107 | "type": "javaScriptConsole", 108 | "name": "Bard, as JavaScript Console", 109 | "desc": 110 | "As javascript console. Type the command and the AI will reply with what the javascript console should show", 111 | "isContinuous": false, 112 | "content": "\nInstructions:" 113 | "\nI want you to act as a javascript console. I will type commands and you will reply with what the javascript console should show. I want you to only reply with the terminal output inside one unique code block, and nothing else. do not write explanations. do not type commands unless I instruct you to do so. when I need to tell you something in english, I will do so by putting text inside curly brackets {like this}." 114 | " If possible, please format it in a friendly markdown format." 115 | '\n', 116 | "tips": [ 117 | 'console.log("Hello World");', 118 | 'window.alert("Hello");', 119 | ], 120 | }, 121 | { 122 | "type": "excelSheet", 123 | "name": "Bard, the Excel Hero", 124 | "desc": 125 | "Acts as a text-based excel. You'll only respond to my text-based 10-row Excel sheet with row numbers and cell letters as columns (A through L)", 126 | "isContinuous": false, 127 | "content": "\nInstructions:" 128 | "\nI want you to act as a text based excel. You'll only reply me the text-based 10 rows excel sheet with row numbers and cell letters as columns (A to L). First column header should be empty to reference row number. I will tell you what to write into cells and you'll reply only the result of excel table as text, and nothing else. Do not write explanations. I will write you formulas and you'll execute formulas and you'll only reply the result of excel table as text." 129 | " If possible, please format it in a friendly markdown format." 130 | '\n', 131 | "tips": [ 132 | "Issue an empty sheet", 133 | "I need a weekly expenses excel sheet", 134 | ], 135 | }, 136 | { 137 | "type": "spokenEnglishTeacher", 138 | "name": "Bard, as Grammar Hero", 139 | "desc": 140 | "Talk to AI in English, AI will reply you in English to practice your English speaking", 141 | "isContinuous": false, 142 | "content": "\nInstructions:" 143 | "\nI want you to act as a spoken English teacher and improver. I will speak to you in English and you will reply to me in English to practice my spoken English. I want you to keep your reply neat, limiting the reply to 100 words. I want you to strictly correct my grammar mistakes, typos, and factual errors. I want you to ask me a question in your reply. Remember, I want you to strictly correct my grammar mistakes, typos, and factual errors." 144 | " If possible, please format it in a friendly markdown format." 145 | '\n', 146 | "tips": [ 147 | "Now let's start practicing", 148 | ], 149 | }, 150 | { 151 | "type": "travelGuide", 152 | "name": "Bard, on your Vacation", 153 | "desc": 154 | "Write down your location and AI will recommend attractions near you", 155 | "isContinuous": false, 156 | "content": "\nInstructions:" 157 | "\nI want you to act as a travel guide. I will write you my location and you will suggest a place to visit near my location. In some cases, I will also give you the type of places I will visit. You will also suggest me places of similar type that are close to my first location." 158 | " If possible, please format it in a friendly markdown format." 159 | '\n', 160 | "tips": [ 161 | "I am in las vegas, and i want it to be awesome.", 162 | ], 163 | }, 164 | { 165 | "type": "storyteller", 166 | "name": "Bard, the Storyteller", 167 | "desc": 168 | "AI will come up with interesting stories that are engaging, imaginative and captivating to the audience", 169 | "isContinuous": false, 170 | "content": "\nInstructions:" 171 | "\nI want you to act as a storyteller. You will come up with entertaining stories that are engaging, imaginative and captivating for the audience. It can be fairy tales, educational stories or any other type of stories which has the potential to capture people's attention and imagination. Depending on the target audience, you may choose specific themes or topics for your storytelling session e.g., if it’s children then you can talk about animals; If it’s adults then history-based tales might engage them better etc. " 172 | " If possible, please format it in a friendly markdown format." 173 | '\n', 174 | "tips": [ 175 | "I need an story on how a rich man become poor due to his bad ethics", 176 | ], 177 | }, 178 | { 179 | "type": "novelist", 180 | "name": "Bard, the Novelist", 181 | "desc": 182 | "AI plays a novelist. You'll come up with creative and engaging stories", 183 | "isContinuous": false, 184 | "content": "\nInstructions:" 185 | "\nI want you to act as a novelist. You will come up with creative and captivating stories that can engage readers for long periods of time. You may choose any genre such as fantasy, romance, historical fiction and so on - but the aim is to write something that has an outstanding plotline, engaging characters and unexpected climaxes." 186 | " If possible, please format it in a friendly markdown format." 187 | '\n', 188 | "tips": [ 189 | 'I need to write a science-fiction novel set in the future with a time travelling mysterious ambience', 190 | ], 191 | }, 192 | { 193 | "type": "legalAdvisor", 194 | "name": "Bard, as Legal Advisor", 195 | "desc": 196 | "AI as your legal advisor. You need to describe a legal situation and the AI will provide advice on how to handle it", 197 | "isContinuous": false, 198 | "content": "\nInstructions:" 199 | "\nI want you to act as my legal advisor. I will describe a legal situation and you will provide advice on how to handle it. You should only reply with your advice, and nothing else. Do not write explanations." 200 | " If possible, please format it in a friendly markdown format." 201 | '\n', 202 | "tips": [ 203 | 'I’m making surrealistic portrait paintings', 204 | ], 205 | }, 206 | ]; 207 | 208 | static Future setOpenAIKey(String key) async { 209 | await storage.write('OpenAIKey', key); 210 | await initChatGPT(); 211 | } 212 | 213 | static String getCacheOpenAIKey() { 214 | String? key = storage.read('OpenAIKey'); 215 | if (key != null && key != '' && key != chatGptToken) { 216 | return key; 217 | } 218 | return ''; 219 | } 220 | 221 | static Future setOpenAIBaseUrl(String url) async { 222 | await storage.write('OpenAIBaseUrl', url); 223 | await initChatGPT(); 224 | } 225 | 226 | static String getCacheOpenAIBaseUrl() { 227 | String? key = storage.read('OpenAIBaseUrl'); 228 | return (key ?? "").isEmpty ? "" : key!; 229 | } 230 | 231 | static Set chatModelTypeList = 232 | chatModelList.map((map) => map['type']).toSet(); 233 | 234 | 235 | static getAiInfoByType(String chatType) { 236 | return chatModelList.firstWhere( 237 | (item) => item['type'] == chatType, 238 | orElse: () => null, 239 | ); 240 | } 241 | 242 | static Future initChatGPT() async { 243 | String cacheKey = getCacheOpenAIKey(); 244 | String cacheUrl = getCacheOpenAIBaseUrl(); 245 | var apiKey = cacheKey != '' ? cacheKey : chatGptToken; 246 | OpenAI.apiKey = apiKey; 247 | if (apiKey != chatGptToken) { 248 | OpenAI.baseUrl = 249 | cacheUrl.isNotEmpty ? cacheUrl : "https://api.openai.com"; 250 | } 251 | } 252 | 253 | static getRoleFromString(String role) { 254 | if (role == "system") return OpenAIChatMessageRole.system; 255 | if (role == "user") return OpenAIChatMessageRole.user; 256 | if (role == "assistant") return OpenAIChatMessageRole.assistant; 257 | return "unknown"; 258 | } 259 | 260 | static convertListToModel(List messages) { 261 | List modelMessages = []; 262 | for (var element in messages) { 263 | modelMessages.add(OpenAIChatCompletionChoiceMessageModel( 264 | role: getRoleFromString(element["role"]), 265 | content: element["content"], 266 | )); 267 | } 268 | return modelMessages; 269 | } 270 | 271 | static List filterMessageParams(List messages) { 272 | List newMessages = []; 273 | for (var v in messages) { 274 | if (defaultRoles.contains(v['role'])) { 275 | newMessages.add({ 276 | "role": v["role"], 277 | "content": v["content"], 278 | }); 279 | } 280 | } 281 | return newMessages; 282 | } 283 | 284 | static Future checkRelation( 285 | List beforeMessages, 286 | Map message, { 287 | String model = '', 288 | }) async { 289 | beforeMessages = filterMessageParams(beforeMessages); 290 | String text = "\nInstructions:" 291 | "\nCheck whether the problem is related to the given conversation. If yes, return true. If no, return false. Please return only true or false. The answer length is 5." 292 | "\nquestion:$message}" 293 | "\nconversation:$beforeMessages" 294 | "\n"; 295 | OpenAIChatCompletionModel chatCompletion = await sendMessage( 296 | [ 297 | { 298 | "role": 'user', 299 | "content": text, 300 | } 301 | ], 302 | model: model, 303 | ); 304 | debugPrint('---text $text---'); 305 | String content = chatCompletion.choices.first.message.content ?? ''; 306 | bool hasRelation = content.toLowerCase().contains('true'); 307 | debugPrint('--- $hasRelation---'); 308 | return hasRelation; 309 | } 310 | 311 | static Future sendMessage( 312 | List messages, { 313 | String model = '', 314 | }) async { 315 | messages = filterMessageParams(messages); 316 | List modelMessages = 317 | convertListToModel(messages); 318 | OpenAIChatCompletionModel chatCompletion = 319 | await OpenAI.instance.chat.create( 320 | model: model != '' ? model : defaultModel, 321 | messages: modelMessages, 322 | ); 323 | return chatCompletion; 324 | } 325 | 326 | static Future sendMessageOnStream( 327 | List messages, { 328 | String model = '', 329 | Function? onProgress, 330 | }) async { 331 | messages = filterMessageParams(messages); 332 | List modelMessages = 333 | convertListToModel(messages); 334 | 335 | Stream chatStream = 336 | OpenAI.instance.chat.createStream( 337 | model: defaultModel, 338 | messages: modelMessages, 339 | ); 340 | print(chatStream); 341 | 342 | chatStream.listen((chatStreamEvent) { 343 | print('---chatStreamEvent---'); 344 | print('$chatStreamEvent'); 345 | print('---chatStreamEvent end---'); 346 | if (onProgress != null) { 347 | onProgress(chatStreamEvent); 348 | } 349 | }); 350 | } 351 | 352 | static Future genImage(String imageDesc) async { 353 | debugPrint('---genImage starting: $imageDesc---'); 354 | OpenAIImageModel image = await OpenAI.instance.image.create( 355 | prompt: imageDesc, 356 | n: 1, 357 | size: OpenAIImageSize.size1024, 358 | responseFormat: OpenAIImageResponseFormat.url, 359 | ); 360 | debugPrint('---genImage success: $image---'); 361 | return image; 362 | } 363 | } 364 | -------------------------------------------------------------------------------- /lib/page/ChatPage.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/services.dart'; 4 | import 'package:flutter_easyloading/flutter_easyloading.dart'; 5 | import 'package:flutter_markdown/flutter_markdown.dart'; 6 | import 'package:flutter_tts/flutter_tts.dart'; 7 | import 'package:hypebard/components/QuestionInput.dart'; 8 | import 'package:hypebard/stores/AIChatStore.dart'; 9 | import 'package:hypebard/utils/Chatgpt.dart'; 10 | import 'package:lottie/lottie.dart'; 11 | import 'package:provider/provider.dart'; 12 | import 'package:share_plus/share_plus.dart'; 13 | import 'package:vibration/vibration.dart'; 14 | 15 | class ChatPage extends StatefulWidget { 16 | final String chatId; 17 | final bool autofocus; 18 | final String chatType; 19 | 20 | const ChatPage({ 21 | Key? key, 22 | required this.chatId, 23 | required this.autofocus, 24 | required this.chatType, 25 | }) : super(key: key); 26 | 27 | @override 28 | _ChatPageState createState() => _ChatPageState(); 29 | } 30 | 31 | enum TtsState { playing, stopped, paused, continued } 32 | 33 | class _ChatPageState extends State { 34 | static final LottieBuilder _generatingLottie = 35 | Lottie.asset("images/loading2.json"); 36 | 37 | final ScrollController _listController = ScrollController(); 38 | 39 | late FlutterTts _flutterTts; 40 | TtsState _ttsState = TtsState.stopped; 41 | String _speakText = ''; 42 | 43 | bool _isCopying = false; 44 | 45 | @override 46 | void didChangeDependencies() { 47 | super.didChangeDependencies(); 48 | } 49 | 50 | Future initTts() async { 51 | _flutterTts = FlutterTts(); 52 | 53 | _setAwaitOptions(); 54 | 55 | _flutterTts.setStartHandler(() { 56 | setState(() { 57 | if (kDebugMode) { 58 | print("Playing"); 59 | } 60 | _ttsState = TtsState.playing; 61 | }); 62 | }); 63 | 64 | _flutterTts.setCompletionHandler(() { 65 | setState(() { 66 | if (kDebugMode) { 67 | print("Complete"); 68 | } 69 | _ttsState = TtsState.stopped; 70 | }); 71 | }); 72 | 73 | _flutterTts.setCancelHandler(() { 74 | setState(() { 75 | if (kDebugMode) { 76 | print("Cancel"); 77 | } 78 | _ttsState = TtsState.stopped; 79 | }); 80 | }); 81 | 82 | _flutterTts.setPauseHandler(() { 83 | setState(() { 84 | if (kDebugMode) { 85 | print("Paused"); 86 | } 87 | _ttsState = TtsState.paused; 88 | }); 89 | }); 90 | 91 | _flutterTts.setContinueHandler(() { 92 | setState(() { 93 | if (kDebugMode) { 94 | print("Continued"); 95 | } 96 | _ttsState = TtsState.continued; 97 | }); 98 | }); 99 | 100 | _flutterTts.setErrorHandler((msg) { 101 | setState(() { 102 | if (kDebugMode) { 103 | print("error: $msg"); 104 | } 105 | _ttsState = TtsState.stopped; 106 | }); 107 | }); 108 | } 109 | 110 | Future _setAwaitOptions() async { 111 | await _flutterTts.awaitSpeakCompletion(true); 112 | } 113 | 114 | Future _speak(String text) async { 115 | if (_ttsState == TtsState.playing) { 116 | await _flutterTts.stop(); 117 | } 118 | if (_speakText == text) { 119 | _speakText = ''; 120 | return; 121 | } 122 | _speakText = text; 123 | await _flutterTts.speak(text); 124 | } 125 | 126 | @override 127 | void initState() { 128 | super.initState(); 129 | 130 | initTts(); 131 | } 132 | 133 | @override 134 | void dispose() { 135 | _flutterTts.stop(); 136 | super.dispose(); 137 | } 138 | 139 | void scrollToBottom() { 140 | _listController.animateTo( 141 | _listController.position.maxScrollExtent, 142 | duration: const Duration(milliseconds: 500), 143 | curve: Curves.easeInOut, 144 | ); 145 | if (_listController.hasClients) { 146 | _listController.jumpTo(_listController.position.maxScrollExtent); 147 | } 148 | } 149 | 150 | @override 151 | Widget build(BuildContext context) { 152 | final store = Provider.of(context, listen: true); 153 | final chat = store.getChatById(widget.chatType, widget.chatId); 154 | 155 | return Scaffold( 156 | extendBodyBehindAppBar: true, 157 | resizeToAvoidBottomInset: true, 158 | appBar: AppBar( 159 | systemOverlayStyle: SystemUiOverlayStyle.dark, 160 | toolbarHeight: 60, 161 | automaticallyImplyLeading: false, 162 | titleSpacing: 0, 163 | backgroundColor: Colors.transparent, 164 | elevation: 0, 165 | centerTitle: true, 166 | title: Row( 167 | mainAxisAlignment: MainAxisAlignment.center, 168 | children: [ 169 | InkWell( 170 | splashColor: Colors.transparent, 171 | highlightColor: Colors.transparent, 172 | onTap: () { 173 | Navigator.pop(context); 174 | }, 175 | child: Material( 176 | elevation: 4, 177 | borderRadius: BorderRadius.circular(20), 178 | child: ClipRRect( 179 | borderRadius: BorderRadius.circular(20), 180 | child: Container( 181 | width: 150, 182 | height: 35, 183 | decoration: const BoxDecoration( 184 | color: Colors.white, 185 | ), 186 | child: Center( 187 | child: Image.asset( 188 | 'images/hypeBard.png', 189 | width: 150, 190 | height: 35, 191 | fit: BoxFit.fill, 192 | ), 193 | ), 194 | ), 195 | ), 196 | ), 197 | ), 198 | ], 199 | ), 200 | ), 201 | body: SafeArea( 202 | child: Column( 203 | children: [ 204 | Expanded( 205 | child: _renderMessageListWidget( 206 | chat['messages'], 207 | ), 208 | ), 209 | QuestionInput( 210 | key: globalQuestionInputKey, 211 | chat: chat, 212 | autofocus: widget.autofocus, 213 | enabled: true, 214 | scrollToBottom: () { 215 | Future.delayed(const Duration(milliseconds: 300), () { 216 | scrollToBottom(); 217 | }); 218 | }, 219 | ), 220 | ], 221 | ), 222 | ), 223 | ); 224 | } 225 | 226 | Widget _renderMessageListWidget(List messages) { 227 | if (messages.isEmpty) { 228 | Map aiData = ChatGPT.getAiInfoByType(widget.chatType); 229 | 230 | List tipsWidget = []; 231 | for (int i = 0; i < aiData['tips'].length; i++) { 232 | String tip = aiData['tips'][i]; 233 | tipsWidget.add( 234 | Ink( 235 | decoration: BoxDecoration( 236 | color: const Color.fromRGBO(70, 20, 75, 0.9254901960784314), 237 | borderRadius: BorderRadius.circular(25.0), 238 | ), 239 | child: InkWell( 240 | splashColor: const Color(0xFFF6F1F1), 241 | highlightColor: const Color.fromRGBO(192, 238, 221, 1.0), 242 | borderRadius: BorderRadius.circular(25.0), 243 | onTap: () { 244 | if (globalQuestionInputKey.currentState != null) { 245 | final currentState = globalQuestionInputKey.currentState; 246 | if (currentState != null) { 247 | currentState.myQuestion = tip; 248 | currentState.questionController.clear(); 249 | currentState.questionController.text = tip; 250 | currentState.focusNode.requestFocus(); 251 | currentState.questionController.selection = 252 | TextSelection.fromPosition( 253 | TextPosition(offset: tip.length)); 254 | setState(() {}); 255 | } 256 | } 257 | }, 258 | child: Container( 259 | width: MediaQuery.of(context).size.width - 120, 260 | padding: const EdgeInsets.fromLTRB(20, 12, 20, 12), 261 | child: Text( 262 | tip, 263 | softWrap: true, 264 | textAlign: TextAlign.center, 265 | style: const TextStyle( 266 | color: Color.fromRGBO(255, 255, 255, 1.0), 267 | fontSize: 14, 268 | height: 20 / 14, 269 | ), 270 | ), 271 | ), 272 | ), 273 | ), 274 | ); 275 | tipsWidget.add( 276 | const SizedBox(height: 10), 277 | ); 278 | } 279 | return SingleChildScrollView( 280 | child: Column( 281 | children: [ 282 | const SizedBox(height: 60), 283 | const Image( 284 | width: 50, 285 | height: 60, 286 | image: AssetImage('images/bard.png'), 287 | ), 288 | const SizedBox(height: 12), 289 | const Text( 290 | 'Get started, Made with ❤ by Somrit', 291 | style: TextStyle( 292 | color: Colors.black, 293 | fontSize: 16, 294 | ), 295 | ), 296 | const SizedBox(height: 12), 297 | Column( 298 | children: tipsWidget, 299 | ), 300 | const SizedBox(height: 60), 301 | ], 302 | ), 303 | ); 304 | } 305 | 306 | return _genMessageListWidget(messages); 307 | } 308 | 309 | Widget _genMessageListWidget(List messages) { 310 | return ListView.builder( 311 | padding: const EdgeInsets.fromLTRB(0, 8, 0, 20), 312 | addAutomaticKeepAlives: false, 313 | addRepaintBoundaries: false, 314 | controller: _listController, 315 | reverse: false, 316 | itemCount: messages.length, 317 | itemBuilder: (BuildContext context, int index) { 318 | return _genMessageItemWidget(messages[index], index); 319 | }, 320 | ); 321 | } 322 | 323 | Widget _genMessageItemWidget(Map message, int index) { 324 | return Container( 325 | color: const Color(0xFFF6F1F1), 326 | padding: const EdgeInsets.fromLTRB(12, 6, 12, 6), 327 | child: _renderMessageItem(message, index), 328 | ); 329 | } 330 | 331 | Widget _renderMessageItem(Map message, int index) { 332 | String role = message['role']; 333 | String defaultAvatar = 'images/bard.png'; 334 | String defaultRoleName = 'Bard'; 335 | Color defaultColor = const Color(0x8B46144B); 336 | Color defaultTextColor = Colors.black; 337 | String defaultTextPrefix = ''; 338 | List defaultIcons = [ 339 | _renderVoiceWidget(message), 340 | const SizedBox(width: 8), 341 | _renderShareWidget(message), 342 | const SizedBox(width: 8), 343 | _renderCopyWidget(message), 344 | ]; 345 | Widget? customContent; 346 | 347 | if (role == 'user') { 348 | defaultAvatar = 'images/you.png'; 349 | defaultRoleName = 'You'; 350 | defaultColor = const Color(0x848A9169); 351 | defaultIcons = []; 352 | } else if (role == 'error') { 353 | defaultTextColor = const Color.fromRGBO(5, 0, 0, 0.796078431372549); 354 | defaultTextPrefix = 'Error: '; 355 | defaultIcons = [ 356 | _renderRegenerateWidget(index), 357 | ]; 358 | } else if (role == 'generating') { 359 | defaultIcons = []; 360 | customContent = Row( 361 | children: [ 362 | SizedBox( 363 | height: 60, 364 | child: _generatingLottie, 365 | ) 366 | ], 367 | ); 368 | } 369 | return Container( 370 | margin: const EdgeInsets.symmetric(vertical: 5, horizontal: 12), 371 | decoration: BoxDecoration( 372 | gradient: LinearGradient( 373 | begin: Alignment.topLeft, 374 | end: Alignment.bottomCenter, 375 | colors: [const Color(0xFFF0F2F5), defaultColor], 376 | ), 377 | borderRadius: BorderRadius.circular(20.0), 378 | boxShadow: [ 379 | BoxShadow( 380 | color: Colors.blueGrey.withOpacity(0.2), 381 | offset: const Offset(0, 2), 382 | blurRadius: 4, 383 | ), 384 | ], 385 | ), 386 | child: Column( 387 | crossAxisAlignment: CrossAxisAlignment.start, 388 | children: [ 389 | Padding( 390 | padding: const EdgeInsets.fromLTRB(12, 16, 12, 0), 391 | child: Row( 392 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 393 | children: [ 394 | Container( 395 | padding: const EdgeInsets.fromLTRB(0, 0, 0, 0), 396 | child: Row( 397 | children: [ 398 | ClipRRect( 399 | borderRadius: BorderRadius.circular(15.0), 400 | clipBehavior: Clip.antiAlias, 401 | child: Image( 402 | width: 36, 403 | height: 36, 404 | image: AssetImage(defaultAvatar), 405 | ), 406 | ), 407 | const SizedBox(width: 8), 408 | Text( 409 | defaultRoleName, 410 | softWrap: true, 411 | style: const TextStyle( 412 | color: Colors.black, 413 | fontSize: 18, 414 | height: 24 / 16, 415 | fontWeight: FontWeight.bold, 416 | ), 417 | ), 418 | ], 419 | ), 420 | ), 421 | Row( 422 | children: defaultIcons, 423 | ), 424 | ], 425 | ), 426 | ), 427 | const SizedBox(height: 10), 428 | const SizedBox(height: 10), 429 | Padding( 430 | padding: const EdgeInsets.fromLTRB(12, 0, 12, 16), 431 | child: customContent ?? 432 | MarkdownBody( 433 | data: '$defaultTextPrefix${message['content']}', 434 | shrinkWrap: true, 435 | selectable: true, 436 | styleSheet: MarkdownStyleSheet( 437 | textScaleFactor: 1.1, 438 | textAlign: WrapAlignment.start, 439 | p: TextStyle( 440 | height: 1.5, 441 | color: defaultTextColor, 442 | ), 443 | ), 444 | ), 445 | ), 446 | ], 447 | ), 448 | ); 449 | } 450 | 451 | Widget _renderShareWidget(Map message) { 452 | return GestureDetector( 453 | onTap: () async { 454 | Vibration.vibrate(duration: 50); 455 | Share.share(message['content']); 456 | }, 457 | child: Container( 458 | padding: const EdgeInsets.fromLTRB(2, 2, 2, 2), 459 | child: const Image( 460 | image: AssetImage('images/share_message_icon.png'), 461 | width: 22, 462 | ), 463 | ), 464 | ); 465 | } 466 | 467 | Widget _renderVoiceWidget(Map message) { 468 | return GestureDetector( 469 | onTap: () async { 470 | Vibration.vibrate(duration: 50); 471 | _speak(message['content']); 472 | }, 473 | child: Container( 474 | padding: const EdgeInsets.fromLTRB(2, 2, 2, 2), 475 | child: const Image( 476 | image: AssetImage('images/voice_icon.png'), 477 | width: 26, 478 | ), 479 | ), 480 | ); 481 | } 482 | 483 | Widget _renderCopyWidget(Map message) { 484 | return GestureDetector( 485 | onTap: () async { 486 | Vibration.vibrate(duration: 50); 487 | if (_isCopying) { 488 | return; 489 | } 490 | _isCopying = true; 491 | await Clipboard.setData( 492 | ClipboardData( 493 | text: message['content'], 494 | ), 495 | ); 496 | EasyLoading.showToast( 497 | 'Copied successfully', 498 | dismissOnTap: true, 499 | ); 500 | _isCopying = false; 501 | }, 502 | child: Container( 503 | padding: const EdgeInsets.fromLTRB(2, 2, 2, 2), 504 | child: const Image( 505 | image: AssetImage('images/chat_copy_icon.png'), 506 | width: 26, 507 | ), 508 | ), 509 | ); 510 | } 511 | 512 | Widget _renderRegenerateWidget(int index) { 513 | return GestureDetector( 514 | onTap: () { 515 | Vibration.vibrate(duration: 50); 516 | globalQuestionInputKey.currentState?.reGenerate(index); 517 | }, 518 | child: Container( 519 | padding: const EdgeInsets.fromLTRB(8, 2, 8, 2), 520 | child: const Image( 521 | image: AssetImage('images/refresh_icon.png'), 522 | width: 26, 523 | ), 524 | ), 525 | ); 526 | } 527 | } 528 | -------------------------------------------------------------------------------- /lib/page/SettingPage.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:flutter_easyloading/flutter_easyloading.dart'; 4 | import 'package:hypebard/page/PrivacyPolicyPage.dart'; 5 | import 'package:hypebard/stores/AIChatStore.dart'; 6 | import 'package:hypebard/utils/Chatgpt.dart'; 7 | import 'package:provider/provider.dart'; 8 | import 'package:sp_util/sp_util.dart'; 9 | import 'package:url_launcher/url_launcher.dart'; 10 | import 'package:vibration/vibration.dart'; 11 | 12 | import '../utils/Utils.dart'; 13 | 14 | class SettingPage extends StatefulWidget { 15 | const SettingPage({Key? key}) : super(key: key); 16 | 17 | @override 18 | _SettingPageState createState() => _SettingPageState(); 19 | } 20 | 21 | class _SettingPageState extends State with WidgetsBindingObserver { 22 | bool isCopying = false; 23 | final TextEditingController _keyTextEditingController = 24 | TextEditingController(); 25 | final TextEditingController _urlTextEditingController = 26 | TextEditingController(); 27 | 28 | @override 29 | void initState() { 30 | super.initState(); 31 | } 32 | 33 | @override 34 | void didChangeAppLifecycleState(AppLifecycleState state) { 35 | switch (state) { 36 | case AppLifecycleState.resumed: 37 | // TODO: Switch from background to foreground, the interface is visible. 38 | break; 39 | case AppLifecycleState.paused: 40 | 41 | /// TODO: Switch from foreground to background, the interface is not visible. 42 | break; 43 | case AppLifecycleState.inactive: 44 | 45 | /// TODO: Handle this case. 46 | break; 47 | case AppLifecycleState.detached: 48 | 49 | /// TODO: Handle this case. 50 | break; 51 | } 52 | } 53 | 54 | @override 55 | Widget build(BuildContext context) { 56 | return Stack( 57 | children: [ 58 | Scaffold( 59 | resizeToAvoidBottomInset: true, 60 | appBar: AppBar( 61 | systemOverlayStyle: SystemUiOverlayStyle.dark, 62 | toolbarHeight: 60, 63 | automaticallyImplyLeading: false, 64 | titleSpacing: 0, 65 | title: Row( 66 | crossAxisAlignment: CrossAxisAlignment.center, 67 | children: [ 68 | InkWell( 69 | splashColor: Colors.white, 70 | highlightColor: Colors.white, 71 | onTap: () { 72 | Navigator.pop(context); 73 | }, 74 | child: const SizedBox( 75 | height: 60, 76 | child: Row( 77 | children: [ 78 | SizedBox(width: 24), 79 | Icon( 80 | Icons.arrow_back_ios_rounded, 81 | size: 30, 82 | weight: 100, 83 | color: Colors.black, 84 | ), 85 | SizedBox(width: 12), 86 | Text( 87 | "Settings", 88 | style: TextStyle( 89 | color: Color.fromRGBO(0, 0, 0, 1), 90 | fontSize: 26, 91 | height: 0, 92 | fontWeight: FontWeight.w400, 93 | ), 94 | ), 95 | SizedBox(width: 24), 96 | ], 97 | ), 98 | ), 99 | ), 100 | ], 101 | ), 102 | backgroundColor: const Color(0xFFF6F1F1), 103 | elevation: 0, 104 | ), 105 | body: Container( 106 | color: const Color(0xFFF6F1F1), 107 | child: SafeArea( 108 | child: SingleChildScrollView( 109 | child: Column( 110 | children: [ 111 | renderItemWidget( 112 | Icons.vpn_key, 113 | Colors.lightGreen, 114 | 26, 115 | 'Customize OpenAI API', 116 | () async { 117 | String cacheKey = ChatGPT.getCacheOpenAIKey(); 118 | _keyTextEditingController.text = cacheKey; 119 | _showCustomOpenAIKeyDialog(); 120 | }, 121 | ), 122 | renderItemWidget( 123 | Icons.link, 124 | Colors.deepPurpleAccent, 125 | 26, 126 | 'Customize OpenAI URL', 127 | () async { 128 | String cacheUrl = ChatGPT.getCacheOpenAIBaseUrl(); 129 | _urlTextEditingController.text = cacheUrl; 130 | _showCustomOpenAIUrlDialog(); 131 | }, 132 | ), 133 | renderItemWidget( 134 | Icons.face, 135 | Colors.deepPurple, 136 | 26, 137 | 'Hey, somrit here!', 138 | () async { 139 | final Uri url = Uri.parse( 140 | 'https://www.linkedin.com/in/somritdasgupta'); 141 | launchURL(url.toString()); 142 | }, 143 | ), 144 | renderItemWidget( 145 | Icons.privacy_tip_rounded, 146 | Colors.red, 147 | 26, 148 | 'Privacy Policy', 149 | () async { 150 | Vibration.vibrate(duration: 50); 151 | Utils.jumpPage(context, const PrivacyPolicyPage()); 152 | }, 153 | ), 154 | renderItemWidget( 155 | Icons.delete, 156 | Colors.indigo, 157 | 22, 158 | 'Clear Data', 159 | () { 160 | ChatGPT.storage.erase(); 161 | final store = 162 | Provider.of(context, listen: false); 163 | store.syncStorage(); 164 | SpUtil.clear(); 165 | EasyLoading.showToast('Data cleared successfully'); 166 | }, 167 | ), 168 | ], 169 | ), 170 | ), 171 | ), 172 | ), 173 | ), 174 | const Positioned( 175 | left: 0, 176 | right: 0, 177 | bottom: 10, 178 | child: Text( 179 | 'Made with ❤ by Somrit', 180 | textAlign: TextAlign.center, 181 | style: TextStyle( 182 | color: Colors.blueGrey, 183 | fontSize: 14, 184 | ), 185 | ), 186 | ), 187 | ], 188 | ); 189 | } 190 | 191 | Widget renderItemWidget( 192 | IconData icon, 193 | Color iconColor, 194 | double iconSize, 195 | String title, 196 | VoidCallback onPressed, 197 | ) { 198 | return InkWell( 199 | onTap: onPressed, 200 | child: Container( 201 | margin: const EdgeInsets.symmetric(vertical: 20, horizontal: 20), 202 | padding: const EdgeInsets.all(15), 203 | decoration: BoxDecoration( 204 | color: Colors.pink[350], 205 | borderRadius: BorderRadius.circular(20), 206 | boxShadow: [ 207 | BoxShadow( 208 | color: Colors.pinkAccent.withOpacity(0.4), 209 | offset: const Offset(0, 2), 210 | blurRadius: 10, 211 | ), 212 | ], 213 | ), 214 | child: Row( 215 | crossAxisAlignment: CrossAxisAlignment.center, 216 | children: [ 217 | Container( 218 | padding: const EdgeInsets.all(12), 219 | decoration: const BoxDecoration( 220 | color: Colors.white60, 221 | shape: BoxShape.circle, 222 | ), 223 | child: Icon( 224 | icon, 225 | color: iconColor, 226 | size: iconSize, 227 | ), 228 | ), 229 | const SizedBox(width: 16), 230 | Expanded( 231 | child: Text( 232 | title, 233 | style: const TextStyle( 234 | fontSize: 18, 235 | fontWeight: FontWeight.bold, 236 | color: Colors.black87, 237 | ), 238 | ), 239 | ), 240 | ], 241 | ), 242 | ), 243 | ); 244 | } 245 | 246 | @override 247 | void dispose() { 248 | super.dispose(); 249 | } 250 | 251 | void _showCustomOpenAIKeyDialog() async { 252 | showDialog( 253 | context: context, 254 | builder: (BuildContext context) { 255 | return Dialog( 256 | shape: RoundedRectangleBorder( 257 | borderRadius: BorderRadius.circular(30.0), 258 | ), 259 | backgroundColor: Colors.white, 260 | elevation: 60, 261 | clipBehavior: Clip.antiAlias, 262 | insetAnimationCurve: Curves.fastEaseInToSlowEaseOut, 263 | child: Container( 264 | color: Colors.transparent, // Set background color to transparent 265 | padding: const EdgeInsets.all(20.0), 266 | child: ListView( 267 | shrinkWrap: true, 268 | children: [ 269 | const Text( 270 | 'Custom OpenAI API Key', 271 | style: TextStyle( 272 | fontSize: 18, 273 | fontWeight: FontWeight.bold, 274 | ), 275 | ), 276 | const SizedBox(height: 20), 277 | TextField( 278 | controller: _keyTextEditingController, 279 | autofocus: true, 280 | decoration: InputDecoration( 281 | hintText: 'Enter your API key', 282 | border: OutlineInputBorder( 283 | borderRadius: BorderRadius.circular(15.0), 284 | ), 285 | ), 286 | ), 287 | const SizedBox(height: 20), 288 | GestureDetector( 289 | onTap: () async { 290 | if (isCopying) { 291 | return; 292 | } 293 | isCopying = true; 294 | await Clipboard.setData( 295 | const ClipboardData( 296 | text: 'https://platform.openai.com/', 297 | ), 298 | ); 299 | EasyLoading.showToast( 300 | 'Copied successfully!', 301 | dismissOnTap: true, 302 | ); 303 | isCopying = false; 304 | }, 305 | child: const Text( 306 | '⦿ hypeBard does not collect this key.\n' 307 | '⦿ In case our API key reports an error, custom keys need to be created at https://platform.openai.com/.\n', 308 | style: TextStyle( 309 | fontSize: 14, 310 | color: Colors.grey, 311 | ), 312 | ), 313 | ), 314 | const SizedBox(height: 20), 315 | Row( 316 | mainAxisAlignment: MainAxisAlignment.end, 317 | children: [ 318 | TextButton( 319 | style: TextButton.styleFrom( 320 | padding: const EdgeInsets.symmetric( 321 | vertical: 10, 322 | horizontal: 20, 323 | ), 324 | shape: RoundedRectangleBorder( 325 | borderRadius: BorderRadius.circular(12.0), 326 | ), 327 | backgroundColor: Colors.grey.withOpacity(0.2), 328 | ), 329 | child: const Text('Cancel'), 330 | onPressed: () { 331 | _keyTextEditingController.clear(); 332 | Navigator.of(context).pop(false); 333 | }, 334 | ), 335 | const SizedBox(width: 10), 336 | TextButton( 337 | style: TextButton.styleFrom( 338 | padding: const EdgeInsets.symmetric( 339 | vertical: 10, 340 | horizontal: 20, 341 | ), 342 | shape: RoundedRectangleBorder( 343 | borderRadius: BorderRadius.circular(12.0), 344 | ), 345 | backgroundColor: Colors.black.withOpacity(0.8), 346 | ), 347 | child: const Text('Confirm', 348 | style: TextStyle(color: Colors.white)), 349 | onPressed: () async { 350 | ChatGPT.setOpenAIKey(_keyTextEditingController.text) 351 | .then((_) { 352 | _keyTextEditingController.clear(); 353 | Navigator.of(context).pop(true); 354 | EasyLoading.showToast( 355 | 'API key integrated', 356 | dismissOnTap: true, 357 | ); 358 | }); 359 | }, 360 | ), 361 | ], 362 | ), 363 | ], 364 | ), 365 | ), 366 | ); 367 | }, 368 | ); 369 | } 370 | 371 | void _showCustomOpenAIUrlDialog() async { 372 | showDialog( 373 | context: context, 374 | builder: (BuildContext context) { 375 | return Dialog( 376 | shape: RoundedRectangleBorder( 377 | borderRadius: BorderRadius.circular(30.0), 378 | ), 379 | backgroundColor: Colors.white, 380 | insetAnimationCurve: Curves.fastLinearToSlowEaseIn, 381 | child: Container( 382 | decoration: BoxDecoration( 383 | color: Colors.transparent, 384 | borderRadius: BorderRadius.circular(30.0), 385 | ), 386 | padding: const EdgeInsets.all(20.0), 387 | child: Column( 388 | mainAxisSize: MainAxisSize.min, 389 | crossAxisAlignment: CrossAxisAlignment.start, 390 | children: [ 391 | const Text( 392 | 'Custom OpenAI Base URL', 393 | style: TextStyle( 394 | fontSize: 18, 395 | fontWeight: FontWeight.bold, 396 | ), 397 | ), 398 | const SizedBox(height: 20), 399 | TextField( 400 | controller: _urlTextEditingController, 401 | autofocus: true, 402 | decoration: InputDecoration( 403 | hintText: 'Enter your OpenAI URL', 404 | border: OutlineInputBorder( 405 | borderRadius: BorderRadius.circular(15.0), 406 | ), 407 | ), 408 | ), 409 | const SizedBox(height: 20), 410 | GestureDetector( 411 | onTap: () async { 412 | if (isCopying) { 413 | return; 414 | } 415 | isCopying = true; 416 | await Clipboard.setData( 417 | const ClipboardData( 418 | text: 'https://platform.openai.com/', 419 | ), 420 | ); 421 | EasyLoading.showToast( 422 | 'Copied successfully!', 423 | dismissOnTap: true, 424 | ); 425 | isCopying = false; 426 | }, 427 | child: const Text( 428 | '⦿ Custom URL allows you to connect to your own OpenAI instance.\n' 429 | '⦿ You can set up a local or custom OpenAI deployment.\n', 430 | style: TextStyle( 431 | fontSize: 14, 432 | color: Colors.grey, 433 | ), 434 | ), 435 | ), 436 | const SizedBox(height: 20), 437 | Row( 438 | mainAxisAlignment: MainAxisAlignment.end, 439 | children: [ 440 | TextButton( 441 | style: TextButton.styleFrom( 442 | padding: const EdgeInsets.symmetric( 443 | vertical: 10, 444 | horizontal: 20, 445 | ), 446 | shape: RoundedRectangleBorder( 447 | borderRadius: BorderRadius.circular(12.0), 448 | ), 449 | backgroundColor: Colors.grey.withOpacity(0.2), 450 | ), 451 | child: const Text('Cancel'), 452 | onPressed: () { 453 | _urlTextEditingController.clear(); 454 | Navigator.of(context).pop(false); 455 | }, 456 | ), 457 | const SizedBox(width: 10), 458 | TextButton( 459 | style: TextButton.styleFrom( 460 | padding: const EdgeInsets.symmetric( 461 | vertical: 10, 462 | horizontal: 20, 463 | ), 464 | shape: RoundedRectangleBorder( 465 | borderRadius: BorderRadius.circular(12.0), 466 | ), 467 | backgroundColor: Colors.black.withOpacity(0.8), 468 | ), 469 | child: const Text('Confirm', 470 | style: TextStyle(color: Colors.white)), 471 | onPressed: () async { 472 | ChatGPT.setOpenAIBaseUrl(_urlTextEditingController.text) 473 | .then((_) { 474 | _urlTextEditingController.clear(); 475 | Navigator.of(context).pop(true); 476 | EasyLoading.showToast( 477 | 'URL updated', 478 | dismissOnTap: true, 479 | ); 480 | }); 481 | }, 482 | ), 483 | ], 484 | ), 485 | ], 486 | ), 487 | ), 488 | ); 489 | }, 490 | ); 491 | } 492 | 493 | void launchURL(String url) async { 494 | try { 495 | await launch( 496 | url, 497 | forceSafariVC: false, 498 | forceWebView: false, 499 | ); 500 | } catch (e) { 501 | throw 'Could not launch $url'; 502 | } 503 | } 504 | } 505 | -------------------------------------------------------------------------------- /lib/page/HomePage.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/services.dart'; 4 | import 'package:hypebard/components/QuestionInput.dart'; 5 | import 'package:hypebard/page/ChatHistoryPage.dart'; 6 | import 'package:hypebard/page/ChatPage.dart'; 7 | import 'package:hypebard/page/SettingPage.dart'; 8 | import 'package:hypebard/stores/AIChatStore.dart'; 9 | import 'package:hypebard/utils/Chatgpt.dart'; 10 | import 'package:hypebard/utils/Time.dart'; 11 | import 'package:hypebard/utils/Utils.dart'; 12 | import 'package:provider/provider.dart'; 13 | import 'package:uuid/uuid.dart'; 14 | import 'package:vibration/vibration.dart'; 15 | import 'package:animated_text_kit/animated_text_kit.dart'; 16 | 17 | class HomePage extends StatefulWidget { 18 | const HomePage({Key? key}) : super(key: key); 19 | 20 | @override 21 | _HomePageState createState() => _HomePageState(); 22 | } 23 | 24 | class _HomePageState extends State { 25 | TextEditingController questionController = TextEditingController(); 26 | 27 | @override 28 | void didChangeDependencies() { 29 | super.didChangeDependencies(); 30 | } 31 | 32 | Widget _renderBottomInputWidget() { 33 | return GestureDetector( 34 | behavior: HitTestBehavior.translucent, 35 | onTap: () { 36 | handleClickInput(); 37 | }, 38 | child: const QuestionInput( 39 | chat: {}, 40 | autofocus: false, 41 | enabled: false, 42 | ), 43 | ); 44 | } 45 | 46 | @override 47 | void initState() { 48 | super.initState(); 49 | } 50 | 51 | @override 52 | void dispose() { 53 | super.dispose(); 54 | } 55 | 56 | void _handleClickModel(Map chatModel) { 57 | final store = Provider.of(context, listen: false); 58 | store.fixChatList(); 59 | Utils.jumpPage( 60 | context, 61 | ChatPage( 62 | chatId: const Uuid().v4(), 63 | autofocus: true, 64 | chatType: chatModel['type'], 65 | ), 66 | ); 67 | } 68 | 69 | void handleClickInput() async { 70 | final store = Provider.of(context, listen: false); 71 | store.fixChatList(); 72 | Utils.jumpPage( 73 | context, 74 | ChatPage( 75 | chatType: 'chat', 76 | autofocus: true, 77 | chatId: const Uuid().v4(), 78 | ), 79 | ); 80 | } 81 | 82 | @override 83 | Widget build(BuildContext context) { 84 | final store = Provider.of(context, listen: true); 85 | return Scaffold( 86 | extendBodyBehindAppBar: true, 87 | extendBody: true, 88 | appBar: AppBar( 89 | systemOverlayStyle: SystemUiOverlayStyle.dark, 90 | forceMaterialTransparency: true, 91 | toolbarHeight: 70, 92 | automaticallyImplyLeading: true, 93 | titleSpacing: 0, 94 | title: Stack( 95 | alignment: Alignment.centerLeft, 96 | children: [ 97 | PhysicalModel( 98 | color: Colors.transparent, 99 | elevation: 0, 100 | borderRadius: const BorderRadius.only( 101 | topRight: Radius.circular(20.0), 102 | bottomRight: Radius.circular(20.0), 103 | ), 104 | shadowColor: Colors.black.withOpacity(0.2), 105 | child: const ClipRRect( 106 | borderRadius: BorderRadius.only( 107 | topRight: Radius.circular(25.0), 108 | bottomRight: Radius.circular(25.0), 109 | ), 110 | child: Image( 111 | width: 180, 112 | height: 45, 113 | colorBlendMode: BlendMode.clear, 114 | filterQuality: FilterQuality.high, 115 | isAntiAlias: true, 116 | alignment: Alignment.centerLeft, 117 | image: AssetImage('images/hypeBard.png'), 118 | ), 119 | ), 120 | ), 121 | ClipRRect( 122 | borderRadius: const BorderRadius.only( 123 | topLeft: Radius.circular(15.0), 124 | bottomLeft: Radius.circular(15.0), 125 | ), 126 | child: Container( 127 | width: 180, 128 | height: 45, 129 | color: Colors.transparent, 130 | ), 131 | ), 132 | ], 133 | ), 134 | backgroundColor: Colors.transparent, 135 | elevation: 0, 136 | actions: [ 137 | const SizedBox(width: 6), 138 | IconButton( 139 | icon: const Icon(Icons.security_rounded), 140 | splashColor: Colors.black54, 141 | iconSize: 40, 142 | color: const Color.fromRGBO(37, 34, 34, 1.0), 143 | onPressed: () { 144 | Vibration.vibrate(duration: 50); 145 | Utils.jumpPage(context, const SettingPage()); 146 | }, 147 | ), 148 | const SizedBox(width: 8), 149 | ], 150 | ), 151 | body: SafeArea( 152 | child: Column( 153 | children: [ 154 | Expanded( 155 | child: SingleChildScrollView( 156 | child: Column( 157 | crossAxisAlignment: CrossAxisAlignment.start, 158 | children: [ 159 | if (store.homeHistoryList.length > 0) 160 | _renderTitle( 161 | 'Your Rewinds', 162 | animateText: false, 163 | rightContent: Flexible( 164 | child: SizedBox( 165 | width: double.maxFinite, 166 | child: GestureDetector( 167 | onTap: () { 168 | Vibration.vibrate(duration: 50); 169 | Utils.jumpPage( 170 | context, const ChatHistoryPage()); 171 | }, 172 | child: Container( 173 | padding: const EdgeInsets.symmetric( 174 | vertical: 6, horizontal: 12), 175 | decoration: BoxDecoration( 176 | borderRadius: BorderRadius.circular(20), 177 | boxShadow: [ 178 | BoxShadow( 179 | color: Colors.grey.withOpacity( 180 | 0.1), // Customize the shadow color here 181 | blurRadius: 10, 182 | offset: const Offset(0, 0), 183 | ), 184 | ], 185 | gradient: const LinearGradient( 186 | begin: Alignment.topLeft, 187 | end: Alignment.centerRight, 188 | colors: [ 189 | Color( 190 | 0xFF46144B), // Customize the gradient start color here 191 | Color( 192 | 0xFFF6F1F1), // Customize the gradient end color here 193 | ], 194 | ), 195 | ), 196 | child: const Row( 197 | crossAxisAlignment: CrossAxisAlignment.start, 198 | mainAxisAlignment: MainAxisAlignment.start, 199 | children: [ 200 | Text( 201 | 'More', 202 | textAlign: TextAlign.start, 203 | style: TextStyle( 204 | fontSize: 18, 205 | height: 18 / 16, 206 | fontWeight: FontWeight.bold, 207 | color: Colors 208 | .white, // Customize the text color here 209 | ), 210 | ), 211 | ], 212 | ), 213 | ), 214 | ), 215 | ), 216 | ), 217 | ), 218 | if (store.homeHistoryList.length > 0) 219 | _renderChatListWidget( 220 | store.homeHistoryList, 221 | ), 222 | _renderTitle("Hey, I am"), 223 | _renderChatModelListWidget(), 224 | ], 225 | ), 226 | ), 227 | ), 228 | _renderBottomInputWidget(), 229 | ], 230 | ), 231 | ), 232 | ); 233 | } 234 | 235 | Widget _renderTitle( 236 | String text, { 237 | Widget? rightContent, 238 | TextStyle? textStyle, 239 | bool animateText = true, 240 | }) { 241 | return Container( 242 | width: MediaQuery.of(context).size.width, 243 | margin: const EdgeInsets.fromLTRB(0, 0, 0, 0), 244 | padding: const EdgeInsets.fromLTRB(2, 0, 2, 0), 245 | child: Row( 246 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 247 | children: [ 248 | const SizedBox(width: 20.0, height: 30), 249 | Expanded( 250 | child: Row( 251 | crossAxisAlignment: CrossAxisAlignment.center, 252 | children: [ 253 | Flexible( 254 | child: Text( 255 | text, 256 | style: textStyle ?? 257 | const TextStyle( 258 | fontSize: 24.0, 259 | fontWeight: FontWeight.bold, 260 | ), 261 | ), 262 | ), 263 | if (animateText) 264 | Flexible( 265 | flex: 1, 266 | child: FractionallySizedBox( 267 | alignment: Alignment.topRight, 268 | widthFactor: 1.25, 269 | child: SizedBox( 270 | height: 40.0, 271 | child: DefaultTextStyle( 272 | style: const TextStyle( 273 | color: Colors.black, 274 | fontSize: 24.0, 275 | fontFamily: 'Poppins', 276 | ), 277 | child: AnimatedTextKit( 278 | repeatForever: true, 279 | animatedTexts: [ 280 | RotateAnimatedText('Awesome.'), 281 | RotateAnimatedText('Incredible.'), 282 | RotateAnimatedText('hypeBard.'), 283 | ], 284 | ), 285 | ), 286 | ), 287 | ), 288 | ), 289 | ], 290 | ), 291 | ), 292 | rightContent ?? Container(), 293 | ], 294 | ), 295 | ); 296 | } 297 | 298 | Widget _renderChatModelListWidget() { 299 | List list = []; 300 | for (var i = 0; i < ChatGPT.chatModelList.length; i++) { 301 | list.add( 302 | _genChatModelItemWidget(ChatGPT.chatModelList[i]), 303 | ); 304 | } 305 | list.add( 306 | const SizedBox(height: 10), 307 | ); 308 | return Column( 309 | children: list, 310 | ); 311 | } 312 | 313 | Widget _genChatModelItemWidget(Map chatModel) { 314 | return InkWell( 315 | highlightColor: Colors.white, 316 | splashColor: Colors.white, 317 | onTap: () { 318 | _handleClickModel(chatModel); 319 | }, 320 | child: Column( 321 | crossAxisAlignment: CrossAxisAlignment.start, 322 | children: [ 323 | const SizedBox(height: 10), 324 | Container( 325 | width: MediaQuery.of(context).size.width, 326 | padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), 327 | child: Container( 328 | clipBehavior: Clip.antiAlias, 329 | decoration: BoxDecoration( 330 | color: const Color(0xA5B7D5AF), 331 | borderRadius: BorderRadius.circular(20), 332 | ), 333 | child: Row( 334 | crossAxisAlignment: CrossAxisAlignment.center, 335 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 336 | children: [ 337 | Expanded( 338 | child: Container( 339 | padding: const EdgeInsets.fromLTRB(20, 20, 0, 20), 340 | child: Column( 341 | crossAxisAlignment: CrossAxisAlignment.start, 342 | children: [ 343 | Text( 344 | chatModel['name'], 345 | softWrap: true, 346 | style: const TextStyle( 347 | color: Color.fromRGBO(1, 2, 6, 1), 348 | fontSize: 16, 349 | height: 24 / 16, 350 | fontWeight: FontWeight.bold, 351 | ), 352 | ), 353 | const SizedBox(height: 8), 354 | Text( 355 | chatModel['desc'], 356 | softWrap: true, 357 | style: const TextStyle( 358 | color: Color.fromRGBO(70, 54, 77, 1.0), 359 | fontSize: 16, 360 | height: 22 / 16, 361 | ), 362 | ), 363 | ], 364 | ), 365 | ), 366 | ), 367 | Expanded( 368 | flex: 0, 369 | child: Container( 370 | padding: const EdgeInsets.fromLTRB(20, 12, 20, 12), 371 | child: const Image( 372 | image: AssetImage('images/arrow_icon.png'), 373 | width: 22, 374 | ), 375 | ), 376 | ), 377 | ], 378 | ), 379 | ), 380 | ), 381 | const SizedBox(height: 10), 382 | ], 383 | ), 384 | ); 385 | } 386 | 387 | Widget _renderChatListWidget(List chatList) { 388 | List list = []; 389 | for (var i = 0; i < chatList.length; i++) { 390 | list.add( 391 | _genChatItemWidget(chatList[i]), 392 | ); 393 | } 394 | return Container( 395 | padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), 396 | child: Column( 397 | children: [ 398 | ...list, 399 | ], 400 | ), 401 | ); 402 | } 403 | 404 | Widget _genChatItemWidget(Map chat) { 405 | return InkWell( 406 | highlightColor: Colors.white, 407 | splashColor: Colors.white, 408 | onTap: () { 409 | final store = Provider.of(context, listen: false); 410 | store.fixChatList(); 411 | Utils.jumpPage( 412 | context, 413 | ChatPage( 414 | chatId: chat['id'], 415 | autofocus: false, 416 | chatType: chat['ai']['type'], 417 | ), 418 | ); 419 | }, 420 | child: Column( 421 | crossAxisAlignment: CrossAxisAlignment.start, 422 | children: [ 423 | const SizedBox(height: 10), 424 | Container( 425 | width: MediaQuery.of(context).size.width, 426 | padding: const EdgeInsets.fromLTRB(2, 0, 2, 0), 427 | child: Container( 428 | clipBehavior: Clip.antiAlias, 429 | decoration: BoxDecoration( 430 | color: Colors.black54, 431 | borderRadius: BorderRadius.circular(20), 432 | ), 433 | child: Row( 434 | crossAxisAlignment: CrossAxisAlignment.start, 435 | mainAxisAlignment: MainAxisAlignment.start, 436 | children: [ 437 | Expanded( 438 | child: Container( 439 | padding: const EdgeInsets.fromLTRB(20, 20, 0, 20), 440 | child: Column( 441 | crossAxisAlignment: CrossAxisAlignment.start, 442 | children: [ 443 | if (chat['updatedTime'] != null) 444 | Text( 445 | TimeUtils().formatTime( 446 | chat['updatedTime'], 447 | format: 'dd/MM/yyyy ➜ HH:mm', 448 | ), 449 | softWrap: false, 450 | style: const TextStyle( 451 | color: Colors.tealAccent, 452 | fontSize: 16, 453 | fontWeight: FontWeight.w600, 454 | height: 24 / 16, 455 | ), 456 | ), 457 | const SizedBox(height: 8), 458 | Text( 459 | chat['messages'][0]['content'], 460 | softWrap: true, 461 | style: const TextStyle( 462 | color: Colors.tealAccent, 463 | fontSize: 16, 464 | height: 24 / 16, 465 | fontWeight: FontWeight.w500, 466 | ), 467 | ), 468 | ], 469 | ), 470 | ), 471 | ), 472 | IconButton( 473 | icon: const Icon( 474 | Icons.playlist_remove_rounded, 475 | size: 30, 476 | ), 477 | color: Colors.tealAccent, 478 | onPressed: () { 479 | Vibration.vibrate(duration: 50); 480 | _showDeleteConfirmationDialog(context, chat['id']); 481 | }, 482 | ), 483 | ], 484 | ), 485 | ), 486 | ), 487 | const SizedBox(height: 10), 488 | ], 489 | ), 490 | ); 491 | } 492 | 493 | Future _showDeleteConfirmationDialog( 494 | BuildContext context, String chatId) async { 495 | final store = Provider.of(context, listen: false); 496 | await showDialog( 497 | context: context, 498 | builder: (BuildContext context) { 499 | return Dialog( 500 | elevation: 90, 501 | shadowColor: Colors.black, 502 | surfaceTintColor: Colors.lime[200], 503 | shape: RoundedRectangleBorder( 504 | borderRadius: BorderRadius.circular(24.0), 505 | ), 506 | backgroundColor: 507 | Colors.white.withOpacity(0.8), // Adjust the opacity here 508 | child: Container( 509 | padding: const EdgeInsets.all(24.0), 510 | child: Column( 511 | mainAxisSize: MainAxisSize.min, 512 | children: [ 513 | const Text( 514 | 'Confirm deletion?', 515 | style: TextStyle( 516 | fontSize: 18, 517 | fontWeight: FontWeight.bold, 518 | ), 519 | ), 520 | const SizedBox(height: 24), 521 | Row( 522 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 523 | children: [ 524 | CupertinoButton( 525 | color: Colors.grey.withOpacity(0.2), 526 | borderRadius: BorderRadius.circular(12.0), 527 | padding: const EdgeInsets.symmetric( 528 | vertical: 12.0, 529 | horizontal: 24.0, 530 | ), 531 | child: const Text( 532 | 'Cancel', 533 | style: TextStyle( 534 | fontSize: 16, 535 | color: Colors.black, 536 | ), 537 | ), 538 | onPressed: () { 539 | Navigator.of(context).pop(false); 540 | }, 541 | ), 542 | CupertinoButton( 543 | color: Colors.red, 544 | borderRadius: BorderRadius.circular(12.0), 545 | padding: const EdgeInsets.symmetric( 546 | vertical: 12.0, 547 | horizontal: 24.0, 548 | ), 549 | child: const Text( 550 | 'Confirm', 551 | style: TextStyle( 552 | fontSize: 16, 553 | color: Colors.white, 554 | ), 555 | ), 556 | onPressed: () async { 557 | await store.deleteChatById(chatId); 558 | Navigator.of(context).pop(true); 559 | }, 560 | ), 561 | ], 562 | ), 563 | ], 564 | ), 565 | ), 566 | ); 567 | }, 568 | ); 569 | } 570 | } 571 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 54; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 13 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 14 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 15 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 16 | B26519A961D1423B1468D270 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 36FEB9A197D9A051FA7AF3FB /* Pods_Runner.framework */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXCopyFilesBuildPhase section */ 20 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 21 | isa = PBXCopyFilesBuildPhase; 22 | buildActionMask = 2147483647; 23 | dstPath = ""; 24 | dstSubfolderSpec = 10; 25 | files = ( 26 | ); 27 | name = "Embed Frameworks"; 28 | runOnlyForDeploymentPostprocessing = 0; 29 | }; 30 | /* End PBXCopyFilesBuildPhase section */ 31 | 32 | /* Begin PBXFileReference section */ 33 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 34 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 35 | 36FEB9A197D9A051FA7AF3FB /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 36 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 37 | 5617C309AE9A6FB8881D7FB8 /* 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 = ""; }; 38 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 39 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 40 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 41 | 84EFC16491DED064955FCFA4 /* 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 = ""; }; 42 | 8C26DED7CCDA36A96C69F89F /* 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 = ""; }; 43 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 44 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 45 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 47 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 48 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 49 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | /* End PBXFileReference section */ 51 | 52 | /* Begin PBXFrameworksBuildPhase section */ 53 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 54 | isa = PBXFrameworksBuildPhase; 55 | buildActionMask = 2147483647; 56 | files = ( 57 | B26519A961D1423B1468D270 /* Pods_Runner.framework in Frameworks */, 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | /* End PBXFrameworksBuildPhase section */ 62 | 63 | /* Begin PBXGroup section */ 64 | 292F98C264B539EEF4EDFE6A /* Pods */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | 8C26DED7CCDA36A96C69F89F /* Pods-Runner.debug.xcconfig */, 68 | 84EFC16491DED064955FCFA4 /* Pods-Runner.release.xcconfig */, 69 | 5617C309AE9A6FB8881D7FB8 /* Pods-Runner.profile.xcconfig */, 70 | ); 71 | path = Pods; 72 | sourceTree = ""; 73 | }; 74 | 9740EEB11CF90186004384FC /* Flutter */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 78 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 79 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 80 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 81 | ); 82 | name = Flutter; 83 | sourceTree = ""; 84 | }; 85 | 97C146E51CF9000F007C117D = { 86 | isa = PBXGroup; 87 | children = ( 88 | 9740EEB11CF90186004384FC /* Flutter */, 89 | 97C146F01CF9000F007C117D /* Runner */, 90 | 97C146EF1CF9000F007C117D /* Products */, 91 | 292F98C264B539EEF4EDFE6A /* Pods */, 92 | AA2CFA85C99765FEF5D1491E /* Frameworks */, 93 | ); 94 | sourceTree = ""; 95 | }; 96 | 97C146EF1CF9000F007C117D /* Products */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | 97C146EE1CF9000F007C117D /* Runner.app */, 100 | ); 101 | name = Products; 102 | sourceTree = ""; 103 | }; 104 | 97C146F01CF9000F007C117D /* Runner */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 108 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 109 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 110 | 97C147021CF9000F007C117D /* Info.plist */, 111 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 112 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 113 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 114 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 115 | ); 116 | path = Runner; 117 | sourceTree = ""; 118 | }; 119 | AA2CFA85C99765FEF5D1491E /* Frameworks */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | 36FEB9A197D9A051FA7AF3FB /* Pods_Runner.framework */, 123 | ); 124 | name = Frameworks; 125 | sourceTree = ""; 126 | }; 127 | /* End PBXGroup section */ 128 | 129 | /* Begin PBXNativeTarget section */ 130 | 97C146ED1CF9000F007C117D /* Runner */ = { 131 | isa = PBXNativeTarget; 132 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 133 | buildPhases = ( 134 | FFA578104241B04B463E9698 /* [CP] Check Pods Manifest.lock */, 135 | 9740EEB61CF901F6004384FC /* Run Script */, 136 | 97C146EA1CF9000F007C117D /* Sources */, 137 | 97C146EB1CF9000F007C117D /* Frameworks */, 138 | 97C146EC1CF9000F007C117D /* Resources */, 139 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 140 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 141 | 5B5A326EC2F7DA9E452532CA /* [CP] Embed Pods Frameworks */, 142 | ); 143 | buildRules = ( 144 | ); 145 | dependencies = ( 146 | ); 147 | name = Runner; 148 | productName = Runner; 149 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 150 | productType = "com.apple.product-type.application"; 151 | }; 152 | /* End PBXNativeTarget section */ 153 | 154 | /* Begin PBXProject section */ 155 | 97C146E61CF9000F007C117D /* Project object */ = { 156 | isa = PBXProject; 157 | attributes = { 158 | LastUpgradeCheck = 1300; 159 | ORGANIZATIONNAME = ""; 160 | TargetAttributes = { 161 | 97C146ED1CF9000F007C117D = { 162 | CreatedOnToolsVersion = 7.3.1; 163 | LastSwiftMigration = 1100; 164 | }; 165 | }; 166 | }; 167 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 168 | compatibilityVersion = "Xcode 9.3"; 169 | developmentRegion = en; 170 | hasScannedForEncodings = 0; 171 | knownRegions = ( 172 | en, 173 | Base, 174 | ); 175 | mainGroup = 97C146E51CF9000F007C117D; 176 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 177 | projectDirPath = ""; 178 | projectRoot = ""; 179 | targets = ( 180 | 97C146ED1CF9000F007C117D /* Runner */, 181 | ); 182 | }; 183 | /* End PBXProject section */ 184 | 185 | /* Begin PBXResourcesBuildPhase section */ 186 | 97C146EC1CF9000F007C117D /* Resources */ = { 187 | isa = PBXResourcesBuildPhase; 188 | buildActionMask = 2147483647; 189 | files = ( 190 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 191 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 192 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 193 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 194 | ); 195 | runOnlyForDeploymentPostprocessing = 0; 196 | }; 197 | /* End PBXResourcesBuildPhase section */ 198 | 199 | /* Begin PBXShellScriptBuildPhase section */ 200 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 201 | isa = PBXShellScriptBuildPhase; 202 | alwaysOutOfDate = 1; 203 | buildActionMask = 2147483647; 204 | files = ( 205 | ); 206 | inputPaths = ( 207 | ); 208 | name = "Thin Binary"; 209 | outputPaths = ( 210 | ); 211 | runOnlyForDeploymentPostprocessing = 0; 212 | shellPath = /bin/sh; 213 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 214 | }; 215 | 5B5A326EC2F7DA9E452532CA /* [CP] Embed Pods Frameworks */ = { 216 | isa = PBXShellScriptBuildPhase; 217 | buildActionMask = 2147483647; 218 | files = ( 219 | ); 220 | inputFileListPaths = ( 221 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", 222 | ); 223 | name = "[CP] Embed Pods Frameworks"; 224 | outputFileListPaths = ( 225 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", 226 | ); 227 | runOnlyForDeploymentPostprocessing = 0; 228 | shellPath = /bin/sh; 229 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 230 | showEnvVarsInLog = 0; 231 | }; 232 | 9740EEB61CF901F6004384FC /* Run Script */ = { 233 | isa = PBXShellScriptBuildPhase; 234 | alwaysOutOfDate = 1; 235 | buildActionMask = 2147483647; 236 | files = ( 237 | ); 238 | inputPaths = ( 239 | ); 240 | name = "Run Script"; 241 | outputPaths = ( 242 | ); 243 | runOnlyForDeploymentPostprocessing = 0; 244 | shellPath = /bin/sh; 245 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 246 | }; 247 | FFA578104241B04B463E9698 /* [CP] Check Pods Manifest.lock */ = { 248 | isa = PBXShellScriptBuildPhase; 249 | buildActionMask = 2147483647; 250 | files = ( 251 | ); 252 | inputFileListPaths = ( 253 | ); 254 | inputPaths = ( 255 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 256 | "${PODS_ROOT}/Manifest.lock", 257 | ); 258 | name = "[CP] Check Pods Manifest.lock"; 259 | outputFileListPaths = ( 260 | ); 261 | outputPaths = ( 262 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 263 | ); 264 | runOnlyForDeploymentPostprocessing = 0; 265 | shellPath = /bin/sh; 266 | 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"; 267 | showEnvVarsInLog = 0; 268 | }; 269 | /* End PBXShellScriptBuildPhase section */ 270 | 271 | /* Begin PBXSourcesBuildPhase section */ 272 | 97C146EA1CF9000F007C117D /* Sources */ = { 273 | isa = PBXSourcesBuildPhase; 274 | buildActionMask = 2147483647; 275 | files = ( 276 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 277 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 278 | ); 279 | runOnlyForDeploymentPostprocessing = 0; 280 | }; 281 | /* End PBXSourcesBuildPhase section */ 282 | 283 | /* Begin PBXVariantGroup section */ 284 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 285 | isa = PBXVariantGroup; 286 | children = ( 287 | 97C146FB1CF9000F007C117D /* Base */, 288 | ); 289 | name = Main.storyboard; 290 | sourceTree = ""; 291 | }; 292 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 293 | isa = PBXVariantGroup; 294 | children = ( 295 | 97C147001CF9000F007C117D /* Base */, 296 | ); 297 | name = LaunchScreen.storyboard; 298 | sourceTree = ""; 299 | }; 300 | /* End PBXVariantGroup section */ 301 | 302 | /* Begin XCBuildConfiguration section */ 303 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 304 | isa = XCBuildConfiguration; 305 | buildSettings = { 306 | ALWAYS_SEARCH_USER_PATHS = NO; 307 | CLANG_ANALYZER_NONNULL = YES; 308 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 309 | CLANG_CXX_LIBRARY = "libc++"; 310 | CLANG_ENABLE_MODULES = YES; 311 | CLANG_ENABLE_OBJC_ARC = YES; 312 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 313 | CLANG_WARN_BOOL_CONVERSION = YES; 314 | CLANG_WARN_COMMA = YES; 315 | CLANG_WARN_CONSTANT_CONVERSION = YES; 316 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 317 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 318 | CLANG_WARN_EMPTY_BODY = YES; 319 | CLANG_WARN_ENUM_CONVERSION = YES; 320 | CLANG_WARN_INFINITE_RECURSION = YES; 321 | CLANG_WARN_INT_CONVERSION = YES; 322 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 323 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 324 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 325 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 326 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 327 | CLANG_WARN_STRICT_PROTOTYPES = YES; 328 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 329 | CLANG_WARN_UNREACHABLE_CODE = YES; 330 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 331 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 332 | COPY_PHASE_STRIP = NO; 333 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 334 | ENABLE_NS_ASSERTIONS = NO; 335 | ENABLE_STRICT_OBJC_MSGSEND = YES; 336 | GCC_C_LANGUAGE_STANDARD = gnu99; 337 | GCC_NO_COMMON_BLOCKS = YES; 338 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 339 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 340 | GCC_WARN_UNDECLARED_SELECTOR = YES; 341 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 342 | GCC_WARN_UNUSED_FUNCTION = YES; 343 | GCC_WARN_UNUSED_VARIABLE = YES; 344 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 345 | MTL_ENABLE_DEBUG_INFO = NO; 346 | SDKROOT = iphoneos; 347 | SUPPORTED_PLATFORMS = iphoneos; 348 | TARGETED_DEVICE_FAMILY = "1,2"; 349 | VALIDATE_PRODUCT = YES; 350 | }; 351 | name = Profile; 352 | }; 353 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 354 | isa = XCBuildConfiguration; 355 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 356 | buildSettings = { 357 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 358 | CLANG_ENABLE_MODULES = YES; 359 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 360 | ENABLE_BITCODE = NO; 361 | INFOPLIST_FILE = Runner/Info.plist; 362 | LD_RUNPATH_SEARCH_PATHS = ( 363 | "$(inherited)", 364 | "@executable_path/Frameworks", 365 | ); 366 | PRODUCT_BUNDLE_IDENTIFIER = com.wewehao.aichat; 367 | PRODUCT_NAME = "$(TARGET_NAME)"; 368 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 369 | SWIFT_VERSION = 5.0; 370 | VERSIONING_SYSTEM = "apple-generic"; 371 | }; 372 | name = Profile; 373 | }; 374 | 97C147031CF9000F007C117D /* Debug */ = { 375 | isa = XCBuildConfiguration; 376 | buildSettings = { 377 | ALWAYS_SEARCH_USER_PATHS = NO; 378 | CLANG_ANALYZER_NONNULL = YES; 379 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 380 | CLANG_CXX_LIBRARY = "libc++"; 381 | CLANG_ENABLE_MODULES = YES; 382 | CLANG_ENABLE_OBJC_ARC = YES; 383 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 384 | CLANG_WARN_BOOL_CONVERSION = YES; 385 | CLANG_WARN_COMMA = YES; 386 | CLANG_WARN_CONSTANT_CONVERSION = YES; 387 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 388 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 389 | CLANG_WARN_EMPTY_BODY = YES; 390 | CLANG_WARN_ENUM_CONVERSION = YES; 391 | CLANG_WARN_INFINITE_RECURSION = YES; 392 | CLANG_WARN_INT_CONVERSION = YES; 393 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 394 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 395 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 396 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 397 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 398 | CLANG_WARN_STRICT_PROTOTYPES = YES; 399 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 400 | CLANG_WARN_UNREACHABLE_CODE = YES; 401 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 402 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 403 | COPY_PHASE_STRIP = NO; 404 | DEBUG_INFORMATION_FORMAT = dwarf; 405 | ENABLE_STRICT_OBJC_MSGSEND = YES; 406 | ENABLE_TESTABILITY = YES; 407 | GCC_C_LANGUAGE_STANDARD = gnu99; 408 | GCC_DYNAMIC_NO_PIC = NO; 409 | GCC_NO_COMMON_BLOCKS = YES; 410 | GCC_OPTIMIZATION_LEVEL = 0; 411 | GCC_PREPROCESSOR_DEFINITIONS = ( 412 | "DEBUG=1", 413 | "$(inherited)", 414 | ); 415 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 416 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 417 | GCC_WARN_UNDECLARED_SELECTOR = YES; 418 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 419 | GCC_WARN_UNUSED_FUNCTION = YES; 420 | GCC_WARN_UNUSED_VARIABLE = YES; 421 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 422 | MTL_ENABLE_DEBUG_INFO = YES; 423 | ONLY_ACTIVE_ARCH = YES; 424 | SDKROOT = iphoneos; 425 | TARGETED_DEVICE_FAMILY = "1,2"; 426 | }; 427 | name = Debug; 428 | }; 429 | 97C147041CF9000F007C117D /* Release */ = { 430 | isa = XCBuildConfiguration; 431 | buildSettings = { 432 | ALWAYS_SEARCH_USER_PATHS = NO; 433 | CLANG_ANALYZER_NONNULL = YES; 434 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 435 | CLANG_CXX_LIBRARY = "libc++"; 436 | CLANG_ENABLE_MODULES = YES; 437 | CLANG_ENABLE_OBJC_ARC = YES; 438 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 439 | CLANG_WARN_BOOL_CONVERSION = YES; 440 | CLANG_WARN_COMMA = YES; 441 | CLANG_WARN_CONSTANT_CONVERSION = YES; 442 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 443 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 444 | CLANG_WARN_EMPTY_BODY = YES; 445 | CLANG_WARN_ENUM_CONVERSION = YES; 446 | CLANG_WARN_INFINITE_RECURSION = YES; 447 | CLANG_WARN_INT_CONVERSION = YES; 448 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 449 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 450 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 451 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 452 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 453 | CLANG_WARN_STRICT_PROTOTYPES = YES; 454 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 455 | CLANG_WARN_UNREACHABLE_CODE = YES; 456 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 457 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 458 | COPY_PHASE_STRIP = NO; 459 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 460 | ENABLE_NS_ASSERTIONS = NO; 461 | ENABLE_STRICT_OBJC_MSGSEND = YES; 462 | GCC_C_LANGUAGE_STANDARD = gnu99; 463 | GCC_NO_COMMON_BLOCKS = YES; 464 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 465 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 466 | GCC_WARN_UNDECLARED_SELECTOR = YES; 467 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 468 | GCC_WARN_UNUSED_FUNCTION = YES; 469 | GCC_WARN_UNUSED_VARIABLE = YES; 470 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 471 | MTL_ENABLE_DEBUG_INFO = NO; 472 | SDKROOT = iphoneos; 473 | SUPPORTED_PLATFORMS = iphoneos; 474 | SWIFT_COMPILATION_MODE = wholemodule; 475 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 476 | TARGETED_DEVICE_FAMILY = "1,2"; 477 | VALIDATE_PRODUCT = YES; 478 | }; 479 | name = Release; 480 | }; 481 | 97C147061CF9000F007C117D /* Debug */ = { 482 | isa = XCBuildConfiguration; 483 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 484 | buildSettings = { 485 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 486 | CLANG_ENABLE_MODULES = YES; 487 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 488 | ENABLE_BITCODE = NO; 489 | INFOPLIST_FILE = Runner/Info.plist; 490 | LD_RUNPATH_SEARCH_PATHS = ( 491 | "$(inherited)", 492 | "@executable_path/Frameworks", 493 | ); 494 | PRODUCT_BUNDLE_IDENTIFIER = com.wewehao.aichat; 495 | PRODUCT_NAME = "$(TARGET_NAME)"; 496 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 497 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 498 | SWIFT_VERSION = 5.0; 499 | VERSIONING_SYSTEM = "apple-generic"; 500 | }; 501 | name = Debug; 502 | }; 503 | 97C147071CF9000F007C117D /* Release */ = { 504 | isa = XCBuildConfiguration; 505 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 506 | buildSettings = { 507 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 508 | CLANG_ENABLE_MODULES = YES; 509 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 510 | ENABLE_BITCODE = NO; 511 | INFOPLIST_FILE = Runner/Info.plist; 512 | LD_RUNPATH_SEARCH_PATHS = ( 513 | "$(inherited)", 514 | "@executable_path/Frameworks", 515 | ); 516 | PRODUCT_BUNDLE_IDENTIFIER = com.wewehao.aichat; 517 | PRODUCT_NAME = "$(TARGET_NAME)"; 518 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 519 | SWIFT_VERSION = 5.0; 520 | VERSIONING_SYSTEM = "apple-generic"; 521 | }; 522 | name = Release; 523 | }; 524 | /* End XCBuildConfiguration section */ 525 | 526 | /* Begin XCConfigurationList section */ 527 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 528 | isa = XCConfigurationList; 529 | buildConfigurations = ( 530 | 97C147031CF9000F007C117D /* Debug */, 531 | 97C147041CF9000F007C117D /* Release */, 532 | 249021D3217E4FDB00AE95B9 /* Profile */, 533 | ); 534 | defaultConfigurationIsVisible = 0; 535 | defaultConfigurationName = Release; 536 | }; 537 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 538 | isa = XCConfigurationList; 539 | buildConfigurations = ( 540 | 97C147061CF9000F007C117D /* Debug */, 541 | 97C147071CF9000F007C117D /* Release */, 542 | 249021D4217E4FDB00AE95B9 /* Profile */, 543 | ); 544 | defaultConfigurationIsVisible = 0; 545 | defaultConfigurationName = Release; 546 | }; 547 | /* End XCConfigurationList section */ 548 | }; 549 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 550 | } 551 | --------------------------------------------------------------------------------