├── .gitignore
├── .metadata
├── LICENSE
├── README-CN.md
├── README.md
├── analysis_options.yaml
├── android
├── .gitignore
├── app
│ ├── build.gradle
│ └── src
│ │ ├── debug
│ │ └── AndroidManifest.xml
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── kotlin
│ │ │ └── com
│ │ │ │ └── taxze
│ │ │ │ └── chat
│ │ │ │ └── craft
│ │ │ │ └── flutter_chat_craft
│ │ │ │ └── MainActivity.kt
│ │ └── res
│ │ │ ├── drawable-v21
│ │ │ └── launch_background.xml
│ │ │ ├── drawable
│ │ │ └── launch_background.xml
│ │ │ ├── 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
│ │ │ ├── values-night
│ │ │ └── styles.xml
│ │ │ └── values
│ │ │ └── styles.xml
│ │ └── profile
│ │ └── AndroidManifest.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
└── settings.gradle
├── assets
├── anim
│ ├── voice_black.json
│ ├── voice_blue.json
│ ├── voice_record.json
│ └── voice_ripple.json
├── fonts
│ └── bahnschrift.ttf
├── images
│ ├── app_icon.svg
│ ├── back.svg
│ ├── ic_add.svg
│ ├── ic_arrow.svg
│ ├── ic_audio.svg
│ ├── ic_back_left.svg
│ ├── ic_browser.png
│ ├── ic_camera.svg
│ ├── ic_chat_tools.svg
│ ├── ic_check.svg
│ ├── ic_collect.svg
│ ├── ic_down_arrow.svg
│ ├── ic_email.svg
│ ├── ic_friend_setting.svg
│ ├── ic_group_chat.svg
│ ├── ic_home.svg
│ ├── ic_keyboard.svg
│ ├── ic_like.svg
│ ├── ic_logout.svg
│ ├── ic_mass_send.svg
│ ├── ic_me.svg
│ ├── ic_message_copy.svg
│ ├── ic_message_forword.svg
│ ├── ic_message_reply.svg
│ ├── ic_more.svg
│ ├── ic_new_chat_icon.svg
│ ├── ic_new_contact_icon.svg
│ ├── ic_new_group_icon.svg
│ ├── ic_not_show_pwd.svg
│ ├── ic_phone.svg
│ ├── ic_photo_album.svg
│ ├── ic_private_chat.svg
│ ├── ic_scan.svg
│ ├── ic_search.svg
│ ├── ic_send.svg
│ ├── ic_share_add.svg
│ ├── ic_share_app.svg
│ ├── ic_show_pwd.svg
│ ├── ic_story_collect.svg
│ ├── ic_story_comment.svg
│ ├── ic_story_like.svg
│ ├── ic_story_share.svg
│ ├── ic_voice.svg
│ ├── replaced
│ │ └── close.png
│ ├── share
│ │ ├── github.png
│ │ ├── juejin.png
│ │ ├── wechat.png
│ │ └── wechat_taxze.jpg
│ └── voice
│ │ ├── ic_voice_black.webp
│ │ ├── ic_voice_blue.webp
│ │ ├── ic_voice_cancel.webp
│ │ ├── ic_voice_confirm.webp
│ │ ├── ic_voice_convert_fail.webp
│ │ ├── ic_voice_convert_suc.webp
│ │ ├── ic_voice_record_bg1.webp
│ │ ├── ic_voice_record_bg2.webp
│ │ ├── ic_voice_record_cancel_grey.webp
│ │ ├── ic_voice_record_cancel_white.webp
│ │ ├── ic_voice_record_speaker.webp
│ │ ├── ic_voice_record_zi_grey.webp
│ │ └── ic_voice_record_zi_white.webp
└── key
│ └── public.pem
├── devtools_options.yaml
├── ios
├── .gitignore
├── Flutter
│ ├── AppFrameworkInfo.plist
│ ├── Debug.xcconfig
│ └── Release.xcconfig
├── Podfile
├── Podfile.lock
├── Runner.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── WorkspaceSettings.xcsettings
├── Runner
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── Icon-App-1024x1024@1x.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-83.5x83.5@2x.png
│ │ └── LaunchImage.imageset
│ │ │ ├── Contents.json
│ │ │ ├── LaunchImage.png
│ │ │ ├── LaunchImage@2x.png
│ │ │ ├── LaunchImage@3x.png
│ │ │ └── README.md
│ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── Info.plist
│ └── Runner-Bridging-Header.h
└── RunnerTests
│ └── RunnerTests.swift
├── lib
├── app.dart
├── common
│ ├── apis.dart
│ ├── config.dart
│ ├── fluent_emoji_icon_data.dart
│ ├── global_data.dart
│ ├── ip_config.dart
│ └── urls.dart
├── core
│ ├── app_controller.dart
│ └── permission_controller.dart
├── im
│ └── im_utils.dart
├── main.dart
├── models
│ ├── api_resp.dart
│ ├── contact.dart
│ ├── file_path.dart
│ ├── message.dart
│ ├── user_info.dart
│ └── user_story.dart
├── pages
│ ├── chat
│ │ ├── add_friend
│ │ │ ├── add_friend_binding.dart
│ │ │ ├── add_friend_logic.dart
│ │ │ └── add_friend_view.dart
│ │ ├── chat
│ │ │ ├── chat_binding.dart
│ │ │ ├── chat_logic.dart
│ │ │ ├── chat_view.dart
│ │ │ └── widget
│ │ │ │ ├── chat_input_box_view.dart
│ │ │ │ ├── chat_item_view.dart
│ │ │ │ ├── chat_listview.dart
│ │ │ │ ├── chat_picture.dart
│ │ │ │ ├── chat_quote_text.dart
│ │ │ │ ├── chat_reply_emoji.dart
│ │ │ │ ├── chat_send_failed_view.dart
│ │ │ │ ├── chat_send_progress.dart
│ │ │ │ ├── chat_single_layout.dart
│ │ │ │ ├── chat_text.dart
│ │ │ │ ├── chat_typing_view.dart
│ │ │ │ ├── chat_voice_record_widget
│ │ │ │ ├── chat_voice_record_bar.dart
│ │ │ │ ├── chat_voice_record_layout.dart
│ │ │ │ ├── chat_voice_record_view.dart
│ │ │ │ └── voice_record.dart
│ │ │ │ ├── chat_voice_view.dart
│ │ │ │ ├── interactive_dialog
│ │ │ │ ├── interactive_dialog.dart
│ │ │ │ ├── list_meme_widget.dart
│ │ │ │ └── list_tools_widget.dart
│ │ │ │ └── menu
│ │ │ │ ├── chat_menu.dart
│ │ │ │ └── message_custom_popup.dart
│ │ ├── conversation_binding.dart
│ │ ├── conversation_logic.dart
│ │ ├── conversation_view.dart
│ │ ├── new_chat
│ │ │ ├── new_chat_binding.dart
│ │ │ ├── new_chat_logic.dart
│ │ │ ├── new_chat_view.dart
│ │ │ └── widget
│ │ │ │ ├── new_chat_cursor.dart
│ │ │ │ ├── new_chat_index_bar.dart
│ │ │ │ └── new_chat_item.dart
│ │ ├── rtc
│ │ │ └── video_call
│ │ │ │ ├── video_call_binding.dart
│ │ │ │ ├── video_call_logic.dart
│ │ │ │ └── video_call_page.dart
│ │ └── widget
│ │ │ ├── appbar_title.dart
│ │ │ ├── conversation_item_view.dart
│ │ │ └── dashed_circle_border.dart
│ ├── common
│ │ └── check_code
│ │ │ ├── check_code_binding.dart
│ │ │ ├── check_code_logic.dart
│ │ │ └── check_code_view.dart
│ ├── home
│ │ ├── home_binding.dart
│ │ ├── home_logic.dart
│ │ ├── home_view.dart
│ │ └── widget
│ │ │ └── home_dialog.dart
│ ├── login
│ │ ├── invite
│ │ │ ├── invite_binding.dart
│ │ │ ├── invite_logic.dart
│ │ │ ├── invite_state.dart
│ │ │ └── invite_view.dart
│ │ ├── login_binding.dart
│ │ ├── login_email
│ │ │ ├── login_email_binding.dart
│ │ │ ├── login_email_logic.dart
│ │ │ └── login_email_view.dart
│ │ ├── login_logic.dart
│ │ ├── login_view.dart
│ │ ├── register
│ │ │ ├── register_binding.dart
│ │ │ ├── register_logic.dart
│ │ │ └── register_view.dart
│ │ └── widget
│ │ │ └── login_universal_widget.dart
│ ├── mine
│ │ ├── mine_binding.dart
│ │ ├── mine_logic.dart
│ │ ├── mine_story_details
│ │ │ ├── mine_story_details_binding.dart
│ │ │ ├── mine_story_details_logic.dart
│ │ │ ├── mine_story_details_view.dart
│ │ │ └── widget
│ │ │ │ └── mine_story_interactive_dialog
│ │ │ │ ├── list_meme_widget.dart
│ │ │ │ ├── list_reply_input.dart
│ │ │ │ └── mine_story_interactive_dialog.dart
│ │ ├── mine_view.dart
│ │ └── profile
│ │ │ ├── profile_binding.dart
│ │ │ ├── profile_logic.dart
│ │ │ ├── profile_state.dart
│ │ │ ├── profile_view.dart
│ │ │ └── widget
│ │ │ ├── password_dialog.dart
│ │ │ └── setting_dialog.dart
│ └── splash
│ │ ├── contact_me_dialog.dart
│ │ ├── splash_binding.dart
│ │ ├── splash_logic.dart
│ │ └── splash_view.dart
├── res
│ ├── images.dart
│ ├── lang
│ │ ├── en_US.dart
│ │ └── zh_CN.dart
│ ├── strings.dart
│ └── styles.dart
├── routes
│ ├── app_navigator.dart
│ ├── app_pages.dart
│ ├── app_routes.dart
│ └── router_utils.dart
├── utils
│ ├── app_reg_exp.dart
│ ├── app_utils.dart
│ ├── db
│ │ ├── base_db_provider.dart
│ │ ├── conversation_db_provider.dart
│ │ └── sql_manager.dart
│ ├── device_utils.dart
│ ├── file_util.dart
│ ├── http_util.dart
│ ├── image_util.dart
│ ├── object_util.dart
│ ├── permission_util.dart
│ ├── regex_util.dart
│ ├── sp
│ │ ├── data_persistence.dart
│ │ └── sp_util.dart
│ ├── touch_close_keyboard.dart
│ └── web_socket_manager.dart
└── widget
│ ├── app_view.dart
│ ├── aspect_ratio_image.dart
│ ├── avatar_widget.dart
│ ├── barrage
│ ├── barrage_config.dart
│ ├── barrage_controller.dart
│ ├── barrage_model.dart
│ ├── barrage_track.dart
│ ├── barrage_utils.dart
│ └── barrage_view.dart
│ ├── cached_image.dart
│ ├── click_item.dart
│ ├── dashed_circle_border.dart
│ ├── expanded_viewport.dart
│ ├── loading_view.dart
│ ├── mimicking_hero_animation_image.dart
│ ├── my_appbar.dart
│ ├── my_check_box.dart
│ ├── photo_browser.dart
│ ├── text_btn.dart
│ ├── toast_utils.dart
│ └── water_mark_view.dart
├── macos
├── .gitignore
├── Flutter
│ ├── Flutter-Debug.xcconfig
│ ├── Flutter-Release.xcconfig
│ └── GeneratedPluginRegistrant.swift
├── Podfile
├── Runner.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── Runner
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ │ └── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── app_icon_1024.png
│ │ │ ├── app_icon_128.png
│ │ │ ├── app_icon_16.png
│ │ │ ├── app_icon_256.png
│ │ │ ├── app_icon_32.png
│ │ │ ├── app_icon_512.png
│ │ │ └── app_icon_64.png
│ ├── Base.lproj
│ │ └── MainMenu.xib
│ ├── Configs
│ │ ├── AppInfo.xcconfig
│ │ ├── Debug.xcconfig
│ │ ├── Release.xcconfig
│ │ └── Warnings.xcconfig
│ ├── DebugProfile.entitlements
│ ├── Info.plist
│ ├── MainFlutterWindow.swift
│ └── Release.entitlements
└── RunnerTests
│ └── RunnerTests.swift
├── pubspec.lock
├── pubspec.yaml
├── test
└── widget_test.dart
├── web
├── favicon.png
├── icons
│ ├── Icon-192.png
│ ├── Icon-512.png
│ ├── Icon-maskable-192.png
│ └── Icon-maskable-512.png
├── index.html
└── manifest.json
└── windows
├── .gitignore
├── CMakeLists.txt
├── flutter
├── CMakeLists.txt
├── generated_plugin_registrant.cc
├── generated_plugin_registrant.h
└── generated_plugins.cmake
└── runner
├── CMakeLists.txt
├── Runner.rc
├── flutter_window.cpp
├── flutter_window.h
├── main.cpp
├── resource.h
├── resources
└── app_icon.ico
├── runner.exe.manifest
├── utils.cpp
├── utils.h
├── win32_window.cpp
└── win32_window.h
/.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 |
35 | # Symbolication related
36 | app.*.symbols
37 |
38 | # Obfuscation related
39 | app.*.map.json
40 |
41 | # Android Studio will place build artifacts here
42 | /android/app/debug
43 | /android/app/profile
44 | /android/app/release
45 |
46 | # Ignore pubspec.lock
47 | pubspec.lock
48 |
49 | # Ignore ip_config.dart
50 | /lib/common/ip_config.dart
--------------------------------------------------------------------------------
/.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 and should not be manually edited.
5 |
6 | version:
7 | revision: "ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a"
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: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a
17 | base_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a
18 | - platform: android
19 | create_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a
20 | base_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a
21 | - platform: ios
22 | create_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a
23 | base_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a
24 | - platform: macos
25 | create_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a
26 | base_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a
27 | - platform: web
28 | create_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a
29 | base_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a
30 | - platform: windows
31 | create_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a
32 | base_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a
33 |
34 | # User provided section
35 |
36 | # List of Local paths (relative to this file) that should be
37 | # ignored by the migrate tool.
38 | #
39 | # Files that are not part of the templates will be ignored by default.
40 | unmanaged_files:
41 | - 'lib/main.dart'
42 | - 'ios/Runner.xcodeproj/project.pbxproj'
43 |
--------------------------------------------------------------------------------
/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 https://dart.dev/lints.
17 | #
18 | # Instead of disabling a lint rule for the entire project in the
19 | # section below, it can also be suppressed for a single line of code
20 | # or a specific dart file by using the `// ignore: name_of_lint` and
21 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file
22 | # producing the lint.
23 | rules:
24 | # avoid_print: false # Uncomment to disable the `avoid_print` rule
25 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
26 |
27 | # Additional information about this file can be found at
28 | # https://dart.dev/guides/language/analysis-options
29 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id "com.android.application"
3 | id "kotlin-android"
4 | id "dev.flutter.flutter-gradle-plugin"
5 | }
6 |
7 | def localProperties = new Properties()
8 | def localPropertiesFile = rootProject.file('local.properties')
9 | if (localPropertiesFile.exists()) {
10 | localPropertiesFile.withReader('UTF-8') { reader ->
11 | localProperties.load(reader)
12 | }
13 | }
14 |
15 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
16 | if (flutterVersionCode == null) {
17 | flutterVersionCode = '1'
18 | }
19 |
20 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
21 | if (flutterVersionName == null) {
22 | flutterVersionName = '1.0'
23 | }
24 |
25 | android {
26 | namespace "com.taxze.chat.craft.flutter_chat_craft"
27 | compileSdkVersion 34
28 | ndkVersion flutter.ndkVersion
29 |
30 | compileOptions {
31 | sourceCompatibility JavaVersion.VERSION_1_8
32 | targetCompatibility JavaVersion.VERSION_1_8
33 | }
34 |
35 | kotlinOptions {
36 | jvmTarget = '1.8'
37 | }
38 |
39 | sourceSets {
40 | main.java.srcDirs += 'src/main/kotlin'
41 | }
42 |
43 | defaultConfig {
44 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
45 | applicationId "com.taxze.chat.craft.flutter_chat_craft"
46 | // You can update the following values to match your application needs.
47 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
48 | minSdkVersion 21
49 | targetSdkVersion flutter.targetSdkVersion
50 | versionCode flutterVersionCode.toInteger()
51 | versionName flutterVersionName
52 | }
53 |
54 | buildTypes {
55 | release {
56 | // TODO: Add your own signing config for the release build.
57 | // Signing with the debug keys for now, so `flutter run --release` works.
58 | signingConfig signingConfigs.debug
59 | }
60 | }
61 | }
62 |
63 | flutter {
64 | source '../..'
65 | }
66 |
67 | dependencies {}
68 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
16 |
24 |
28 |
31 |
32 |
33 |
34 |
35 |
36 |
38 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/taxze/chat/craft/flutter_chat_craft/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.taxze.chat.craft.flutter_chat_craft
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.7.10'
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:7.3.0'
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 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/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.5-all.zip
6 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | def flutterSdkPath = {
3 | def properties = new Properties()
4 | file("local.properties").withInputStream { properties.load(it) }
5 | def flutterSdkPath = properties.getProperty("flutter.sdk")
6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
7 | return flutterSdkPath
8 | }
9 | settings.ext.flutterSdkPath = flutterSdkPath()
10 |
11 | includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
12 |
13 | plugins {
14 | id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false
15 | }
16 | }
17 |
18 | include ":app"
19 |
20 | apply from: "${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle/app_plugin_loader.gradle"
21 |
--------------------------------------------------------------------------------
/assets/fonts/bahnschrift.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/assets/fonts/bahnschrift.ttf
--------------------------------------------------------------------------------
/assets/images/app_icon.svg:
--------------------------------------------------------------------------------
1 |
20 |
--------------------------------------------------------------------------------
/assets/images/back.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/images/ic_add.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/images/ic_arrow.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/ic_audio.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/images/ic_back_left.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/images/ic_browser.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/assets/images/ic_browser.png
--------------------------------------------------------------------------------
/assets/images/ic_camera.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/assets/images/ic_check.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/images/ic_collect.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/assets/images/ic_down_arrow.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/ic_email.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/images/ic_friend_setting.svg:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/assets/images/ic_group_chat.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/images/ic_home.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/images/ic_keyboard.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/assets/images/ic_like.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/assets/images/ic_logout.svg:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/assets/images/ic_mass_send.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/images/ic_me.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/assets/images/ic_message_copy.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/ic_message_forword.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/ic_message_reply.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/ic_more.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/assets/images/ic_new_chat_icon.svg:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/assets/images/ic_new_contact_icon.svg:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/assets/images/ic_new_group_icon.svg:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/assets/images/ic_phone.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/images/ic_photo_album.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/images/ic_private_chat.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/assets/images/ic_scan.svg:
--------------------------------------------------------------------------------
1 |
14 |
--------------------------------------------------------------------------------
/assets/images/ic_search.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/images/ic_send.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/images/ic_share_add.svg:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/assets/images/ic_share_app.svg:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/assets/images/ic_show_pwd.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/ic_story_collect.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/images/ic_story_comment.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/images/ic_story_like.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/images/ic_story_share.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/images/ic_voice.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/assets/images/replaced/close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/assets/images/replaced/close.png
--------------------------------------------------------------------------------
/assets/images/share/github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/assets/images/share/github.png
--------------------------------------------------------------------------------
/assets/images/share/juejin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/assets/images/share/juejin.png
--------------------------------------------------------------------------------
/assets/images/share/wechat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/assets/images/share/wechat.png
--------------------------------------------------------------------------------
/assets/images/share/wechat_taxze.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/assets/images/share/wechat_taxze.jpg
--------------------------------------------------------------------------------
/assets/images/voice/ic_voice_black.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/assets/images/voice/ic_voice_black.webp
--------------------------------------------------------------------------------
/assets/images/voice/ic_voice_blue.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/assets/images/voice/ic_voice_blue.webp
--------------------------------------------------------------------------------
/assets/images/voice/ic_voice_cancel.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/assets/images/voice/ic_voice_cancel.webp
--------------------------------------------------------------------------------
/assets/images/voice/ic_voice_confirm.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/assets/images/voice/ic_voice_confirm.webp
--------------------------------------------------------------------------------
/assets/images/voice/ic_voice_convert_fail.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/assets/images/voice/ic_voice_convert_fail.webp
--------------------------------------------------------------------------------
/assets/images/voice/ic_voice_convert_suc.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/assets/images/voice/ic_voice_convert_suc.webp
--------------------------------------------------------------------------------
/assets/images/voice/ic_voice_record_bg1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/assets/images/voice/ic_voice_record_bg1.webp
--------------------------------------------------------------------------------
/assets/images/voice/ic_voice_record_bg2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/assets/images/voice/ic_voice_record_bg2.webp
--------------------------------------------------------------------------------
/assets/images/voice/ic_voice_record_cancel_grey.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/assets/images/voice/ic_voice_record_cancel_grey.webp
--------------------------------------------------------------------------------
/assets/images/voice/ic_voice_record_cancel_white.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/assets/images/voice/ic_voice_record_cancel_white.webp
--------------------------------------------------------------------------------
/assets/images/voice/ic_voice_record_speaker.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/assets/images/voice/ic_voice_record_speaker.webp
--------------------------------------------------------------------------------
/assets/images/voice/ic_voice_record_zi_grey.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/assets/images/voice/ic_voice_record_zi_grey.webp
--------------------------------------------------------------------------------
/assets/images/voice/ic_voice_record_zi_white.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/assets/images/voice/ic_voice_record_zi_white.webp
--------------------------------------------------------------------------------
/assets/key/public.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN PUBLIC KEY-----
2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvy/sRrCh0mSraE/71Cgk
3 | 7UPkCboXK+gqdQy/GYJtrAnS03Q5Eii4b6mrG1aBSpN81i11W6P+zx8O6c6jNk/6
4 | bJy2tTXKOmwX9gLRfYyr8XAwzHfQAMmCv50fPaqxOcUmuMMuaGnI6wjLR0KaYxAC
5 | 4IxlmQvQE/Ad+FBFHeUGW/Ad82IEXcMq+NFBTcbVEz7/emY+GU1mgmAbFhe+f8W3
6 | Di6ICRggdzCOdgvbQiM03Lfp7a2hhMwGbBtTlkD/T6HZrlWI7/W4VkL6LgscQWex
7 | O4T7t2HG8808uzLkwQu8iXqGGabg4hFNXPL3VJQhVd8iZ24VFWEXhFVyU/hLh66y
8 | 6wIDAQAB
9 | -----END PUBLIC KEY-----
10 |
--------------------------------------------------------------------------------
/devtools_options.yaml:
--------------------------------------------------------------------------------
1 | extensions:
2 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | 12.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.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.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
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 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/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/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/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/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/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/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/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/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/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/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/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/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/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/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/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/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/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/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/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/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/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/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/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/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/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`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CADisableMinimumFrameDurationOnPhone
6 |
7 | CFBundleDevelopmentRegion
8 | $(DEVELOPMENT_LANGUAGE)
9 | CFBundleDisplayName
10 | Flutter Chat Craft
11 | CFBundleExecutable
12 | $(EXECUTABLE_NAME)
13 | CFBundleIdentifier
14 | $(PRODUCT_BUNDLE_IDENTIFIER)
15 | CFBundleInfoDictionaryVersion
16 | 6.0
17 | CFBundleName
18 | flutter_chat_craft
19 | CFBundlePackageType
20 | APPL
21 | CFBundleShortVersionString
22 | $(FLUTTER_BUILD_NAME)
23 | CFBundleSignature
24 | ????
25 | CFBundleVersion
26 | $(FLUTTER_BUILD_NUMBER)
27 | LSRequiresIPhoneOS
28 |
29 | NSMicrophoneUsageDescription
30 | 用于即时通讯发送语音消息需要访问麦克风的功能
31 | NSPhotoLibraryAddUsageDescription
32 | 用于即时通讯发送图片消息需要访问相册的功能
33 | NSPhotoLibraryUsageDescription
34 | 用于即时通讯发送图片消息需要访问相册的功能
35 | NSCameraUsageDescription
36 | 用于即时通讯发送图片消息需要访问相机的功能
37 | UIApplicationSupportsIndirectInputEvents
38 |
39 | UILaunchStoryboardName
40 | LaunchScreen
41 | UIMainStoryboardFile
42 | Main
43 | UISupportedInterfaceOrientations
44 |
45 | UIInterfaceOrientationPortrait
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/ios/RunnerTests/RunnerTests.swift:
--------------------------------------------------------------------------------
1 | import Flutter
2 | import UIKit
3 | import XCTest
4 |
5 | class RunnerTests: XCTestCase {
6 |
7 | func testExample() {
8 | // If you add code to the Runner application, consider adding tests here.
9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest.
10 | }
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/lib/app.dart:
--------------------------------------------------------------------------------
1 | import 'package:bot_toast/bot_toast.dart';
2 | import 'package:flutter/cupertino.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:get/get.dart';
5 | import 'core/permission_controller.dart';
6 | import 'res/strings.dart';
7 | import 'routes/app_pages.dart';
8 | import 'routes/app_routes.dart';
9 | import 'widget/app_view.dart';
10 | import 'package:flutter_localizations/flutter_localizations.dart';
11 |
12 | class ChatCraftApp extends StatelessWidget {
13 | const ChatCraftApp({super.key});
14 |
15 | @override
16 | Widget build(BuildContext context) {
17 | return AppView(
18 | builder: (locale, builder) =>
19 | GetMaterialApp(
20 | debugShowCheckedModeBanner: false,
21 | enableLog: true,
22 | builder: builder,
23 | // logWriterCallback: Logger.print,
24 | translations: TranslationService(),
25 | navigatorObservers: [BotToastNavigatorObserver()],
26 | localizationsDelegates: const [
27 | GlobalMaterialLocalizations.delegate,
28 | GlobalWidgetsLocalizations.delegate,
29 | GlobalCupertinoLocalizations.delegate,
30 | ],
31 | fallbackLocale: TranslationService.fallbackLocale,
32 | locale: locale,
33 | localeResolutionCallback: (locale, list) {
34 | Get.locale ??= locale;
35 | return locale;
36 | },
37 | theme: ThemeData(
38 | fontFamily: "bahnschrift",
39 | useMaterial3: true,
40 | ),
41 | supportedLocales: const [
42 | Locale('zh', 'CN'),
43 | Locale('en', 'US'),
44 | ],
45 | getPages: AppPages.routes,
46 | initialBinding: InitBinding(),
47 | initialRoute: AppRoutes.splash,
48 | ),
49 | );
50 | }
51 | }
52 |
53 | class InitBinding extends Bindings {
54 | @override
55 | void dependencies() {
56 | Get.put(PermissionController());
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/lib/common/config.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:flutter/cupertino.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter/services.dart';
6 | import 'package:flutter_chat_craft/common/ip_config.dart';
7 |
8 | import '../utils/db/sql_manager.dart';
9 | import '../utils/sp/sp_util.dart';
10 |
11 | class Config {
12 | static late String cachePath;
13 | static const UI_W = 375.0;
14 | static const UI_H = 812.0;
15 |
16 | static const apiUrl = "http://${IpConfig.ip}/v1";
17 |
18 | static Future init(Function() runApp) async {
19 | // Initialize a WidgetsBinding to ensure that the Flutter framework has been initialized
20 | WidgetsFlutterBinding.ensureInitialized();
21 | await SpUtil.getInstance();
22 | SQLManager.init();
23 | runApp();
24 | // Set screen orientation
25 | SystemChrome.setPreferredOrientations([
26 | DeviceOrientation.portraitUp,
27 | DeviceOrientation.portraitDown,
28 | ]);
29 | // Transparent status bar (Android).
30 | var brightness = Platform.isAndroid ? Brightness.dark : Brightness.light;
31 | SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
32 | statusBarColor: Colors.transparent,
33 | statusBarBrightness: brightness,
34 | statusBarIconBrightness: brightness,
35 | ));
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/lib/common/global_data.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_chat_craft/models/user_info.dart';
2 |
3 | class GlobalData {
4 | static late UserInfo userInfo;
5 | static late String token;
6 | }
7 |
--------------------------------------------------------------------------------
/lib/common/ip_config.dart:
--------------------------------------------------------------------------------
1 | class IpConfig {
2 | //Replace with server address
3 | static const ip = "192.168.31.123:8889";
4 | }
5 |
--------------------------------------------------------------------------------
/lib/common/urls.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_chat_craft/common/ip_config.dart';
2 |
3 | import 'config.dart';
4 |
5 | String webSocketUrl = "ws://";
6 |
7 | class Urls {
8 | //user
9 | static var login = "${Config.apiUrl}/user/login";
10 | static var emailLogin = "${Config.apiUrl}/user/email_login";
11 | static var emailLoginCodeCheck =
12 | "${Config.apiUrl}/user/email_login_code_check";
13 | static var register = "${Config.apiUrl}/user/register";
14 | static var emailRegisterCodeCheck =
15 | "${Config.apiUrl}/user/register_email_code_check";
16 | static var findUserWithName = "${Config.apiUrl}/user/find_user_with_name";
17 |
18 | static var getUserShowStoryList =
19 | "${Config.apiUrl}/user/get_user_show_story_list";
20 |
21 | static var addOrRemoveUserStoryLike = "${Config.apiUrl}/user/add_story_like";
22 |
23 | static var addUserStoryComment = "${Config.apiUrl}/user/add_story_comment";
24 | static var modifyUserInfo = "${Config.apiUrl}/user/user_info_update";
25 | static var modifyPassword = "${Config.apiUrl}/user/user_info_password_update";
26 |
27 | //relation
28 | static var addFriendWithUserName = "${Config.apiUrl}/relation/add_username";
29 | static var loadFriends = "${Config.apiUrl}/relation/list";
30 |
31 | //message
32 | static var sendUserMsg =
33 | "$webSocketUrl${IpConfig.ip}/v1/message/send_user_msg";
34 | static var getRedisMsg = "${Config.apiUrl}/message/get_redis_msg";
35 |
36 | //upload
37 | static var uploadFile = "${Config.apiUrl}/upload/file";
38 |
39 | //Init File-Download
40 | static var downloadEmojiZip = "${Config.apiUrl}/upload/getEmojiZip";
41 | }
42 |
--------------------------------------------------------------------------------
/lib/core/app_controller.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 | import '../utils/sp/data_persistence.dart';
4 |
5 | class AppController extends GetxController {
6 | Locale? getLocale() {
7 | var local = Get.locale;
8 | var index = DataPersistence.getLanguage() ?? 0;
9 | switch (index) {
10 | case 1:
11 | local = const Locale('zh', 'CN');
12 | break;
13 | case 2:
14 | local = const Locale('en', 'US');
15 | break;
16 | }
17 | return local;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/lib/core/permission_controller.dart:
--------------------------------------------------------------------------------
1 | import 'package:get/get.dart';
2 | import 'package:permission_handler/permission_handler.dart';
3 | import '../utils/permission_util.dart';
4 |
5 | class PermissionController extends GetxController {
6 | @override
7 | void onInit() async {
8 | Map statuses = await PermissionUtil.request([
9 | Permission.camera,
10 | Permission.storage,
11 | Permission.microphone,
12 | Permission.speech,
13 | Permission.location,
14 | Permission.notification,
15 | ]);
16 | super.onInit();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/lib/im/im_utils.dart:
--------------------------------------------------------------------------------
1 | import '../models/message.dart';
2 | import '../res/strings.dart';
3 |
4 | class IMUtils {
5 | //Format the time.
6 | static String? formatTime(String? iso8601Time) {
7 | if (iso8601Time == null) {
8 | return null;
9 | } else {
10 | var dateTime = DateTime.parse(iso8601Time);
11 | return _timeFormatEx(dateTime.millisecondsSinceEpoch ~/ 1000);
12 | }
13 | }
14 |
15 | static String _timeFormatEx(int time) {
16 | var dateTime = DateTime.fromMillisecondsSinceEpoch(time * 1000);
17 | var now = DateTime.now();
18 | // 今天,显示时分
19 | // 昨天,显示昨天
20 | // 其他,显示年月日
21 | if (dateTime.day == now.day) {
22 | return '${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}';
23 | }
24 |
25 | var yesterday = now.subtract(const Duration(days: 1));
26 | if (dateTime.day == yesterday.day) {
27 | return "昨天 ${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}";
28 | }
29 |
30 | var week = now.subtract(const Duration(days: 7));
31 | if (dateTime.millisecondsSinceEpoch > week.millisecondsSinceEpoch) {
32 | var weekday = "星期一";
33 | switch (dateTime.weekday) {
34 | case 2:
35 | weekday = "星期二";
36 | break;
37 | case 3:
38 | weekday = "星期三";
39 | break;
40 | case 4:
41 | weekday = "星期四";
42 | break;
43 | case 5:
44 | weekday = "星期五";
45 | break;
46 | case 6:
47 | weekday = "星期六";
48 | break;
49 | case 7:
50 | weekday = "星期日";
51 | break;
52 | }
53 |
54 | return "$weekday ${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}";
55 | }
56 |
57 | // 其他情况,显示年月日
58 | return '${dateTime.year}-${dateTime.month}-${dateTime.day}';
59 | }
60 |
61 | static String messageTypeToString({
62 | required int messageType,
63 | required String content,
64 | }) {
65 | switch (messageType) {
66 | case MessageType.text:
67 | return content;
68 | case MessageType.picture:
69 | return "[${StrRes.picture}]";
70 | case MessageType.voice:
71 | return "[${StrRes.voice}]";
72 | case MessageType.video:
73 | return "[${StrRes.video}]";
74 | case MessageType.quote:
75 | return "${StrRes.quote}:$content";
76 | default:
77 | return "";
78 | }
79 | }
80 | }
81 |
82 | /// complete something within a time interval
83 | class IntervalDo {
84 | DateTime? last;
85 |
86 | void run({required Function() fuc, int milliseconds = 0}) {
87 | DateTime now = DateTime.now();
88 | if (null == last ||
89 | now.difference(last ?? now).inMilliseconds > milliseconds) {
90 | last = now;
91 | fuc();
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_chat_craft/app.dart';
3 | import 'package:flutter_chat_craft/common/config.dart';
4 |
5 | /// @Author: Taxze
6 | /// @GitHub: https://github.com/taxze6
7 | /// @Email: taxze.xiaoyan@gmail.com
8 | /// @Date: 2023/11/21
9 | void main() {
10 | Config.init(() => runApp(ChatCraftApp()));
11 | }
12 |
--------------------------------------------------------------------------------
/lib/models/api_resp.dart:
--------------------------------------------------------------------------------
1 | class ApiResp {
2 | int code;
3 | String message;
4 | dynamic data;
5 |
6 | ApiResp({required this.code, required this.message, required this.data});
7 |
8 | ApiResp.fromJson(Map map)
9 | : code = map["Code"],
10 | message = map["Message"],
11 | data = map["Data"];
12 |
13 | Map toJson() {
14 | final data = {};
15 | data['Code'] = code;
16 | data['Message'] = message;
17 | data['Data'] = data;
18 | return data;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/lib/models/contact.dart:
--------------------------------------------------------------------------------
1 | import 'dart:ui';
2 |
3 | import 'user_info.dart';
4 |
5 | class ContactModel {
6 | final String section;
7 |
8 | // final List users;
9 |
10 | final List users;
11 |
12 | ContactModel({
13 | required this.section,
14 | required this.users,
15 | });
16 | }
17 |
18 | class CursorInfoModel {
19 | final String title;
20 | final Offset offset;
21 |
22 | CursorInfoModel({
23 | required this.title,
24 | required this.offset,
25 | });
26 | }
27 |
--------------------------------------------------------------------------------
/lib/models/file_path.dart:
--------------------------------------------------------------------------------
1 | class FilePath {
2 | static String emojiZipDownloadPath = "/chat-craft/emoji_icon_data.zip";
3 |
4 | static String emojiPath = "/chat-craft";
5 | }
6 |
--------------------------------------------------------------------------------
/lib/models/user_info.dart:
--------------------------------------------------------------------------------
1 | class UserInfo {
2 | int userID;
3 | String userName;
4 | String email;
5 | String phone;
6 | String avatar;
7 | String motto;
8 | String clientIp;
9 | String clientPort;
10 |
11 | UserInfo({
12 | required this.userID,
13 | required this.userName,
14 | required this.email,
15 | required this.phone,
16 | required this.avatar,
17 | required this.motto,
18 | required this.clientIp,
19 | required this.clientPort,
20 | });
21 |
22 | UserInfo.fromJson(Map json)
23 | : userID = json['id'],
24 | userName = json['name'],
25 | email = json['email'],
26 | phone = json['phone'],
27 | avatar = json['avatar'],
28 | motto = json['motto'],
29 | clientIp = json['client_ip'],
30 | clientPort = json['client_port'];
31 |
32 | Map toJson() {
33 | return {
34 | 'id': userID,
35 | 'name': userName,
36 | 'email': email,
37 | 'phone': phone,
38 | 'avatar': avatar,
39 | 'motto': motto,
40 | 'client_ip': clientIp,
41 | 'client_port': clientPort,
42 | };
43 | }
44 |
45 | @override
46 | String toString() {
47 | return 'UserInfo{userID: $userID, userName: $userName, email: $email, phone: $phone, avatar: $avatar, motto: $motto, clientIp: $clientIp, clientPort: $clientPort}';
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/lib/pages/chat/add_friend/add_friend_binding.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_chat_craft/pages/chat/add_friend/add_friend_logic.dart';
2 | import 'package:get/get.dart';
3 |
4 | class AddFriendBinding extends Bindings {
5 | @override
6 | void dependencies() => Get.lazyPut(() => AddFriendLogic());
7 | }
8 |
--------------------------------------------------------------------------------
/lib/pages/chat/chat/chat_binding.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_chat_craft/pages/chat/chat/chat_logic.dart';
2 | import 'package:get/get.dart';
3 |
4 | class ChatBinding extends Bindings {
5 | @override
6 | void dependencies() => Get.lazyPut(() => ChatLogic());
7 | }
8 |
--------------------------------------------------------------------------------
/lib/pages/chat/chat/widget/chat_reply_emoji.dart:
--------------------------------------------------------------------------------
1 | import 'package:fluentui_emoji_icon/fluentui_emoji_icon.dart';
2 | import 'package:flutter/cupertino.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_screenutil/flutter_screenutil.dart';
5 |
6 | import '../../../../common/fluent_emoji_icon_data.dart';
7 |
8 | class ChatReplyEmoji extends StatelessWidget {
9 | const ChatReplyEmoji({
10 | Key? key,
11 | this.replyEmoji,
12 | required this.isFromMsg,
13 | }) : super(key: key);
14 | final List? replyEmoji;
15 | final bool isFromMsg;
16 |
17 | @override
18 | Widget build(BuildContext context) {
19 | return Container(
20 | alignment: Alignment.centerLeft,
21 | margin: EdgeInsets.only(bottom: 6.w),
22 | width: double.infinity,
23 | constraints: BoxConstraints(
24 | maxWidth: (replyEmoji!.length * 20.w + 24.w) > 0.7.sw
25 | ? 0.7.sw
26 | : (replyEmoji!.length * 20.w + 24.w).toDouble(),
27 | maxHeight: 32.w),
28 | padding: EdgeInsets.symmetric(vertical: 6.w, horizontal: 12.w),
29 | decoration: BoxDecoration(
30 | color: Colors.grey.shade100,
31 | borderRadius: BorderRadius.only(
32 | topLeft: Radius.circular(isFromMsg ? 0 : 12),
33 | topRight: Radius.circular(isFromMsg ? 12 : 0),
34 | bottomRight: const Radius.circular(12),
35 | bottomLeft: const Radius.circular(12),
36 | ),
37 | ),
38 | child: ListView.builder(
39 | itemCount: replyEmoji?.length,
40 | scrollDirection: Axis.horizontal,
41 | shrinkWrap: true,
42 | itemBuilder: (context, index) {
43 | return Padding(
44 | padding: EdgeInsets.symmetric(horizontal: 2.w),
45 | child: FluentUiEmojiIcon(
46 | w: 16.w,
47 | h: 16.w,
48 | fl: FluentEmojiIconData.stringGetFluentsData(
49 | replyEmoji![index],
50 | ),
51 | ),
52 | );
53 | },
54 | ),
55 | );
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/lib/pages/chat/chat/widget/chat_send_failed_view.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'chat_item_view.dart';
5 |
6 | class ChatSendFailedView extends StatefulWidget {
7 | final String msgId;
8 | final bool isReceived;
9 | final Stream>? stream;
10 | final bool isSendFailed;
11 | final Function()? onFailedResend;
12 |
13 | const ChatSendFailedView({
14 | Key? key,
15 | required this.msgId,
16 | required this.isReceived,
17 | this.isSendFailed = false,
18 | this.stream,
19 | this.onFailedResend,
20 | }) : super(key: key);
21 |
22 | @override
23 | _ChatSendFailedViewState createState() => _ChatSendFailedViewState();
24 | }
25 |
26 | class _ChatSendFailedViewState extends State {
27 | late bool _failed;
28 | StreamSubscription? _statusSubs;
29 |
30 | @override
31 | void initState() {
32 | _failed = widget.isSendFailed;
33 | _statusSubs = widget.stream?.listen((event) {
34 | if (!mounted) return;
35 | if (widget.msgId == event.msgId) {
36 | setState(() {
37 | _failed = !event.value;
38 | });
39 | }
40 | });
41 | super.initState();
42 | }
43 |
44 | @override
45 | void dispose() {
46 | _statusSubs?.cancel();
47 | super.dispose();
48 | }
49 |
50 | @override
51 | Widget build(BuildContext context) {
52 | return Visibility(
53 | visible: !widget.isReceived && _failed,
54 | child: GestureDetector(
55 | behavior: HitTestBehavior.translucent,
56 | onTap: widget.onFailedResend,
57 | child: const Icon(
58 | Icons.error_outline,
59 | color: Colors.red,
60 | ),
61 | ),
62 | );
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/lib/pages/chat/chat/widget/chat_send_progress.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:flutter/cupertino.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter_screenutil/flutter_screenutil.dart';
6 |
7 | import 'chat_item_view.dart';
8 |
9 | class ChatSendProgressView extends StatefulWidget {
10 | const ChatSendProgressView({
11 | Key? key,
12 | required this.msgId,
13 | required this.width,
14 | required this.height,
15 | required this.msgProgressControllerStream,
16 | }) : super(key: key);
17 | final String msgId;
18 | final double width;
19 | final double height;
20 | final Stream> msgProgressControllerStream;
21 |
22 | @override
23 | State createState() => _ChatSendProgressViewState();
24 | }
25 |
26 | class _ChatSendProgressViewState extends State {
27 | double _progress = 100.0;
28 | StreamSubscription? _progressSubs;
29 |
30 | @override
31 | void initState() {
32 | super.initState();
33 | _progressSubs = widget.msgProgressControllerStream
34 | .where((event) => widget.msgId == event.msgId)
35 | .listen((event) {
36 | if (!mounted) return;
37 | setState(() {
38 | _progress = event.value;
39 | });
40 | });
41 | }
42 |
43 | @override
44 | void dispose() {
45 | super.dispose();
46 | _progressSubs?.cancel();
47 | }
48 |
49 | @override
50 | Widget build(BuildContext context) {
51 | return Visibility(
52 | visible: _progress != 100.0,
53 | child: Container(
54 | height: widget.height,
55 | width: widget.width,
56 | alignment: Alignment.center,
57 | decoration: BoxDecoration(
58 | borderRadius: BorderRadius.circular(8),
59 | color: Colors.grey.withOpacity(0.2),
60 | ),
61 | child: Column(
62 | children: [
63 | CircularProgressIndicator(
64 | value: _progress,
65 | ),
66 | Text(
67 | "${_progress.toStringAsFixed(2)}%",
68 | style: TextStyle(
69 | fontSize: 10.sp,
70 | color: Colors.white,
71 | ),
72 | )
73 | ],
74 | ),
75 | ),
76 | );
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/lib/pages/chat/chat/widget/chat_text.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_screenutil/flutter_screenutil.dart';
4 |
5 | class ChatText extends StatelessWidget {
6 | const ChatText({Key? key, required this.text, required this.isFromMsg})
7 | : super(key: key);
8 | final String? text;
9 | final bool isFromMsg;
10 |
11 | Color get bubbleColor =>
12 | isFromMsg ? const Color(0xFFF7F7F7) : const Color(0xFFFCC504);
13 |
14 | @override
15 | Widget build(BuildContext context) {
16 | return Container(
17 | margin: EdgeInsets.only(bottom: 6.w),
18 | constraints: BoxConstraints(maxWidth: 0.7.sw),
19 | padding: EdgeInsets.symmetric(vertical: 10.w, horizontal: 12.w),
20 | decoration: BoxDecoration(
21 | color: bubbleColor,
22 | borderRadius: BorderRadius.circular(12.0),
23 | ),
24 | child: SelectableText(
25 | text ?? "",
26 | style: TextStyle(
27 | color: Colors.black,
28 | fontSize: 12.sp,
29 | ),
30 | ),
31 | );
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/lib/pages/chat/chat/widget/chat_voice_record_widget/chat_voice_record_bar.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_chat_craft/res/strings.dart';
3 | import 'package:flutter_screenutil/flutter_screenutil.dart';
4 |
5 | class ChatVoiceRecordBar extends StatefulWidget {
6 | const ChatVoiceRecordBar({
7 | Key? key,
8 | required this.onLongPressStart,
9 | required this.onLongPressEnd,
10 | required this.onLongPressMoveUpdate,
11 | }) : super(key: key);
12 |
13 | final Function(LongPressStartDetails details) onLongPressStart;
14 | final Function(LongPressEndDetails details) onLongPressEnd;
15 | final Function(LongPressMoveUpdateDetails details) onLongPressMoveUpdate;
16 |
17 | @override
18 | State createState() => _ChatVoiceRecordBarState();
19 | }
20 |
21 | class _ChatVoiceRecordBarState extends State {
22 | bool _pressing = false;
23 |
24 | @override
25 | Widget build(BuildContext context) {
26 | return GestureDetector(
27 | behavior: HitTestBehavior.translucent,
28 | onLongPressStart: (details) {
29 | widget.onLongPressStart(details);
30 | setState(() {
31 | _pressing = true;
32 | });
33 | },
34 | onLongPressEnd: (details) {
35 | widget.onLongPressEnd(details);
36 | setState(() {
37 | _pressing = false;
38 | });
39 | },
40 | onLongPressMoveUpdate: (details) {
41 | widget.onLongPressMoveUpdate(details);
42 | // Offset global = details.globalPosition;
43 | // Offset local = details.localPosition;
44 | // print('global:$global');
45 | // print('local:$local');
46 | },
47 | child: Container(
48 | height: 42.h,
49 | alignment: Alignment.center,
50 | decoration: BoxDecoration(
51 | color: const Color(0xFFFFFFFF).withOpacity(_pressing ? 0.3 : 1),
52 | borderRadius: BorderRadius.circular(4),
53 | // boxShadow: [
54 | // BoxShadow(
55 | // color: const Color(0xFF000000).withOpacity(0.12),
56 | // offset: const Offset(0, -1),
57 | // blurRadius: 4,
58 | // spreadRadius: 0,
59 | // ),
60 | // ],
61 | ),
62 | child: Text(
63 | _pressing ? StrRes.releaseSend : StrRes.pressSpeak,
64 | style: TextStyle(
65 | fontSize: 12.sp,
66 | color: const Color(0xFF000000),
67 | ),
68 | ),
69 | ),
70 | );
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/lib/pages/chat/chat/widget/chat_voice_record_widget/voice_record.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:path_provider/path_provider.dart';
4 | import 'package:record/record.dart';
5 |
6 | typedef RecordFc = Function(int sec, String path, int fileSize);
7 |
8 | class VoiceRecord {
9 | static const _dir = "voice";
10 | static const _ext = ".m4a";
11 | late String _path;
12 | int _long = 0;
13 | late final int _tag;
14 | final RecordFc _callback;
15 | final _audioRecorder = Record();
16 | int _fileSize = 0;
17 |
18 | VoiceRecord(this._callback) : _tag = _now();
19 |
20 | start() async {
21 | if (await _audioRecorder.hasPermission()) {
22 | var path = (await getApplicationDocumentsDirectory()).path;
23 | _path = '$path/$_dir/$_tag$_ext';
24 | File file = File(_path);
25 | if (!(await file.exists())) {
26 | await file.create(recursive: true);
27 | }
28 | _long = _now();
29 | _audioRecorder.start(path: _path);
30 | }
31 | }
32 |
33 | stop() async {
34 | if (await _audioRecorder.isRecording()) {
35 | _long = (_now() - _long) ~/ 1000;
36 | await _audioRecorder.stop();
37 | File file = File(_path);
38 | _fileSize = await file.length();
39 | _callback(_long, _path, _fileSize);
40 | }
41 | }
42 |
43 | static int _now() => DateTime.now().millisecondsSinceEpoch;
44 | }
45 |
--------------------------------------------------------------------------------
/lib/pages/chat/chat/widget/interactive_dialog/list_tools_widget.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter_chat_craft/res/images.dart';
3 | import 'package:flutter_screenutil/flutter_screenutil.dart';
4 | import 'package:flutter_svg/flutter_svg.dart';
5 |
6 | class ListToolsWidget extends StatelessWidget {
7 | const ListToolsWidget({
8 | Key? key,
9 | required this.openCamera,
10 | required this.openPhotoAlbum,
11 | }) : super(key: key);
12 |
13 | final GestureTapCallback openCamera;
14 | final GestureTapCallback openPhotoAlbum;
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 | return SizedBox(
19 | height: 52.w,
20 | child: ListView(
21 | scrollDirection: Axis.horizontal,
22 | children: [
23 | functionItem(iconUrl: ImagesRes.icPhotoAlbum, onTap: openPhotoAlbum),
24 | functionItem(iconUrl: ImagesRes.icCamera, onTap: openCamera),
25 | ],
26 | ),
27 | );
28 | }
29 |
30 | Widget functionItem(
31 | {required String iconUrl, required GestureTapCallback onTap}) {
32 | return GestureDetector(
33 | onTap: onTap,
34 | child: Container(
35 | width: 52.w,
36 | height: 52.w,
37 | margin: EdgeInsets.only(right: 8.w),
38 | padding: EdgeInsets.all(16.w),
39 | decoration: BoxDecoration(
40 | color: const Color(0xFFF7F7F7),
41 | borderRadius: BorderRadius.circular(10),
42 | ),
43 | child: SvgPicture.asset(iconUrl),
44 | ),
45 | );
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/lib/pages/chat/conversation_binding.dart:
--------------------------------------------------------------------------------
1 | import 'package:get/get.dart';
2 |
3 | class ConversationBinding extends Bindings {
4 | @override
5 | void dependencies() => Get.lazyPut(() => ConversationBinding());
6 | }
7 |
--------------------------------------------------------------------------------
/lib/pages/chat/new_chat/new_chat_binding.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_chat_craft/pages/chat/new_chat/new_chat_logic.dart';
2 | import 'package:get/get.dart';
3 |
4 | class NewChatBinding extends Bindings {
5 | @override
6 | void dependencies() => Get.lazyPut(() => NewChatLogic());
7 | }
8 |
--------------------------------------------------------------------------------
/lib/pages/chat/new_chat/new_chat_logic.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 | import 'dart:math';
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter/services.dart';
5 | import 'package:flutter_chat_craft/models/user_info.dart';
6 | import 'package:flutter_chat_craft/routes/app_navigator.dart';
7 | import 'package:get/get.dart';
8 | import 'package:scrollview_observer/scrollview_observer.dart';
9 | import '../../../common/apis.dart';
10 | import '../../../models/contact.dart';
11 | import '../../../res/strings.dart';
12 | import '../../../widget/toast_utils.dart';
13 |
14 | class NewChatLogic extends GetxController {
15 | List users = [];
16 | late SliverObserverController observerController;
17 | bool isShowListMode = true;
18 | ScrollController scrollController = ScrollController();
19 | List contactList = [];
20 | Map sliverContextMap = {};
21 | ValueNotifier cursorInfo = ValueNotifier(null);
22 | double indexBarWidth = 20;
23 | final indexBarContainerKey = GlobalKey();
24 |
25 | List get symbols => contactList.map((e) => e.section).toList();
26 |
27 | @override
28 | onInit() {
29 | super.onInit();
30 | observerController = SliverObserverController(controller: scrollController);
31 | loadFriends();
32 | }
33 |
34 | Future loadFriends() async {
35 | var data = await Apis.getFriends();
36 | if (data != false) {
37 | for (var info in data) {
38 | users.add(UserInfo.fromJson(info));
39 | }
40 | generateContactData();
41 | } else {
42 | ToastUtils.toastText(StrRes.friendListIsEmpty);
43 | }
44 | }
45 |
46 | generateContactData() {
47 | final a = const Utf8Codec().encode("A").first;
48 | final z = const Utf8Codec().encode("Z").first;
49 | int pointer = a;
50 | while (pointer >= a && pointer <= z) {
51 | final character = const Utf8Codec().decode(Uint8List.fromList([pointer]));
52 | List info = users
53 | .where((user) =>
54 | user.userName.toLowerCase().startsWith(character.toLowerCase()))
55 | .toList();
56 | if (info.isNotEmpty) {
57 | contactList.add(
58 | ContactModel(
59 | section: character,
60 | users: info,
61 | ),
62 | );
63 | }
64 | pointer++;
65 | }
66 | update(["cursor", "chatList"]);
67 | }
68 |
69 | void toMine(UserInfo userInfo) {
70 | AppNavigator.startMine(isMine: false, userInfo: userInfo);
71 | }
72 |
73 | void startNewChat() {
74 | AppNavigator.startNewChat();
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/lib/pages/chat/new_chat/widget/new_chat_cursor.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class NewChatCursor extends StatelessWidget {
4 | final double size;
5 |
6 | final String title;
7 |
8 | final double arrowSize = 30;
9 |
10 | const NewChatCursor({
11 | Key? key,
12 | required this.size,
13 | required this.title,
14 | }) : super(key: key);
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 | return Stack(
19 | clipBehavior: Clip.none,
20 | children: [
21 | _buildTitle(),
22 | Positioned(
23 | right: -arrowSize * 0.5 - 2.5,
24 | top: (size - arrowSize) * 0.5,
25 | child: _buildArrow(),
26 | ),
27 | ],
28 | );
29 | }
30 |
31 | Widget _buildArrow() {
32 | Widget resultWidget = Icon(
33 | Icons.arrow_right,
34 | color: Colors.black54,
35 | size: arrowSize,
36 | );
37 | return resultWidget;
38 | }
39 |
40 | Widget _buildTitle() {
41 | Widget resultWidget = Text(
42 | title,
43 | style: const TextStyle(color: Colors.white, fontSize: 32),
44 | );
45 | resultWidget = Container(
46 | width: size,
47 | height: size,
48 | alignment: Alignment.center,
49 | decoration: BoxDecoration(
50 | color: Colors.black54,
51 | borderRadius: BorderRadius.circular(5),
52 | ),
53 | child: resultWidget,
54 | );
55 | return resultWidget;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/lib/pages/chat/new_chat/widget/new_chat_item.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_chat_craft/widget/avatar_widget.dart';
4 | import 'package:flutter_screenutil/flutter_screenutil.dart';
5 | import '../../../../models/user_info.dart';
6 |
7 | class NewChatItem extends StatelessWidget {
8 | const NewChatItem({
9 | Key? key,
10 | required this.user,
11 | this.isShowSeparator = true,
12 | required this.onTap,
13 | }) : super(key: key);
14 |
15 | final UserInfo user;
16 |
17 | final bool isShowSeparator;
18 |
19 | final GestureTapCallback onTap;
20 |
21 | @override
22 | Widget build(BuildContext context) {
23 | return GestureDetector(
24 | onTap: onTap,
25 | child: Container(
26 | color: Colors.white,
27 | padding: EdgeInsets.symmetric(vertical: 8.w),
28 | child: Container(
29 | alignment: Alignment.centerLeft,
30 | margin: EdgeInsets.only(left: 20.0.w),
31 | child: Column(
32 | children: [
33 | Row(
34 | children: [
35 | AvatarWidget(
36 | imageUrl: user.avatar,
37 | radius: 16.w,
38 | imageSize: Size(48.w, 48.w),
39 | ),
40 | SizedBox(width: 10.w),
41 | Column(
42 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
43 | crossAxisAlignment: CrossAxisAlignment.start,
44 | children: [
45 | Text(
46 | user.userName,
47 | style: TextStyle(
48 | color: Colors.black,
49 | fontSize: 14.sp,
50 | ),
51 | ),
52 | Text(
53 | user.motto,
54 | style: TextStyle(
55 | color: Colors.grey,
56 | fontSize: 12.sp,
57 | ),
58 | ),
59 | ],
60 | ),
61 | ],
62 | ),
63 | Container(
64 | margin: EdgeInsets.only(left: 50.w, right: 24.w),
65 | height: 1,
66 | color: Colors.grey.shade100,
67 | )
68 | ],
69 | ),
70 | ),
71 | ),
72 | );
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/lib/pages/chat/rtc/video_call/video_call_binding.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_chat_craft/pages/chat/rtc/video_call/video_call_logic.dart';
2 | import 'package:get/get.dart';
3 |
4 | class VideoCallBinding extends Bindings{
5 | @override
6 | void dependencies() => Get.lazyPut(() => VideoCallLogic());
7 | }
--------------------------------------------------------------------------------
/lib/pages/chat/rtc/video_call/video_call_logic.dart:
--------------------------------------------------------------------------------
1 | import 'package:get/get.dart';
2 |
3 | enum VideoAction {
4 | cancel,
5 | connect,
6 | }
7 | enum SignalingState {
8 | connectionOpen,
9 | connectionClosed,
10 | connectionError,
11 | }
12 |
13 | enum CallState {
14 | callStateNew,
15 | callStateRinging,
16 | callStateInvite,
17 | callStateConnected,
18 | callStateBye,
19 | }
20 |
21 | enum VideoSource {
22 | camera,
23 | screen,
24 | }
25 |
26 |
27 | class VideoCallLogic extends GetxController {
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/lib/pages/chat/rtc/video_call/video_call_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class VideoCallPage extends StatefulWidget {
4 | const VideoCallPage({Key? key}) : super(key: key);
5 |
6 | @override
7 | State createState() => _VideoCallPageState();
8 | }
9 |
10 | class _VideoCallPageState extends State {
11 | @override
12 | Widget build(BuildContext context) {
13 | return Scaffold(
14 | body: Container(),
15 | );
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/lib/pages/chat/widget/appbar_title.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_chat_craft/res/images.dart';
5 | import 'package:flutter_screenutil/flutter_screenutil.dart';
6 | import 'package:flutter_svg/flutter_svg.dart';
7 | import '../../../res/strings.dart';
8 |
9 | class AppBarTitle extends StatefulWidget {
10 | const AppBarTitle({Key? key}) : super(key: key);
11 |
12 | @override
13 | State createState() => _AppBarTitleState();
14 | }
15 |
16 | class _AppBarTitleState extends State
17 | with SingleTickerProviderStateMixin {
18 | late AnimationController _controller;
19 | late Animation _offsetAnimation;
20 | late Timer _timer;
21 | bool showTextOrIcon = true;
22 |
23 | @override
24 | void initState() {
25 | super.initState();
26 | _controller = AnimationController(
27 | vsync: this,
28 | duration: const Duration(milliseconds: 1000),
29 | );
30 | _offsetAnimation = Tween(
31 | begin: const Offset(-2.0, 0.0),
32 | end: Offset.zero,
33 | ).animate(CurvedAnimation(
34 | parent: _controller,
35 | curve: Curves.fastLinearToSlowEaseIn,
36 | ));
37 | _controller.forward();
38 | Future.delayed(const Duration(seconds: 3), () {
39 | startTimer();
40 | });
41 | }
42 |
43 | void startTimer() {
44 | _timer = Timer.periodic(const Duration(seconds: 2), (Timer timer) {
45 | setState(() {
46 | if (_controller.isCompleted) {
47 | _controller
48 | .reverse()
49 | .then((value) => showTextOrIcon = !showTextOrIcon);
50 | } else {
51 | _controller.forward();
52 | }
53 | });
54 | // 在运行一段时间后暂停动画
55 | Future.delayed(const Duration(seconds: 12), () {
56 | _timer.cancel(); // 取消定时器
57 | _controller.stop(); // 停止动画
58 | });
59 | });
60 | }
61 |
62 | @override
63 | Widget build(BuildContext context) {
64 | return SlideTransition(
65 | position: _offsetAnimation,
66 | child: showTextOrIcon
67 | ? Text(
68 | StrRes.chatCraft,
69 | )
70 | : SvgPicture.asset(
71 | ImagesRes.appIcon,
72 | width: 36.w,
73 | height: 36.w,
74 | ),
75 | );
76 | }
77 |
78 | @override
79 | void dispose() {
80 | super.dispose();
81 | _controller.dispose();
82 | _timer.cancel();
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/lib/pages/chat/widget/dashed_circle_border.dart:
--------------------------------------------------------------------------------
1 | import 'dart:math';
2 |
3 | import 'package:flutter/material.dart';
4 |
5 | class DashedCircleBorder extends StatelessWidget {
6 | final Widget child;
7 | final Color color;
8 | final double strokeWidth;
9 | final double dashWidth;
10 | final double dashGap;
11 |
12 | const DashedCircleBorder({
13 | required this.child,
14 | this.color = Colors.black,
15 | this.strokeWidth = 1.0,
16 | this.dashWidth = 0.3,
17 | this.dashGap = 0.4,
18 | });
19 |
20 | @override
21 | Widget build(BuildContext context) {
22 | return CustomPaint(
23 | painter: _DashedCircleBorderPainter(
24 | color: color,
25 | strokeWidth: strokeWidth,
26 | dashWidth: dashWidth,
27 | dashGap: dashGap,
28 | ),
29 | child: child,
30 | );
31 | }
32 | }
33 |
34 | class _DashedCircleBorderPainter extends CustomPainter {
35 | final Color color;
36 | final double strokeWidth;
37 | final double dashWidth;
38 | final double dashGap;
39 |
40 | _DashedCircleBorderPainter({
41 | required this.color,
42 | required this.strokeWidth,
43 | required this.dashWidth,
44 | required this.dashGap,
45 | });
46 |
47 | @override
48 | void paint(Canvas canvas, Size size) {
49 | Offset center = Offset(size.width / 2, size.height / 2);
50 | double radius = min(size.width / 2, size.height / 2);
51 | final paint = Paint()
52 | ..color = color
53 | ..strokeWidth = strokeWidth
54 | ..style = PaintingStyle.stroke;
55 |
56 | double i = 0.00;
57 | while (i < pi * 2) {
58 | canvas.drawArc(
59 | Rect.fromCircle(center: center, radius: radius),
60 | i,
61 | dashWidth,
62 | false,
63 | paint,
64 | );
65 | i = i + dashGap;
66 | }
67 | }
68 |
69 | @override
70 | bool shouldRepaint(covariant CustomPainter oldDelegate) {
71 | return false;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/lib/pages/common/check_code/check_code_binding.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_chat_craft/pages/common/check_code/check_code_logic.dart';
2 | import 'package:get/get.dart';
3 |
4 | class CheckCodeBinding extends Bindings {
5 | @override
6 | void dependencies() => Get.lazyPut(() => CheckCodeLogic());
7 | }
8 |
--------------------------------------------------------------------------------
/lib/pages/common/check_code/check_code_logic.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_chat_craft/routes/app_routes.dart';
5 | import 'package:get/get.dart';
6 | import 'package:pin_code_fields/pin_code_fields.dart';
7 | import '../../../routes/app_navigator.dart';
8 |
9 | enum SendVerificationCodeState {
10 | notSent, //The verification code has expired.
11 | wait, //Waiting for the user to enter the verification code.
12 | }
13 |
14 | class CheckCodeLogic extends GetxController {
15 | late String name;
16 | late String email;
17 | late CheckCodeMethod checkCodeMethod;
18 | late Function() regainVerifyCode;
19 | late int countDown;
20 | late Timer countDownTimer;
21 | late StreamController errorController;
22 | late TextEditingController pinTextEditingController;
23 | late SendVerificationCodeState sendVerificationCodeState;
24 |
25 | @override
26 | void onInit() {
27 | super.onInit();
28 | name = Get.arguments["name"];
29 | email = Get.arguments["email"];
30 | regainVerifyCode = Get.arguments["regainVerifyCode"];
31 | checkCodeMethod = Get.arguments["checkCodeMethod"];
32 | countDown = 60;
33 | countDownFunction();
34 | errorController = StreamController();
35 | pinTextEditingController = TextEditingController();
36 | }
37 |
38 | @override
39 | void onClose() {
40 | super.onClose();
41 | errorController.close();
42 | countDownTimer.cancel();
43 | }
44 |
45 | ///60-second countdown
46 | void countDownFunction() {
47 | sendVerificationCodeState = SendVerificationCodeState.wait;
48 | errorController = StreamController();
49 | pinTextEditingController = TextEditingController();
50 | countDownTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
51 | if (countDown == 0) {
52 | countDownTimer.cancel();
53 | countDown = 60;
54 | sendVerificationCodeState = SendVerificationCodeState.notSent;
55 | } else {
56 | sendVerificationCodeState = SendVerificationCodeState.wait;
57 | countDown--;
58 | }
59 | update();
60 | });
61 | }
62 |
63 | ///Verify Captcha
64 | void checkVerifyCode(value) async {
65 | //If the verification is successful, execute the passed-in function.
66 | bool isAgree = await checkCodeMethod(pinTextEditingController.text);
67 | //If the captcha is incorrect, enable the error animation and clear the captcha input field.
68 | if (isAgree) {
69 | countDownTimer.cancel();
70 | Get.toNamed(AppRoutes.invite, arguments: {'email': email, 'name': name});
71 | } else {
72 | shake();
73 | }
74 | }
75 |
76 | void shake() {
77 | errorController.add(ErrorAnimationType.shake);
78 | Future.delayed(const Duration(milliseconds: 1500), () {
79 | pinTextEditingController.clear();
80 | });
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/lib/pages/home/home_binding.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_chat_craft/pages/chat/conversation_logic.dart';
2 | import 'package:flutter_chat_craft/pages/home/home_logic.dart';
3 | import 'package:get/get.dart';
4 |
5 | class HomeBinding extends Bindings {
6 | @override
7 | void dependencies() {
8 | Get.lazyPut(() => HomeLogic());
9 | Get.lazyPut(() => ConversationLogic());
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/lib/pages/home/home_logic.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_chat_craft/routes/app_navigator.dart';
3 | import 'package:get/get.dart';
4 | import '../../res/images.dart';
5 | import '../../res/strings.dart';
6 | import '../../widget/toast_utils.dart';
7 | import '../chat/conversation_view.dart';
8 | import 'home_view.dart';
9 | import 'widget/home_dialog.dart';
10 |
11 | class HomeLogic extends GetxController {
12 | PageController pageController = PageController();
13 | int selectIndex = 0;
14 | bool isShowDialog = false;
15 | List pages = [
16 | ConversationPage(),
17 | ];
18 |
19 | List bottomMenu = [
20 | BottomMenu(0, ImagesRes.icHome),
21 | BottomMenu(1, ImagesRes.icMe),
22 | ];
23 |
24 | void showHomeDialog(BuildContext context) {
25 | isShowDialog = true;
26 | update(["homeDialog"]);
27 | Get.dialog(
28 | barrierColor: const Color(0x573D3D3D),
29 | HomeDialog(
30 | toAddNewChat: () => toNewChat(),
31 | toAddGroup: () {
32 | ToastUtils.toastText(StrRes.notImplemented);
33 | },
34 | toAddFriend: () => toAddFriendPage(),
35 | ),
36 | ).then((value) {
37 | isShowDialog = false;
38 | update(["homeDialog"]);
39 | });
40 | }
41 |
42 | void changeSelectIndex(int value) {
43 | selectIndex = value.toInt();
44 | update(["bottomMenu"]);
45 | }
46 |
47 | void jumpToPage(int index) {
48 | pageController.jumpToPage(index);
49 | selectIndex = index;
50 | update(["bottomMenu"]);
51 | }
52 |
53 | void toAddFriendPage() {
54 | AppNavigator.startAddFriend();
55 | }
56 |
57 | void toNewChat() {
58 | AppNavigator.startNewChat();
59 | }
60 |
61 | void startMine() {
62 | AppNavigator.startMine(isMine: true);
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/lib/pages/login/invite/invite_binding.dart:
--------------------------------------------------------------------------------
1 | import 'package:get/get.dart';
2 |
3 | import 'invite_logic.dart';
4 |
5 | class InviteBinding extends Bindings {
6 | @override
7 | void dependencies() {
8 | Get.lazyPut(() => InviteLogic());
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/lib/pages/login/invite/invite_state.dart:
--------------------------------------------------------------------------------
1 | import 'package:get/get.dart';
2 |
3 | class InviteState {
4 | late String name;
5 | late String email;
6 |
7 | String? avatar;
8 | String? motto;
9 | String? phone;
10 |
11 | InviteState() {
12 | name = Get.arguments['name'];
13 | email = Get.arguments['email'];
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/lib/pages/login/login_binding.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_chat_craft/pages/login/login_logic.dart';
2 | import 'package:get/get.dart';
3 |
4 | class LoginBinding extends Bindings {
5 | @override
6 | void dependencies() => Get.lazyPut(()=>LoginLogic());
7 | }
8 |
--------------------------------------------------------------------------------
/lib/pages/login/login_email/login_email_binding.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_chat_craft/pages/login/login_email/login_email_logic.dart';
2 | import 'package:get/get.dart';
3 |
4 | class LoginEmailBinding extends Bindings {
5 | @override
6 | void dependencies() => Get.lazyPut(() => LoginEmailLogic());
7 | }
8 |
--------------------------------------------------------------------------------
/lib/pages/login/login_email/login_email_view.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_chat_craft/pages/login/login_email/login_email_logic.dart';
3 | import 'package:flutter_screenutil/flutter_screenutil.dart';
4 | import 'package:get/get.dart';
5 |
6 | import '../../../res/strings.dart';
7 | import '../../../utils/touch_close_keyboard.dart';
8 | import '../widget/login_universal_widget.dart';
9 |
10 | class LoginEmailPage extends StatefulWidget {
11 | const LoginEmailPage({Key? key}) : super(key: key);
12 |
13 | @override
14 | State createState() => _LoginEmailPageState();
15 | }
16 |
17 | class _LoginEmailPageState extends State {
18 | final emailLogic = Get.find();
19 |
20 | @override
21 | Widget build(BuildContext context) {
22 | return TouchCloseSoftKeyboard(
23 | child: Scaffold(
24 | backgroundColor: Colors.white,
25 | appBar: loginAppBar(),
26 | body: Padding(
27 | padding: EdgeInsets.symmetric(horizontal: 18.w, vertical: 12.w),
28 | child: SingleChildScrollView(
29 | child: Column(
30 | crossAxisAlignment: CrossAxisAlignment.start,
31 | children: [
32 | titleText(
33 | title: StrRes.loginWithEmail,
34 | content: StrRes.loginContent,
35 | ),
36 | SizedBox(
37 | height: 10.w,
38 | ),
39 | titleText(
40 | title: StrRes.email,
41 | ),
42 | SizedBox(
43 | height: 14.w,
44 | ),
45 | GetBuilder(
46 | id: "emailInput",
47 | builder: (logic) => loginInput(
48 | controller: logic.emailController,
49 | focusNode: logic.emailFocusNode,
50 | hintText: StrRes.emailInputHintText,
51 | ),
52 | ),
53 | SizedBox(
54 | height: 22.w,
55 | ),
56 | Obx(
57 | () => loginButton(
58 | text: StrRes.sendCode,
59 | onTap: emailLogic.sendCode,
60 | isClick: emailLogic.isClick.value),
61 | ),
62 | ],
63 | ),
64 | ),
65 | ),
66 | ));
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/lib/pages/login/register/register_binding.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_chat_craft/pages/login/register/register_logic.dart';
2 | import 'package:get/get.dart';
3 |
4 | class RegisterBinding extends Bindings {
5 | @override
6 | void dependencies() => Get.lazyPut(() => RegisterLogic());
7 | }
8 |
--------------------------------------------------------------------------------
/lib/pages/mine/mine_binding.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_chat_craft/pages/mine/mine_logic.dart';
2 | import 'package:get/get.dart';
3 |
4 | class MineBinding extends Bindings {
5 | @override
6 | void dependencies() {
7 | Get.lazyPut(() => MineLogic());
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/lib/pages/mine/mine_logic.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_chat_craft/common/global_data.dart';
2 | import 'package:flutter_chat_craft/models/user_story.dart';
3 | import 'package:flutter_chat_craft/routes/app_routes.dart';
4 | import 'package:flutter_chat_craft/utils/sp/data_persistence.dart';
5 | import 'package:flutter_chat_craft/widget/toast_utils.dart';
6 | import 'package:get/get.dart';
7 | import '../../common/apis.dart';
8 | import '../../models/user_info.dart';
9 | import '../../routes/app_navigator.dart';
10 |
11 | class MineLogic extends GetxController {
12 | late UserInfo userInfo;
13 | RxList userStories = [].obs;
14 | RxInt storyLike = 0.obs;
15 |
16 | @override
17 | void onReady() {
18 | super.onReady();
19 | getUserShowStoryList();
20 | }
21 |
22 | void setUserInfo(UserInfo info) {
23 | userInfo = info;
24 | }
25 |
26 | bool get isSelf => userInfo.userID == GlobalData.userInfo.userID;
27 |
28 | void toChat() {
29 | AppNavigator.startChat(userInfo: userInfo);
30 | }
31 |
32 | Future getUserShowStoryList() async {
33 | var data = await Apis.getUserShowStoryList(
34 | userId: userInfo.userID,
35 | );
36 | if (data == false) {
37 | return false;
38 | } else {
39 | print(data.toString());
40 | var storyData = data["StoryList"];
41 | var count = data["Count"];
42 | storyLike.value = count;
43 | for (var story in storyData) {
44 | UserStory info = UserStory.fromJson(story["story"]);
45 |
46 | List storyLikes =
47 | story["story_likes"] != null ? (story["story_likes"] as List).map((likeJson) => UserStoryLike.fromJson(likeJson)).toList() : [];
48 |
49 | List storyComments = story["story_comments"] != null
50 | ? (story["story_comments"] as List).map((commentJson) => UserStoryComment.fromJson(commentJson)).toList()
51 | : [];
52 | info.storyLikes = storyLikes;
53 | info.storyComments = storyComments;
54 | userStories.add(info);
55 | print(info.toString());
56 | }
57 | }
58 | return false;
59 | }
60 |
61 | void startMineStoryDetails(UserStory story) {
62 | AppNavigator.startMineStoryDetails(userInfo: userInfo, userStory: story);
63 | }
64 |
65 | void logout() {
66 | DataPersistence.removeToken();
67 | DataPersistence.removeUser();
68 | AppNavigator.logout();
69 | }
70 |
71 | void toProfile() {
72 | Get.toNamed(AppRoutes.profile, arguments: {'userInfo': userInfo});
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/lib/pages/mine/mine_story_details/mine_story_details_binding.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_chat_craft/pages/mine/mine_story_details/mine_story_details_logic.dart';
2 | import 'package:get/get.dart';
3 |
4 | class MineStoryDetailsBindings extends Bindings {
5 | @override
6 | void dependencies() => Get.lazyPut(() => MineStoryDetailsLogic());
7 | }
8 |
--------------------------------------------------------------------------------
/lib/pages/mine/mine_story_details/widget/mine_story_interactive_dialog/list_reply_input.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_chat_craft/res/strings.dart';
4 | import 'package:flutter_screenutil/flutter_screenutil.dart';
5 |
6 | class ListReplyInputWidget extends StatelessWidget {
7 | const ListReplyInputWidget(
8 | {Key? key, required this.controller, required this.onSubmitted})
9 | : super(key: key);
10 |
11 | final TextEditingController controller;
12 | final Function(String value) onSubmitted;
13 |
14 | @override
15 | Widget build(BuildContext context) {
16 | return Container(
17 | padding: const EdgeInsets.symmetric(horizontal: 12),
18 | decoration: BoxDecoration(
19 | borderRadius: BorderRadius.circular(14),
20 | color: const Color(0xFFF7F7F7),
21 | ),
22 | child: TextField(
23 | controller: controller,
24 | maxLength: 120,
25 | decoration: InputDecoration(
26 | border: InputBorder.none,
27 | counterText: "",
28 | hintText: StrRes.storyCommentHintText,
29 | hintStyle: TextStyle(
30 | fontSize: 14.sp,
31 | ),
32 | ),
33 | style: TextStyle(
34 | fontSize: 14.sp,
35 | ),
36 | onSubmitted: onSubmitted,
37 | ),
38 | );
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/lib/pages/mine/profile/profile_binding.dart:
--------------------------------------------------------------------------------
1 | import 'package:get/get.dart';
2 |
3 | import 'profile_logic.dart';
4 |
5 | class ProfileBinding extends Bindings {
6 | @override
7 | void dependencies() {
8 | Get.lazyPut(() => ProfileLogic());
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/lib/pages/mine/profile/profile_state.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_chat_craft/models/user_info.dart';
2 | import 'package:get/get.dart';
3 |
4 | class ProfileState {
5 | late UserInfo userInfo;
6 |
7 | late Rx hasAnyChange;
8 |
9 | ProfileState() {
10 | userInfo = Get.arguments['userInfo'];
11 | hasAnyChange = false.obs;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/lib/pages/splash/splash_binding.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_chat_craft/pages/splash/splash_logic.dart';
2 | import 'package:get/get.dart';
3 |
4 | class SplashBinding extends Bindings {
5 | @override
6 | void dependencies() => Get.lazyPut(()=>SplashLogic());
7 | }
8 |
--------------------------------------------------------------------------------
/lib/pages/splash/splash_logic.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_chat_craft/common/global_data.dart';
3 | import 'package:flutter_chat_craft/models/user_info.dart';
4 | import 'package:flutter_chat_craft/res/strings.dart';
5 | import 'package:flutter_chat_craft/routes/app_navigator.dart';
6 | import 'package:flutter_chat_craft/utils/file_util.dart';
7 | import 'package:flutter_chat_craft/utils/sp/data_persistence.dart';
8 | import 'package:get/get.dart';
9 |
10 | import '../../models/file_path.dart';
11 |
12 | class SplashLogic extends GetxController {
13 | void enterChatCraft() async {
14 | String emojiCachePath =
15 | await FileUtil.getCachePath(FilePath.emojiPath);
16 | FileUtil.isDirectoryCompleteBySize(
17 | emojiCachePath,
18 | 36.65,
19 | ).then((value) {
20 | if (value) {
21 | String token = DataPersistence.getToken();
22 | if (token == '') {
23 | AppNavigator.startLogin();
24 | } else {
25 | UserInfo user = DataPersistence.getUserInfo();
26 | GlobalData.userInfo = user;
27 | GlobalData.token = token;
28 | AppNavigator.startHome();
29 | }
30 | } else {
31 | //System file loss.
32 | Get.dialog(
33 | AlertDialog(
34 | title: Text(StrRes.loseEmojiFile),
35 | actions: [
36 | ElevatedButton(
37 | onPressed: () {
38 | Get.back();
39 | },
40 | child: Text(StrRes.cancel),
41 | ),
42 | ElevatedButton(
43 | onPressed: () {
44 | FileUtil.downloadEmoji().then((value) async {
45 | if (value) {
46 | Get.back();
47 | String emojiCachePath = await FileUtil.getCachePath(
48 | FilePath.emojiZipDownloadPath);
49 | FileUtil.extractZipFile(emojiCachePath);
50 | } else {}
51 | });
52 | },
53 | child: Text(StrRes.confirm),
54 | ),
55 | ],
56 | ),
57 | );
58 | }
59 | });
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/lib/res/styles.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class PageStyle {
4 | static const Color mainColor = Color(0xFFFDCE85);
5 | static const Color chatColor = Color(0xFFFCC504);
6 |
7 | static const Color tipColor = Color(0xFF848484);
8 | static const Color contentColor = Color(0xFFA4A4A4);
9 | static const Color btnBgColor = Color(0xFFF6F6F6);
10 | }
11 |
--------------------------------------------------------------------------------
/lib/routes/app_routes.dart:
--------------------------------------------------------------------------------
1 | abstract class AppRoutes {
2 | static const splash = '/splash';
3 | static const login = '/login';
4 | static const loginEmail = '/loginEmail';
5 | static const register = '/register';
6 | static const checkCode = '/checkCode';
7 | static const home = '/home';
8 | static const conversation = '/conversation';
9 | static const addFriend = '/addFriend';
10 | static const chat = '/chat';
11 | static const mine = '/mine';
12 | static const mineStoryDetails = '/mineStoryDetails';
13 | static const newChat = '/newChat';
14 | static const profile = '/profile';
15 | static const invite = '/invite';
16 | }
17 |
--------------------------------------------------------------------------------
/lib/utils/app_reg_exp.dart:
--------------------------------------------------------------------------------
1 | class AppRegExp {
2 | ///用户名:只能为数字、英文字母,且不超过15位,不能出现空格、下划线。
3 | static RegExp userNameRegExp = RegExp(r'^[a-zA-Z0-9]{1,15}$');
4 |
5 | ///用户账户密码:只能为数字、英文字母和特殊字符,且长度为6-18位,不允许出现空格
6 | static RegExp userPwdRegExp = RegExp(r'^[\w!@#\$&*~]{6,18}$');
7 |
8 | static RegExp emailRegExp =
9 | RegExp("^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*\$");
10 | }
11 |
--------------------------------------------------------------------------------
/lib/utils/app_utils.dart:
--------------------------------------------------------------------------------
1 | import 'package:encrypt/encrypt.dart';
2 | import 'package:flutter/services.dart';
3 |
4 | class AppUtils {
5 | // Rsa Encryption
6 | static Future encodeString(String data) async {
7 | final publicPem = await rootBundle.loadString('assets/key/public.pem');
8 | dynamic publicKey = RSAKeyParser().parse(publicPem);
9 | final encrypter = Encrypter(RSA(publicKey: publicKey));
10 |
11 | return encrypter.encrypt(data).base64.replaceAll(RegExp(r''), '');
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/lib/utils/db/base_db_provider.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_chat_craft/utils/db/sql_manager.dart';
3 | import 'package:sqflite/sqflite.dart';
4 |
5 | abstract class BaseDbProvider {
6 | bool isTableExits = false;
7 |
8 | createTableString();
9 |
10 | tableName();
11 |
12 | tableBaseString(String sql) {
13 | return sql;
14 | }
15 |
16 | Future getDataBase() async {
17 | return await open();
18 | }
19 |
20 | /// super 函数对父类进行初始化
21 | @mustCallSuper
22 | prepare(name, String createSql) async {
23 | isTableExits = await SQLManager.isTableExits(name);
24 | if (!isTableExits) {
25 | Database db = await SQLManager.getCurrentDatabase();
26 | return await db.execute(createSql);
27 | }
28 | }
29 |
30 | /// super 打开数据表
31 | @mustCallSuper
32 | open() async {
33 | if (!isTableExits) {
34 | await prepare(tableName(), createTableString());
35 | }
36 | return await SQLManager.getCurrentDatabase();
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/lib/utils/db/sql_manager.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:flutter_chat_craft/utils/db/conversation_db_provider.dart';
4 | import "package:sqflite/sqflite.dart";
5 | import 'package:path/path.dart';
6 |
7 | class SQLManager {
8 | static const _version = 1;
9 | static const _name = "chatChart.db";
10 |
11 | static Database? _database;
12 |
13 | static var _isInit;
14 |
15 | static init() async {
16 | close();
17 | _isInit = true;
18 | var databasePath = await getDatabasesPath();
19 | String path = join(databasePath, _name);
20 | _database = await openDatabase(path,
21 | version: _version, onCreate: (Database db, int version) async {});
22 |
23 | //Initialize local Conversation table.
24 | ConversationDbProvider conversation = ConversationDbProvider();
25 | var exist = await isTableExits(conversation.tableName());
26 | if (!exist) {
27 | await _database?.execute(conversation.createTableString());
28 | }
29 | }
30 |
31 | /// Get the current database object
32 | static Future getCurrentDatabase() async {
33 | if (_isInit == false) {
34 | await init();
35 | }
36 | return _database!;
37 | }
38 |
39 | /// Check if the table exists
40 | static isTableExits(String tableName) async {
41 | await getCurrentDatabase();
42 | var res = await _database?.rawQuery(
43 | "select * from Sqlite_master where type = 'table' and name = '$tableName'");
44 | return res != null && res.isNotEmpty;
45 | }
46 |
47 | /// Clean up the database.
48 | static cleanup() async {
49 | var databasesPath = await getDatabasesPath();
50 |
51 | String path = join(databasesPath, _name);
52 | File file = File(path);
53 | if (await file.exists()) {
54 | file.deleteSync();
55 | }
56 | }
57 |
58 | /// close database
59 | static close() {
60 | _database?.close();
61 | _database = null;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/lib/utils/device_utils.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | class DeviceUtils{
4 | static String get label {
5 | return 'Flutter ' +
6 | Platform.operatingSystem +
7 | '(' +
8 | Platform.localHostname +
9 | ")";
10 | }
11 |
12 | static String get userAgent {
13 | return 'flutter-webrtc/' + Platform.operatingSystem + '-plugin 0.0.1';
14 | }
15 | }
--------------------------------------------------------------------------------
/lib/utils/image_util.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/widgets.dart';
3 |
4 | class ImageUtil {
5 | static ImageProvider getAssetImage(String name, {ImageFormat format = ImageFormat.png}) {
6 | return AssetImage(getImgPath(name, format: format));
7 | }
8 |
9 | static String getImgPath(String name, {ImageFormat format = ImageFormat.png}) {
10 | return 'assets/images/$name.${format.value}';
11 | }
12 | }
13 |
14 | enum ImageFormat { png, jpg, gif, webp, svg }
15 |
16 | extension ImageFormatExtension on ImageFormat {
17 | String get value => ['png', 'jpg', 'gif', 'webp', 'svg'][index];
18 | }
19 |
--------------------------------------------------------------------------------
/lib/utils/object_util.dart:
--------------------------------------------------------------------------------
1 | /// Object Util.
2 | class ObjectUtil {
3 | /// Returns true if the string is null or 0-length.
4 | static bool isEmptyString(String? str) {
5 | return str == null || str.isEmpty;
6 | }
7 |
8 | /// Returns true if the list is null or 0-length.
9 | static bool isEmptyList(Iterable? list) {
10 | return list == null || list.isEmpty;
11 | }
12 |
13 | /// Returns true if there is no key/value pair in the map.
14 | static bool isEmptyMap(Map? map) {
15 | return map == null || map.isEmpty;
16 | }
17 |
18 | /// Returns true String or List or Map is empty.
19 | static bool isEmpty(Object? object) {
20 | if (object == null) return true;
21 | if (object is String && object.isEmpty) {
22 | return true;
23 | } else if (object is Iterable && object.isEmpty) {
24 | return true;
25 | } else if (object is Map && object.isEmpty) {
26 | return true;
27 | }
28 | return false;
29 | }
30 |
31 | /// Returns true String or List or Map is not empty.
32 | static bool isNotEmpty(Object? object) {
33 | return !isEmpty(object);
34 | }
35 |
36 | /// Returns true Two List Is Equal.
37 | static bool twoListIsEqual(List? listA, List? listB) {
38 | if (listA == listB) return true;
39 | if (listA == null || listB == null) return false;
40 | int length = listA.length;
41 | if (length != listB.length) return false;
42 | for (int i = 0; i < length; i++) {
43 | if (!listA.contains(listB[i])) {
44 | return false;
45 | }
46 | }
47 | return true;
48 | }
49 |
50 | /// get length.
51 | static int getLength(Object? value) {
52 | if (value == null) return 0;
53 | if (value is String) {
54 | return value.length;
55 | } else if (value is Iterable) {
56 | return value.length;
57 | } else if (value is Map) {
58 | return value.length;
59 | } else {
60 | return 0;
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/lib/utils/regex_util.dart:
--------------------------------------------------------------------------------
1 | class RegexUtil {
2 | /// Regex of mobile.
3 | static const String regexMobile = '^[1]\\d{10}\$';
4 |
5 | /// Regex of email.
6 | static const String regexEmail =
7 | '^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*\$';
8 |
9 | /// Regex of date which pattern is 'yyyy-MM-dd'.
10 | static const String regexDate =
11 | '^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)\$';
12 |
13 | /// Regex of ip address.
14 | static const String regexIp =
15 | '((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)';
16 |
17 | /// must contain letters and numbers, 6 ~ 18.
18 | /// 必须包含字母和数字, 6~18.
19 | static const String regexUsername =
20 | '^(?![0-9]+\$)(?![a-zA-Z]+\$)[0-9A-Za-z]{6,18}\$';
21 |
22 | /// must contain letters and numbers, can contain special characters 6 ~ 18.
23 | /// 必须包含字母和数字,可包含特殊字符 6~18.
24 | static const String regexUsername2 =
25 | '^(?![0-9]+\$)(?![a-zA-Z]+\$)[0-9A-Za-z\\W]{6,18}\$';
26 |
27 | /// must contain letters and numbers and special characters, 6 ~ 18.
28 | /// 必须包含字母和数字和殊字符, 6~18.
29 | static const String regexUsername3 =
30 | '^(?![0-9]+\$)(?![a-zA-Z]+\$)(?![0-9a-zA-Z]+\$)(?![0-9\\W]+\$)(?![a-zA-Z\\W]+\$)[0-9A-Za-z\\W]{6,18}\$';
31 |
32 |
33 | ///Return whether input matches regex of simple mobile.
34 | static bool isMobile(String input) {
35 | return matches(regexMobile, input);
36 | }
37 |
38 | /// Return whether input matches regex of email.
39 | static bool isEmail(String input) {
40 | return matches(regexEmail, input);
41 | }
42 |
43 | /// Return whether input matches regex of date which pattern is 'yyyy-MM-dd'.
44 | static bool isDate(String input) {
45 | return matches(regexDate, input);
46 | }
47 |
48 | /// Return whether input matches regex of ip address.
49 | static bool isIP(String input) {
50 | return matches(regexIp, input);
51 | }
52 |
53 | /// Return whether input matches regex of username.
54 | static bool isUserName(String input, {String regex = regexUsername}) {
55 | return matches(regex, input);
56 | }
57 |
58 | static bool matches(String regex, String input) {
59 | if (input.isEmpty) return false;
60 | return RegExp(regex).hasMatch(input);
61 | }
62 | }
--------------------------------------------------------------------------------
/lib/utils/sp/data_persistence.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_chat_craft/models/user_info.dart';
2 |
3 | import 'sp_util.dart';
4 |
5 | class DataPersistence {
6 | static const _language = "language";
7 | static const _token = "token";
8 | static const _user = "user";
9 |
10 | static Future? putLanguage(int index) {
11 | return SpUtil.putInt(_language, index);
12 | }
13 |
14 | static int? getLanguage() {
15 | return SpUtil.getInt(_language);
16 | }
17 |
18 | static Future? putToken(String token) {
19 | return SpUtil.putString(_token, token);
20 | }
21 |
22 | static String getToken() {
23 | return SpUtil.getString(_token) ?? "";
24 | }
25 |
26 | static Future? removeToken() {
27 | return SpUtil.remove(_token);
28 | }
29 |
30 | static Future? putUserInfo(UserInfo user) {
31 | return SpUtil.putObject(_user, user);
32 | }
33 |
34 | static UserInfo getUserInfo() {
35 | return UserInfo.fromJson(SpUtil.getObject(_user)!.cast());
36 | }
37 |
38 | static Future? removeUser() {
39 | return SpUtil.remove(_user);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/lib/utils/touch_close_keyboard.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | /// Touch to close the keyboard.
4 | class TouchCloseSoftKeyboard extends StatelessWidget {
5 | final Widget child;
6 | final Function? onTouch;
7 |
8 | const TouchCloseSoftKeyboard({
9 | Key? key,
10 | required this.child,
11 | this.onTouch,
12 | }) : super(key: key);
13 |
14 | @override
15 | Widget build(BuildContext context) {
16 | return GestureDetector(
17 | behavior: HitTestBehavior.translucent,
18 | onTap: () {
19 | FocusScope.of(context).requestFocus(FocusNode());
20 | onTouch?.call();
21 | },
22 | child: child,
23 | );
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/lib/widget/app_view.dart:
--------------------------------------------------------------------------------
1 | import 'package:bot_toast/bot_toast.dart';
2 | import 'package:flutter/cupertino.dart';
3 | import 'package:flutter_screenutil/flutter_screenutil.dart';
4 | import 'package:get/get.dart';
5 |
6 | import '../common/config.dart';
7 | import '../core/app_controller.dart';
8 |
9 | class AppView extends StatelessWidget {
10 | const AppView({Key? key, required this.builder}) : super(key: key);
11 | final Widget Function(
12 | Locale? locale, Widget Function(BuildContext context, Widget? child))
13 | builder;
14 |
15 | @override
16 | Widget build(BuildContext context) {
17 | return GetBuilder(
18 | init: AppController(),
19 | builder: (controller) => ScreenUtilInit(
20 | designSize: const Size(Config.UI_W, Config.UI_H),
21 | minTextAdapt: true,
22 | splitScreenMode: true,
23 | builder: (_, __) => builder(controller.getLocale(), BotToastInit()),
24 | ),
25 | );
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/lib/widget/barrage/barrage_config.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import 'barrage_model.dart';
4 |
5 | class BarrageConfig {
6 | // 帧率
7 | static int frameRate = 60;
8 |
9 | // 单位帧率所需要的时间
10 | static int unitTimer = 1000 ~/ BarrageConfig.frameRate;
11 |
12 | static int baseRunDistance = 1;
13 |
14 | static int everyFrameRateRunDistanceScale = 150;
15 |
16 | static bool pause = false;
17 |
18 | //显示区域
19 | static Size areaSize = const Size(0, 0);
20 |
21 | //弹幕移动倍速,默认为1
22 | static double barrageRate = 1.0;
23 |
24 | //弹幕单击事件
25 | static late Function(BarrageModel) bulletTapCallBack;
26 | }
27 |
--------------------------------------------------------------------------------
/lib/widget/barrage/barrage_model.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../../models/user_story.dart';
4 | import 'barrage_config.dart';
5 | class BarrageModel {
6 | UniqueKey id;
7 | UserStoryComment comment;
8 | double offsetY;
9 | double runDistance = 0;
10 | double everyFrameRunDistance;
11 | Size barrageSize;
12 |
13 | BarrageModel({
14 | required this.id,
15 | required this.comment,
16 | required this.offsetY,
17 | required this.everyFrameRunDistance,
18 | required this.runDistance,
19 | required this.barrageSize,
20 | required int offsetMS,
21 | }) {
22 | runDistance = (offsetMS / BarrageConfig.unitTimer) * everyFrameRunDistance;
23 | }
24 |
25 | /// 弹幕的x轴位置
26 | double get offsetX => runDistance - barrageSize.width;
27 |
28 | /// 弹幕最大可跑距离 弹幕宽度+墙宽度
29 | double get maxRunDistance => barrageSize.width + BarrageConfig.areaSize.width;
30 |
31 | /// 弹幕整体脱离右边墙壁
32 | bool get allOutRight => runDistance > barrageSize.width;
33 |
34 | /// 弹幕整体离开屏幕
35 | bool get allOutLeave => runDistance > maxRunDistance;
36 |
37 | /// 剩余离开的距离
38 | double get remainderDistance => needRunDistance - runDistance;
39 |
40 | /// 需要走的距离
41 | double get needRunDistance =>
42 | BarrageConfig.areaSize.width + barrageSize.width;
43 |
44 | /// 离开屏幕剩余需要的时间
45 | double get leaveScreenRemainderTime =>
46 | remainderDistance / everyFrameRunDistance;
47 |
48 | /// 弹幕执行下一帧
49 | void runNextFrame() {
50 | runDistance += everyFrameRunDistance * BarrageConfig.barrageRate;
51 | }
52 | }
53 |
54 | class BarrageManager {
55 | Map _barrages = {};
56 |
57 | List get barrages => _barrages.values.toList();
58 |
59 | List get barrageKeys => _barrages.keys.toList();
60 |
61 | Map get barragesMap => _barrages;
62 |
63 | /// 记录弹幕到map中
64 | recordBarrage(BarrageModel barrage) {
65 | _barrages[barrage.id] = barrage;
66 | }
67 |
68 | void removeBarrageByKey(UniqueKey id) => _barrages.remove(id);
69 |
70 | void removeAllBarrage() {
71 | _barrages = {};
72 | }
73 |
74 | BarrageModel initBarrage({
75 | required UserStoryComment comment,
76 | required double offsetY,
77 | required double everyFrameRunDistance,
78 | required double runDistance,
79 | required Size barrageSize,
80 | required int offsetMS,
81 | }) {
82 | UniqueKey barrageId = UniqueKey();
83 | BarrageModel barrage = BarrageModel(
84 | id: barrageId,
85 | comment: comment,
86 | offsetY: offsetY,
87 | everyFrameRunDistance: everyFrameRunDistance,
88 | runDistance: runDistance,
89 | barrageSize: barrageSize,
90 | offsetMS: offsetMS,
91 | );
92 | recordBarrage(barrage);
93 | return barrage;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/lib/widget/barrage/barrage_track.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import 'barrage_config.dart';
4 | import 'barrage_model.dart';
5 | import 'barrage_utils.dart';
6 |
7 | class BarrageTrack {
8 | UniqueKey id = UniqueKey();
9 | UniqueKey? lastBulletId;
10 | double offsetTop;
11 | double trackHeight;
12 |
13 | BarrageTrack(
14 | this.offsetTop,
15 | this.trackHeight,
16 | );
17 |
18 | void unloadLastBulletId() {
19 | lastBulletId = null;
20 | }
21 |
22 | @override
23 | String toString() {
24 | return 'BarrageTrack(id: $id, lastBulletId: $lastBulletId, offsetTop: $offsetTop, trackHeight: $trackHeight)';
25 | }
26 | }
27 |
28 | class BarrageTrackManager {
29 | List tracks = [];
30 |
31 | double get allTrackHeight {
32 | if (tracks.isEmpty) return 0;
33 | return tracks.last.offsetTop + tracks.last.trackHeight;
34 | }
35 |
36 | // 剩余可用高度
37 | double get remainderHeight => BarrageConfig.areaSize.height - allTrackHeight;
38 |
39 | // 算轨道相对区域是否溢出
40 | bool get isTrackOverflowArea =>
41 | allTrackHeight > BarrageConfig.areaSize.height;
42 |
43 | BarrageTrack buildTrack(double trackHeight) {
44 | assert(trackHeight > 0);
45 | BarrageTrack track = BarrageTrack(allTrackHeight, trackHeight);
46 | print(track.toString());
47 | tracks.add(track);
48 | return track;
49 | }
50 |
51 | // 补足屏幕内轨道
52 | void buildTrackFullScreen() {
53 | //预估的轨道高度
54 | Size singleTextSize = BarrageUtils.getDanmakuBulletSizeByText('s');
55 | while (allTrackHeight <
56 | (BarrageConfig.areaSize.height - singleTextSize.height)) {
57 | if (areaAllowBuildNewTrack(singleTextSize.height)) {
58 | buildTrack(singleTextSize.height);
59 | }
60 | }
61 | }
62 |
63 | // 是否允许建立新轨道
64 | bool areaAllowBuildNewTrack(double needBuildTrackHeight) {
65 | assert(needBuildTrackHeight > 0);
66 | if (tracks.isEmpty) return true;
67 | return remainderHeight >= needBuildTrackHeight;
68 | }
69 |
70 | /// 删除轨道上绑定的弹幕ID
71 | void removeTrackBindIdByBulletModel(BarrageModel barrageModel) {
72 | tracks.firstWhere((element) => element.lastBulletId == barrageModel.id);
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/lib/widget/dashed_circle_border.dart:
--------------------------------------------------------------------------------
1 | import 'dart:math';
2 |
3 | import 'package:flutter/material.dart';
4 |
5 | class DashedCircleBorder extends StatelessWidget {
6 | final Widget child;
7 | final Color color;
8 | final double strokeWidth;
9 | final double dashWidth;
10 | final double dashGap;
11 |
12 | const DashedCircleBorder({
13 | required this.child,
14 | this.color = Colors.black,
15 | this.strokeWidth = 1.0,
16 | this.dashWidth = 0.3,
17 | this.dashGap = 0.4,
18 | });
19 |
20 | @override
21 | Widget build(BuildContext context) {
22 | return CustomPaint(
23 | painter: _DashedCircleBorderPainter(
24 | color: color,
25 | strokeWidth: strokeWidth,
26 | dashWidth: dashWidth,
27 | dashGap: dashGap,
28 | ),
29 | child: child,
30 | );
31 | }
32 | }
33 |
34 | class _DashedCircleBorderPainter extends CustomPainter {
35 | final Color color;
36 | final double strokeWidth;
37 | final double dashWidth;
38 | final double dashGap;
39 |
40 | _DashedCircleBorderPainter({
41 | required this.color,
42 | required this.strokeWidth,
43 | required this.dashWidth,
44 | required this.dashGap,
45 | });
46 |
47 | @override
48 | void paint(Canvas canvas, Size size) {
49 | Offset center = Offset(size.width / 2, size.height / 2);
50 | double radius = min(size.width / 2, size.height / 2);
51 | final paint = Paint()
52 | ..color = color
53 | ..strokeWidth = strokeWidth
54 | ..style = PaintingStyle.stroke;
55 |
56 | double i = 0.00;
57 | while (i < pi * 2) {
58 | canvas.drawArc(
59 | Rect.fromCircle(center: center, radius: radius),
60 | i,
61 | dashWidth,
62 | false,
63 | paint,
64 | );
65 | i = i + dashGap;
66 | }
67 | }
68 |
69 | @override
70 | bool shouldRepaint(covariant CustomPainter oldDelegate) {
71 | return false;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/lib/widget/loading_view.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_spinkit/flutter_spinkit.dart';
3 | import 'package:get/get.dart';
4 |
5 | class LoadingView {
6 | static final LoadingView singleton = LoadingView._();
7 |
8 | factory LoadingView() => singleton;
9 |
10 | LoadingView._();
11 |
12 | OverlayState? _overlayState;
13 | OverlayEntry? _overlayEntry;
14 | bool _isVisible = false;
15 |
16 | Future wrap({
17 | required Future Function() asyncFunction,
18 | bool showing = true,
19 | }) async {
20 | if (showing) show();
21 | T data;
22 | try {
23 | data = await asyncFunction();
24 | } on Exception catch (e) {
25 | rethrow;
26 | } finally {
27 | dismiss();
28 | }
29 | return data;
30 | }
31 |
32 | void show() async {
33 | if (_isVisible) return;
34 | _overlayState = Overlay.of(Get.overlayContext!);
35 | _overlayEntry = OverlayEntry(
36 | builder: (BuildContext context) => Container(
37 | width: MediaQuery.of(context).size.width,
38 | color: Colors.transparent,
39 | child: const Center(
40 | child: SpinKitSquareCircle(
41 | // color: Color(0xFFFCC604),
42 | color: Colors.black,
43 | )),
44 | ),
45 | );
46 | _isVisible = true;
47 | _overlayState?.insert(_overlayEntry!);
48 | }
49 |
50 | dismiss() async {
51 | if (!_isVisible) return;
52 | _overlayEntry?.remove();
53 | _isVisible = false;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/lib/widget/my_appbar.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_chat_craft/res/images.dart';
3 | import 'package:flutter_svg/flutter_svg.dart';
4 |
5 | myAppBar({
6 | required String title,
7 | required GestureTapCallback backOnTap,
8 | Color color = const Color(0xFF000000),
9 | Color? backColor,
10 | }) {
11 | return AppBar(
12 | backgroundColor: backColor,
13 | leading: IconButton(
14 | icon: SvgPicture.asset(
15 | ImagesRes.icBackLeft,
16 | colorFilter: ColorFilter.mode(
17 | color,
18 | BlendMode.srcIn,
19 | ),
20 | ),
21 | onPressed: backOnTap,
22 | ),
23 | scrolledUnderElevation: 0,
24 | title: Text(
25 | title,
26 | style: TextStyle(
27 | color: color,
28 | ),
29 | ),
30 | centerTitle: true,
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/lib/widget/my_check_box.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_chat_craft/res/images.dart';
4 | import 'package:flutter_svg/flutter_svg.dart';
5 |
6 | class MyCheckBox extends StatelessWidget {
7 | const MyCheckBox({
8 | Key? key,
9 | required this.isCheck,
10 | required this.onTap,
11 | }) : super(key: key);
12 | final bool isCheck;
13 | final Function() onTap;
14 |
15 | @override
16 | Widget build(BuildContext context) {
17 | return GestureDetector(
18 | onTap: onTap,
19 | child: AnimatedContainer(
20 | padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 3),
21 | duration: const Duration(milliseconds: 200),
22 | decoration: BoxDecoration(
23 | border: Border.all(
24 | width: 1,
25 | color:
26 | isCheck ? const Color(0xFFFCC604) : const Color(0xFFA4A4A4)),
27 | color: isCheck ? const Color(0xFFFCC604) : const Color(0xFFFFFFFF),
28 | borderRadius: BorderRadius.circular(4),
29 | ),
30 | child: SvgPicture.asset(ImagesRes.icCheck,
31 | colorFilter: ColorFilter.mode(
32 | isCheck ? Colors.black : Colors.transparent, BlendMode.srcIn)),
33 | ),
34 | );
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/lib/widget/text_btn.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class TextBtn extends StatelessWidget {
4 | final String? text;
5 | final Color? backgroundColor;
6 | final EdgeInsets? padding;
7 | final Size? size;
8 | final double? elevation;
9 | final TextStyle? style;
10 | final VoidCallback? onPressed;
11 | final double? radius;
12 | final Widget? child;
13 | final AlignmentGeometry? alignment;
14 |
15 | const TextBtn({super.key,
16 | this.text,
17 | this.backgroundColor,
18 | this.padding,
19 | this.size,
20 | this.elevation,
21 | this.style,
22 | this.onPressed,
23 | this.radius,
24 | this.child,
25 | this.alignment,
26 | });
27 |
28 | @override
29 | Widget build(BuildContext context) {
30 | return ElevatedButton(
31 | onPressed: onPressed,
32 | style: ButtonStyle(
33 | backgroundColor: MaterialStateProperty.all(backgroundColor ?? Colors.transparent),
34 | minimumSize: MaterialStateProperty.all(size),
35 | padding: MaterialStateProperty.all(padding ?? EdgeInsets.zero),
36 | elevation: MaterialStateProperty.all(elevation ?? 0),
37 | shape: MaterialStateProperty.all(RoundedRectangleBorder(borderRadius: BorderRadius.circular(radius ?? 0))),
38 | alignment: alignment,
39 | tapTargetSize: MaterialTapTargetSize.shrinkWrap,
40 | ),
41 | child: child ??
42 | Text(
43 | text ?? '按钮',
44 | style: style,
45 | ),
46 | );
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/lib/widget/toast_utils.dart:
--------------------------------------------------------------------------------
1 | import 'package:bot_toast/bot_toast.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_screenutil/flutter_screenutil.dart';
4 | import 'package:get/get.dart';
5 |
6 | class ToastUtils {
7 | static CancelFunc toastText(String text) {
8 | return BotToast.showCustomText(
9 | onlyOne: true,
10 | toastBuilder: (cancelFunc) {
11 | double toastHeight = Get.locale!.languageCode == "en"
12 | ? (text.length < 24 ? 36.w : 36.w + 18.w * (text.length / 24 - 1))
13 | : (text.length < 16 ? 36.w : 36.w + 28.w * (text.length / 16 - 1));
14 | return Container(
15 | padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.w),
16 | margin: EdgeInsets.symmetric(horizontal: 64.w),
17 | height: toastHeight,
18 | constraints: BoxConstraints(maxHeight: 144.w, minHeight: 24.w),
19 | alignment: Alignment.center,
20 | decoration: BoxDecoration(
21 | borderRadius: BorderRadius.all(Radius.circular(10.w)),
22 | color: const Color(0xFF000000),
23 | ),
24 | child: Text(
25 | text,
26 | style: TextStyle(color: Colors.white, fontSize: 14.sp),
27 | ),
28 | );
29 | },
30 | wrapAnimation: (controller, cancel, child) => SlideTransition(
31 | position: Tween(begin: const Offset(0, -1), end: const Offset(0, 0))
32 | .animate(
33 | CurvedAnimation(parent: controller, curve: Curves.easeOut)),
34 | child: child,
35 | ),
36 | wrapToastAnimation: (controller, cancel, child) => SlideTransition(
37 | position: Tween(begin: const Offset(0, 0), end: const Offset(0, -1))
38 | .animate(CurvedAnimation(parent: controller, curve: Curves.easeIn)),
39 | child: child,
40 | ),
41 | animationDuration: const Duration(milliseconds: 500),
42 | duration: const Duration(milliseconds: 2000),
43 | align: const Alignment(0.1, -0.75),
44 | );
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/lib/widget/water_mark_view.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_screenutil/flutter_screenutil.dart';
5 |
6 | class WaterMarkBgView extends StatelessWidget {
7 | final String? path;
8 | final String text;
9 | final TextStyle? textStyle;
10 | final Widget child;
11 |
12 | const WaterMarkBgView({
13 | Key? key,
14 | this.path,
15 | this.text = '',
16 | this.textStyle,
17 | required this.child,
18 | }) : super(key: key);
19 |
20 | @override
21 | Widget build(BuildContext context) {
22 | return Stack(
23 | alignment: Alignment.center,
24 | fit: StackFit.expand,
25 | children: [
26 | if (path?.isNotEmpty == true)
27 | Image.file(
28 | File(path!),
29 | fit: BoxFit.cover,
30 | ),
31 | if (text.isNotEmpty) _buildWaterMarkTextView(context: context),
32 | child,
33 | ],
34 | );
35 | }
36 |
37 | Widget _buildWaterMarkTextView({required BuildContext context}) {
38 | var style = textStyle ??
39 | TextStyle(
40 | color: const Color(0xFFBEBEBE).withOpacity(0.4),
41 | fontSize: 24.sp,
42 | decoration: TextDecoration.none,
43 | );
44 | double screenW = MediaQuery.of(context).size.width;
45 | double screenH = MediaQuery.of(context).size.height;
46 | var size = _textSize(text, style);
47 | double itemW = size.width;
48 | double itemH = size.height;
49 |
50 | int rowCount = (screenW / itemW).round() + 1;
51 | int columnCount = (screenH / itemH).round() + 1;
52 |
53 | double maxW = screenW * 1.5;
54 | double maxH = screenH * 1.5;
55 |
56 | List children = List.filled(
57 | columnCount * rowCount,
58 | Text(
59 | text,
60 | style: style,
61 | textAlign: TextAlign.center,
62 | maxLines: 1,
63 | ),
64 | );
65 | return Transform(
66 | transform: Matrix4.skewY(-0.6),
67 | child: OverflowBox(
68 | maxWidth: maxW,
69 | maxHeight: maxH,
70 | alignment: Alignment.center,
71 | child: Wrap(
72 | alignment: WrapAlignment.start,
73 | spacing: 50.w,
74 | runSpacing: 100.h,
75 | children: children,
76 | ),
77 | ),
78 | );
79 | }
80 |
81 | // Here it is!
82 | Size _textSize(String text, TextStyle style) {
83 | final TextPainter textPainter = TextPainter(
84 | text: TextSpan(text: text, style: style),
85 | maxLines: 1,
86 | textDirection: TextDirection.ltr)
87 | ..layout(minWidth: 0, maxWidth: double.infinity);
88 | return textPainter.size;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/macos/.gitignore:
--------------------------------------------------------------------------------
1 | # Flutter-related
2 | **/Flutter/ephemeral/
3 | **/Pods/
4 |
5 | # Xcode-related
6 | **/dgph
7 | **/xcuserdata/
8 |
--------------------------------------------------------------------------------
/macos/Flutter/Flutter-Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "ephemeral/Flutter-Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/macos/Flutter/Flutter-Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "ephemeral/Flutter-Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/macos/Flutter/GeneratedPluginRegistrant.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | import FlutterMacOS
6 | import Foundation
7 |
8 | import audio_session
9 | import flutter_localization
10 | import flutter_webrtc
11 | import just_audio
12 | import path_provider_foundation
13 | import photo_manager
14 | import record_macos
15 | import shared_preferences_foundation
16 | import sqflite
17 | import url_launcher_macos
18 | import video_player_avfoundation
19 |
20 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
21 | AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
22 | FlutterLocalizationPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalizationPlugin"))
23 | FlutterWebRTCPlugin.register(with: registry.registrar(forPlugin: "FlutterWebRTCPlugin"))
24 | JustAudioPlugin.register(with: registry.registrar(forPlugin: "JustAudioPlugin"))
25 | PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
26 | PhotoManagerPlugin.register(with: registry.registrar(forPlugin: "PhotoManagerPlugin"))
27 | RecordMacosPlugin.register(with: registry.registrar(forPlugin: "RecordMacosPlugin"))
28 | SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
29 | SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
30 | UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
31 | FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin"))
32 | }
33 |
--------------------------------------------------------------------------------
/macos/Podfile:
--------------------------------------------------------------------------------
1 | platform :osx, '10.14'
2 |
3 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
4 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
5 |
6 | project 'Runner', {
7 | 'Debug' => :debug,
8 | 'Profile' => :release,
9 | 'Release' => :release,
10 | }
11 |
12 | def flutter_root
13 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)
14 | unless File.exist?(generated_xcode_build_settings_path)
15 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first"
16 | end
17 |
18 | File.foreach(generated_xcode_build_settings_path) do |line|
19 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
20 | return matches[1].strip if matches
21 | end
22 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\""
23 | end
24 |
25 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
26 |
27 | flutter_macos_podfile_setup
28 |
29 | target 'Runner' do
30 | use_frameworks!
31 | use_modular_headers!
32 |
33 | flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
34 | target 'RunnerTests' do
35 | inherit! :search_paths
36 | end
37 | end
38 |
39 | post_install do |installer|
40 | installer.pods_project.targets.each do |target|
41 | flutter_additional_macos_build_settings(target)
42 | end
43 | end
44 |
--------------------------------------------------------------------------------
/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/macos/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/macos/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import Cocoa
2 | import FlutterMacOS
3 |
4 | @NSApplicationMain
5 | class AppDelegate: FlutterAppDelegate {
6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
7 | return true
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "16x16",
5 | "idiom" : "mac",
6 | "filename" : "app_icon_16.png",
7 | "scale" : "1x"
8 | },
9 | {
10 | "size" : "16x16",
11 | "idiom" : "mac",
12 | "filename" : "app_icon_32.png",
13 | "scale" : "2x"
14 | },
15 | {
16 | "size" : "32x32",
17 | "idiom" : "mac",
18 | "filename" : "app_icon_32.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "32x32",
23 | "idiom" : "mac",
24 | "filename" : "app_icon_64.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "128x128",
29 | "idiom" : "mac",
30 | "filename" : "app_icon_128.png",
31 | "scale" : "1x"
32 | },
33 | {
34 | "size" : "128x128",
35 | "idiom" : "mac",
36 | "filename" : "app_icon_256.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "256x256",
41 | "idiom" : "mac",
42 | "filename" : "app_icon_256.png",
43 | "scale" : "1x"
44 | },
45 | {
46 | "size" : "256x256",
47 | "idiom" : "mac",
48 | "filename" : "app_icon_512.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "512x512",
53 | "idiom" : "mac",
54 | "filename" : "app_icon_512.png",
55 | "scale" : "1x"
56 | },
57 | {
58 | "size" : "512x512",
59 | "idiom" : "mac",
60 | "filename" : "app_icon_1024.png",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png
--------------------------------------------------------------------------------
/macos/Runner/Configs/AppInfo.xcconfig:
--------------------------------------------------------------------------------
1 | // Application-level settings for the Runner target.
2 | //
3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the
4 | // future. If not, the values below would default to using the project name when this becomes a
5 | // 'flutter create' template.
6 |
7 | // The application's name. By default this is also the title of the Flutter window.
8 | PRODUCT_NAME = flutter_chat_craft
9 |
10 | // The application's bundle identifier
11 | PRODUCT_BUNDLE_IDENTIFIER = com.taxze.chat.craft.flutterChatCraft
12 |
13 | // The copyright displayed in application information
14 | PRODUCT_COPYRIGHT = Copyright © 2023 com.taxze.chat.craft. All rights reserved.
15 |
--------------------------------------------------------------------------------
/macos/Runner/Configs/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "../../Flutter/Flutter-Debug.xcconfig"
2 | #include "Warnings.xcconfig"
3 |
--------------------------------------------------------------------------------
/macos/Runner/Configs/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "../../Flutter/Flutter-Release.xcconfig"
2 | #include "Warnings.xcconfig"
3 |
--------------------------------------------------------------------------------
/macos/Runner/Configs/Warnings.xcconfig:
--------------------------------------------------------------------------------
1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings
2 | GCC_WARN_UNDECLARED_SELECTOR = YES
3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES
4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
6 | CLANG_WARN_PRAGMA_PACK = YES
7 | CLANG_WARN_STRICT_PROTOTYPES = YES
8 | CLANG_WARN_COMMA = YES
9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES
10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES
11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
12 | GCC_WARN_SHADOW = YES
13 | CLANG_WARN_UNREACHABLE_CODE = YES
14 |
--------------------------------------------------------------------------------
/macos/Runner/DebugProfile.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.cs.allow-jit
8 |
9 | com.apple.security.network.server
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/macos/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | $(FLUTTER_BUILD_NAME)
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSMinimumSystemVersion
24 | $(MACOSX_DEPLOYMENT_TARGET)
25 | NSHumanReadableCopyright
26 | $(PRODUCT_COPYRIGHT)
27 | NSMainNibFile
28 | MainMenu
29 | NSPrincipalClass
30 | NSApplication
31 |
32 |
33 |
--------------------------------------------------------------------------------
/macos/Runner/MainFlutterWindow.swift:
--------------------------------------------------------------------------------
1 | import Cocoa
2 | import FlutterMacOS
3 |
4 | class MainFlutterWindow: NSWindow {
5 | override func awakeFromNib() {
6 | let flutterViewController = FlutterViewController()
7 | let windowFrame = self.frame
8 | self.contentViewController = flutterViewController
9 | self.setFrame(windowFrame, display: true)
10 |
11 | RegisterGeneratedPlugins(registry: flutterViewController)
12 |
13 | super.awakeFromNib()
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/macos/Runner/Release.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/macos/RunnerTests/RunnerTests.swift:
--------------------------------------------------------------------------------
1 | import FlutterMacOS
2 | import Cocoa
3 | import XCTest
4 |
5 | class RunnerTests: XCTestCase {
6 |
7 | func testExample() {
8 | // If you add code to the Runner application, consider adding tests here.
9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest.
10 | }
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | //
3 | // To perform an interaction with a widget in your test, use the WidgetTester
4 | // utility in the flutter_test package. For example, you can send tap and scroll
5 | // gestures. You can also use WidgetTester to find child widgets in the widget
6 | // tree, read text, and verify that the values of widget properties are correct.
7 |
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_chat_craft/app.dart';
10 | import 'package:flutter_test/flutter_test.dart';
11 |
12 | void main() {
13 | testWidgets('Counter increments smoke test', (WidgetTester tester) async {
14 | // Build our app and trigger a frame.
15 | await tester.pumpWidget(const ChatCraftApp());
16 |
17 | // Verify that our counter starts at 0.
18 | expect(find.text('0'), findsOneWidget);
19 | expect(find.text('1'), findsNothing);
20 |
21 | // Tap the '+' icon and trigger a frame.
22 | await tester.tap(find.byIcon(Icons.add));
23 | await tester.pump();
24 |
25 | // Verify that our counter has incremented.
26 | expect(find.text('0'), findsNothing);
27 | expect(find.text('1'), findsOneWidget);
28 | });
29 | }
30 |
--------------------------------------------------------------------------------
/web/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/web/favicon.png
--------------------------------------------------------------------------------
/web/icons/Icon-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/web/icons/Icon-192.png
--------------------------------------------------------------------------------
/web/icons/Icon-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/web/icons/Icon-512.png
--------------------------------------------------------------------------------
/web/icons/Icon-maskable-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/web/icons/Icon-maskable-192.png
--------------------------------------------------------------------------------
/web/icons/Icon-maskable-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/web/icons/Icon-maskable-512.png
--------------------------------------------------------------------------------
/web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | flutter_chat_craft
33 |
34 |
35 |
39 |
40 |
41 |
42 |
43 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/web/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "flutter_chat_craft",
3 | "short_name": "flutter_chat_craft",
4 | "start_url": ".",
5 | "display": "standalone",
6 | "background_color": "#0175C2",
7 | "theme_color": "#0175C2",
8 | "description": "A new Flutter project.",
9 | "orientation": "portrait-primary",
10 | "prefer_related_applications": false,
11 | "icons": [
12 | {
13 | "src": "icons/Icon-192.png",
14 | "sizes": "192x192",
15 | "type": "image/png"
16 | },
17 | {
18 | "src": "icons/Icon-512.png",
19 | "sizes": "512x512",
20 | "type": "image/png"
21 | },
22 | {
23 | "src": "icons/Icon-maskable-192.png",
24 | "sizes": "192x192",
25 | "type": "image/png",
26 | "purpose": "maskable"
27 | },
28 | {
29 | "src": "icons/Icon-maskable-512.png",
30 | "sizes": "512x512",
31 | "type": "image/png",
32 | "purpose": "maskable"
33 | }
34 | ]
35 | }
36 |
--------------------------------------------------------------------------------
/windows/.gitignore:
--------------------------------------------------------------------------------
1 | flutter/ephemeral/
2 |
3 | # Visual Studio user-specific files.
4 | *.suo
5 | *.user
6 | *.userosscache
7 | *.sln.docstates
8 |
9 | # Visual Studio build-related files.
10 | x64/
11 | x86/
12 |
13 | # Visual Studio cache files
14 | # files ending in .cache can be ignored
15 | *.[Cc]ache
16 | # but keep track of directories ending in .cache
17 | !*.[Cc]ache/
18 |
--------------------------------------------------------------------------------
/windows/flutter/generated_plugin_registrant.cc:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | // clang-format off
6 |
7 | #include "generated_plugin_registrant.h"
8 |
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 |
15 | void RegisterPlugins(flutter::PluginRegistry* registry) {
16 | FlutterLocalizationPluginCApiRegisterWithRegistrar(
17 | registry->GetRegistrarForPlugin("FlutterLocalizationPluginCApi"));
18 | FlutterWebRTCPluginRegisterWithRegistrar(
19 | registry->GetRegistrarForPlugin("FlutterWebRTCPlugin"));
20 | PermissionHandlerWindowsPluginRegisterWithRegistrar(
21 | registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
22 | RecordWindowsPluginCApiRegisterWithRegistrar(
23 | registry->GetRegistrarForPlugin("RecordWindowsPluginCApi"));
24 | UrlLauncherWindowsRegisterWithRegistrar(
25 | registry->GetRegistrarForPlugin("UrlLauncherWindows"));
26 | }
27 |
--------------------------------------------------------------------------------
/windows/flutter/generated_plugin_registrant.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | // clang-format off
6 |
7 | #ifndef GENERATED_PLUGIN_REGISTRANT_
8 | #define GENERATED_PLUGIN_REGISTRANT_
9 |
10 | #include
11 |
12 | // Registers Flutter plugins.
13 | void RegisterPlugins(flutter::PluginRegistry* registry);
14 |
15 | #endif // GENERATED_PLUGIN_REGISTRANT_
16 |
--------------------------------------------------------------------------------
/windows/flutter/generated_plugins.cmake:
--------------------------------------------------------------------------------
1 | #
2 | # Generated file, do not edit.
3 | #
4 |
5 | list(APPEND FLUTTER_PLUGIN_LIST
6 | flutter_localization
7 | flutter_webrtc
8 | permission_handler_windows
9 | record_windows
10 | url_launcher_windows
11 | )
12 |
13 | list(APPEND FLUTTER_FFI_PLUGIN_LIST
14 | )
15 |
16 | set(PLUGIN_BUNDLED_LIBRARIES)
17 |
18 | foreach(plugin ${FLUTTER_PLUGIN_LIST})
19 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})
20 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
21 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $)
22 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
23 | endforeach(plugin)
24 |
25 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
26 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})
27 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
28 | endforeach(ffi_plugin)
29 |
--------------------------------------------------------------------------------
/windows/runner/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.14)
2 | project(runner LANGUAGES CXX)
3 |
4 | # Define the application target. To change its name, change BINARY_NAME in the
5 | # top-level CMakeLists.txt, not the value here, or `flutter run` will no longer
6 | # work.
7 | #
8 | # Any new source files that you add to the application should be added here.
9 | add_executable(${BINARY_NAME} WIN32
10 | "flutter_window.cpp"
11 | "main.cpp"
12 | "utils.cpp"
13 | "win32_window.cpp"
14 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
15 | "Runner.rc"
16 | "runner.exe.manifest"
17 | )
18 |
19 | # Apply the standard set of build settings. This can be removed for applications
20 | # that need different build settings.
21 | apply_standard_settings(${BINARY_NAME})
22 |
23 | # Add preprocessor definitions for the build version.
24 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"")
25 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}")
26 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}")
27 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}")
28 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}")
29 |
30 | # Disable Windows macros that collide with C++ standard library functions.
31 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
32 |
33 | # Add dependency libraries and include directories. Add any application-specific
34 | # dependencies here.
35 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
36 | target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib")
37 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
38 |
39 | # Run the Flutter tool portions of the build. This must not be removed.
40 | add_dependencies(${BINARY_NAME} flutter_assemble)
41 |
--------------------------------------------------------------------------------
/windows/runner/flutter_window.cpp:
--------------------------------------------------------------------------------
1 | #include "flutter_window.h"
2 |
3 | #include
4 |
5 | #include "flutter/generated_plugin_registrant.h"
6 |
7 | FlutterWindow::FlutterWindow(const flutter::DartProject& project)
8 | : project_(project) {}
9 |
10 | FlutterWindow::~FlutterWindow() {}
11 |
12 | bool FlutterWindow::OnCreate() {
13 | if (!Win32Window::OnCreate()) {
14 | return false;
15 | }
16 |
17 | RECT frame = GetClientArea();
18 |
19 | // The size here must match the window dimensions to avoid unnecessary surface
20 | // creation / destruction in the startup path.
21 | flutter_controller_ = std::make_unique(
22 | frame.right - frame.left, frame.bottom - frame.top, project_);
23 | // Ensure that basic setup of the controller was successful.
24 | if (!flutter_controller_->engine() || !flutter_controller_->view()) {
25 | return false;
26 | }
27 | RegisterPlugins(flutter_controller_->engine());
28 | SetChildContent(flutter_controller_->view()->GetNativeWindow());
29 |
30 | flutter_controller_->engine()->SetNextFrameCallback([&]() {
31 | this->Show();
32 | });
33 |
34 | // Flutter can complete the first frame before the "show window" callback is
35 | // registered. The following call ensures a frame is pending to ensure the
36 | // window is shown. It is a no-op if the first frame hasn't completed yet.
37 | flutter_controller_->ForceRedraw();
38 |
39 | return true;
40 | }
41 |
42 | void FlutterWindow::OnDestroy() {
43 | if (flutter_controller_) {
44 | flutter_controller_ = nullptr;
45 | }
46 |
47 | Win32Window::OnDestroy();
48 | }
49 |
50 | LRESULT
51 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
52 | WPARAM const wparam,
53 | LPARAM const lparam) noexcept {
54 | // Give Flutter, including plugins, an opportunity to handle window messages.
55 | if (flutter_controller_) {
56 | std::optional result =
57 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
58 | lparam);
59 | if (result) {
60 | return *result;
61 | }
62 | }
63 |
64 | switch (message) {
65 | case WM_FONTCHANGE:
66 | flutter_controller_->engine()->ReloadSystemFonts();
67 | break;
68 | }
69 |
70 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
71 | }
72 |
--------------------------------------------------------------------------------
/windows/runner/flutter_window.h:
--------------------------------------------------------------------------------
1 | #ifndef RUNNER_FLUTTER_WINDOW_H_
2 | #define RUNNER_FLUTTER_WINDOW_H_
3 |
4 | #include
5 | #include
6 |
7 | #include
8 |
9 | #include "win32_window.h"
10 |
11 | // A window that does nothing but host a Flutter view.
12 | class FlutterWindow : public Win32Window {
13 | public:
14 | // Creates a new FlutterWindow hosting a Flutter view running |project|.
15 | explicit FlutterWindow(const flutter::DartProject& project);
16 | virtual ~FlutterWindow();
17 |
18 | protected:
19 | // Win32Window:
20 | bool OnCreate() override;
21 | void OnDestroy() override;
22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
23 | LPARAM const lparam) noexcept override;
24 |
25 | private:
26 | // The project to run.
27 | flutter::DartProject project_;
28 |
29 | // The Flutter instance hosted by this window.
30 | std::unique_ptr flutter_controller_;
31 | };
32 |
33 | #endif // RUNNER_FLUTTER_WINDOW_H_
34 |
--------------------------------------------------------------------------------
/windows/runner/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include "flutter_window.h"
6 | #include "utils.h"
7 |
8 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
9 | _In_ wchar_t *command_line, _In_ int show_command) {
10 | // Attach to console when present (e.g., 'flutter run') or create a
11 | // new console when running with a debugger.
12 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
13 | CreateAndAttachConsole();
14 | }
15 |
16 | // Initialize COM, so that it is available for use in the library and/or
17 | // plugins.
18 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
19 |
20 | flutter::DartProject project(L"data");
21 |
22 | std::vector command_line_arguments =
23 | GetCommandLineArguments();
24 |
25 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
26 |
27 | FlutterWindow window(project);
28 | Win32Window::Point origin(10, 10);
29 | Win32Window::Size size(1280, 720);
30 | if (!window.Create(L"flutter_chat_craft", origin, size)) {
31 | return EXIT_FAILURE;
32 | }
33 | window.SetQuitOnClose(true);
34 |
35 | ::MSG msg;
36 | while (::GetMessage(&msg, nullptr, 0, 0)) {
37 | ::TranslateMessage(&msg);
38 | ::DispatchMessage(&msg);
39 | }
40 |
41 | ::CoUninitialize();
42 | return EXIT_SUCCESS;
43 | }
44 |
--------------------------------------------------------------------------------
/windows/runner/resource.h:
--------------------------------------------------------------------------------
1 | //{{NO_DEPENDENCIES}}
2 | // Microsoft Visual C++ generated include file.
3 | // Used by Runner.rc
4 | //
5 | #define IDI_APP_ICON 101
6 |
7 | // Next default values for new objects
8 | //
9 | #ifdef APSTUDIO_INVOKED
10 | #ifndef APSTUDIO_READONLY_SYMBOLS
11 | #define _APS_NEXT_RESOURCE_VALUE 102
12 | #define _APS_NEXT_COMMAND_VALUE 40001
13 | #define _APS_NEXT_CONTROL_VALUE 1001
14 | #define _APS_NEXT_SYMED_VALUE 101
15 | #endif
16 | #endif
17 |
--------------------------------------------------------------------------------
/windows/runner/resources/app_icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taxze6/flutter-chat-craft/2efc89530ac17e7a6087ebbc5aa418d8bdeb0948/windows/runner/resources/app_icon.ico
--------------------------------------------------------------------------------
/windows/runner/runner.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PerMonitorV2
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/windows/runner/utils.cpp:
--------------------------------------------------------------------------------
1 | #include "utils.h"
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | #include
9 |
10 | void CreateAndAttachConsole() {
11 | if (::AllocConsole()) {
12 | FILE *unused;
13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
14 | _dup2(_fileno(stdout), 1);
15 | }
16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
17 | _dup2(_fileno(stdout), 2);
18 | }
19 | std::ios::sync_with_stdio();
20 | FlutterDesktopResyncOutputStreams();
21 | }
22 | }
23 |
24 | std::vector GetCommandLineArguments() {
25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.
26 | int argc;
27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
28 | if (argv == nullptr) {
29 | return std::vector();
30 | }
31 |
32 | std::vector command_line_arguments;
33 |
34 | // Skip the first argument as it's the binary name.
35 | for (int i = 1; i < argc; i++) {
36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i]));
37 | }
38 |
39 | ::LocalFree(argv);
40 |
41 | return command_line_arguments;
42 | }
43 |
44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) {
45 | if (utf16_string == nullptr) {
46 | return std::string();
47 | }
48 | int target_length = ::WideCharToMultiByte(
49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
50 | -1, nullptr, 0, nullptr, nullptr)
51 | -1; // remove the trailing null character
52 | int input_length = (int)wcslen(utf16_string);
53 | std::string utf8_string;
54 | if (target_length <= 0 || target_length > utf8_string.max_size()) {
55 | return utf8_string;
56 | }
57 | utf8_string.resize(target_length);
58 | int converted_length = ::WideCharToMultiByte(
59 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
60 | input_length, utf8_string.data(), target_length, nullptr, nullptr);
61 | if (converted_length == 0) {
62 | return std::string();
63 | }
64 | return utf8_string;
65 | }
66 |
--------------------------------------------------------------------------------
/windows/runner/utils.h:
--------------------------------------------------------------------------------
1 | #ifndef RUNNER_UTILS_H_
2 | #define RUNNER_UTILS_H_
3 |
4 | #include
5 | #include
6 |
7 | // Creates a console for the process, and redirects stdout and stderr to
8 | // it for both the runner and the Flutter library.
9 | void CreateAndAttachConsole();
10 |
11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string
12 | // encoded in UTF-8. Returns an empty std::string on failure.
13 | std::string Utf8FromUtf16(const wchar_t* utf16_string);
14 |
15 | // Gets the command line arguments passed in as a std::vector,
16 | // encoded in UTF-8. Returns an empty std::vector on failure.
17 | std::vector GetCommandLineArguments();
18 |
19 | #endif // RUNNER_UTILS_H_
20 |
--------------------------------------------------------------------------------