├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── build.yml ├── .gitignore ├── .vscode ├── build.sh └── launch.json ├── LICENSE ├── README.md ├── README_ZH.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── m │ │ │ │ └── c │ │ │ │ └── g │ │ │ │ └── a │ │ │ │ └── i_iwara │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-hdpi │ │ │ └── ic_launcher_foreground.png │ │ │ ├── drawable-mdpi │ │ │ └── ic_launcher_foreground.png │ │ │ ├── drawable-v21 │ │ │ └── launch_background.xml │ │ │ ├── drawable-xhdpi │ │ │ └── ic_launcher_foreground.png │ │ │ ├── drawable-xxhdpi │ │ │ └── ic_launcher_foreground.png │ │ │ ├── drawable-xxxhdpi │ │ │ └── ic_launcher_foreground.png │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ └── launcher_icon.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── launcher_icon.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── launcher_icon.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── launcher_icon.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── launcher_icon.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── launcher_icon.png │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ └── values │ │ │ ├── colors.xml │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets ├── icon │ └── launcher_icon_v2.png ├── images │ ├── 2.0x │ │ └── flutter_logo.png │ ├── 3.0x │ │ └── flutter_logo.png │ └── flutter_logo.png └── svg │ ├── app_enter_fullscreen.svg │ └── app_exit_fullscreen.svg ├── docs └── imgs │ ├── all.png │ ├── dingyue.png │ ├── download.png │ ├── filter.png │ ├── gonggao.png │ ├── huihua.png │ ├── localshoucang.png │ ├── luntan.png │ ├── luntanxaingqing.png │ ├── pinglun.png │ ├── record.png │ ├── shezhi.png │ ├── shipin.png │ ├── shipin2.png │ ├── shipinliebiao.png │ ├── sousuo.png │ ├── tongzhi.png │ ├── tuku.png │ ├── tukuliebiao.png │ └── zuozhe.png ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── 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-50x50@1x.png │ │ │ ├── Icon-App-50x50@2x.png │ │ │ ├── Icon-App-57x57@1x.png │ │ │ ├── Icon-App-57x57@2x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-72x72@1x.png │ │ │ ├── Icon-App-72x72@2x.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 │ ├── models │ │ ├── api_result.model.dart │ │ ├── captcha.model.dart │ │ ├── comment.model.dart │ │ ├── custom_thumbnail.model.dart │ │ ├── download │ │ │ ├── download_task.model.dart │ │ │ └── download_task_ext_data.model.dart │ │ ├── dto │ │ │ ├── escape_intent.dart │ │ │ ├── forum_thread_section_dto.dart │ │ │ ├── history_record_dto.dart │ │ │ ├── profile_user_dto.dart │ │ │ ├── user_dto.dart │ │ │ └── user_request_dto.dart │ │ ├── favorite │ │ │ ├── favorite_folder.model.dart │ │ │ └── favorite_item.model.dart │ │ ├── forum.model.dart │ │ ├── history_record.dart │ │ ├── image.model.dart │ │ ├── light_play_list.model.dart │ │ ├── media_file.model.dart │ │ ├── message_and_conversation.model.dart │ │ ├── page_data.model.dart │ │ ├── play_list.model.dart │ │ ├── post.model.dart │ │ ├── rules.model.dart │ │ ├── search_record.model.dart │ │ ├── sort.model.dart │ │ ├── tag.model.dart │ │ ├── theme_mode.model.dart │ │ ├── update_info.model.dart │ │ ├── user.model.dart │ │ ├── user_avatar.model.dart │ │ ├── user_notification_count.model.dart │ │ ├── video.model.dart │ │ └── video_source.model.dart │ ├── my_app.dart │ ├── repositories │ │ ├── base_repository.dart │ │ ├── commons_repository.dart │ │ ├── download_task_repository.dart │ │ ├── history_repository.dart │ │ └── sign_in_repository.dart │ ├── routes │ │ └── app_routes.dart │ ├── services │ │ ├── api_service.dart │ │ ├── app_service.dart │ │ ├── auth_service.dart │ │ ├── comment_service.dart │ │ ├── config_backup_service.dart │ │ ├── config_service.dart │ │ ├── conversation_service.dart │ │ ├── deep_link_service.dart │ │ ├── download_service.dart │ │ ├── favorite_service.dart │ │ ├── forum_service.dart │ │ ├── gallery_service.dart │ │ ├── light_service.dart │ │ ├── log_service.dart │ │ ├── message_service.dart │ │ ├── play_list_service.dart │ │ ├── playback_history_service.dart │ │ ├── post_service.dart │ │ ├── search_service.dart │ │ ├── share_service.dart │ │ ├── storage_service.dart │ │ ├── tag_service.dart │ │ ├── theme_service.dart │ │ ├── translation_service.dart │ │ ├── user_preference_service.dart │ │ ├── user_service.dart │ │ ├── version_service.dart │ │ └── video_service.dart │ ├── ui │ │ ├── pages │ │ │ ├── author_profile │ │ │ │ ├── author_profile_page.dart │ │ │ │ ├── controllers │ │ │ │ │ ├── authro_profile_controller.dart │ │ │ │ │ ├── userz_image_model_list_controller.dart │ │ │ │ │ ├── userz_post_list_repository.dart │ │ │ │ │ └── userz_video_list_controller.dart │ │ │ │ └── widgets │ │ │ │ │ ├── author_profile_skeleton_widget.dart │ │ │ │ │ ├── post_input_dialog.dart │ │ │ │ │ ├── profile_image_model_tab_list_widget.dart │ │ │ │ │ ├── profile_playlist_tab_list_widget.dart │ │ │ │ │ ├── profile_post_tab_list_widget.dart │ │ │ │ │ ├── profile_video_tab_list_widget.dart │ │ │ │ │ └── share_user_bottom_sheet.dart │ │ │ ├── comment │ │ │ │ ├── controllers │ │ │ │ │ └── comment_controller.dart │ │ │ │ └── widgets │ │ │ │ │ ├── comment_entry_area_widget.dart │ │ │ │ │ ├── comment_input_dialog.dart │ │ │ │ │ ├── comment_item_widget.dart │ │ │ │ │ ├── comment_remove_dialog.dart │ │ │ │ │ ├── comment_section_widget.dart │ │ │ │ │ └── rules_agreement_dialog_widget.dart │ │ │ ├── conversation │ │ │ │ ├── controllers │ │ │ │ │ └── conversation_list_repository.dart │ │ │ │ ├── conversation_page.dart │ │ │ │ └── widgets │ │ │ │ │ ├── conversation_list_widget.dart │ │ │ │ │ ├── conversation_message_dialog.dart │ │ │ │ │ ├── message_list_widget.dart │ │ │ │ │ └── new_conversation_dialog.dart │ │ │ ├── download │ │ │ │ ├── download_task_list_page.dart │ │ │ │ ├── gallery_download_task_detail_page.dart │ │ │ │ └── widgets │ │ │ │ │ ├── default_download_task_item_widget.dart │ │ │ │ │ ├── gallery_download_task_item_widget.dart │ │ │ │ │ ├── status_label_widget.dart │ │ │ │ │ └── video_download_task_item_widget.dart │ │ │ ├── favorite │ │ │ │ ├── favorite_folder_detail_page.dart │ │ │ │ └── favorite_list_page.dart │ │ │ ├── favorites │ │ │ │ ├── controllers │ │ │ │ │ └── favorites_controller.dart │ │ │ │ ├── my_favorites.dart │ │ │ │ ├── repositories │ │ │ │ │ ├── favorite_image_repository.dart │ │ │ │ │ └── favorite_video_repository.dart │ │ │ │ └── widgets │ │ │ │ │ ├── favorite_image_list.dart │ │ │ │ │ └── favorite_video_list.dart │ │ │ ├── follows │ │ │ │ ├── controllers │ │ │ │ │ └── follows_controller.dart │ │ │ │ ├── follows_page.dart │ │ │ │ ├── repositories │ │ │ │ │ ├── followers_list_repository.dart │ │ │ │ │ └── following_list_repository.dart │ │ │ │ └── widgets │ │ │ │ │ ├── followers_list.dart │ │ │ │ │ ├── following_list.dart │ │ │ │ │ └── special_follows_list.dart │ │ │ ├── forum │ │ │ │ ├── controllers │ │ │ │ │ ├── forum_list_controller.dart │ │ │ │ │ ├── recent_thread_repository.dart │ │ │ │ │ ├── thread_detail_repository.dart │ │ │ │ │ └── thread_list_repository.dart │ │ │ │ ├── forum_page.dart │ │ │ │ ├── forum_skeleton_page.dart │ │ │ │ ├── thread_detail_page.dart │ │ │ │ ├── thread_list_page.dart │ │ │ │ └── widgets │ │ │ │ │ ├── forum_edit_reply_dialog.dart │ │ │ │ │ ├── forum_edit_title_dialog.dart │ │ │ │ │ ├── forum_post_dialog.dart │ │ │ │ │ ├── forum_reply_dialog.dart │ │ │ │ │ ├── forum_shimmer_widget.dart │ │ │ │ │ ├── share_thread_bottom_sheet.dart │ │ │ │ │ ├── thread_comment_card_widget.dart │ │ │ │ │ └── thread_list_item_widget.dart │ │ │ ├── friends │ │ │ │ ├── controllers │ │ │ │ │ └── friends_controller.dart │ │ │ │ ├── friends_page.dart │ │ │ │ ├── repositories │ │ │ │ │ ├── friend_list_repository.dart │ │ │ │ │ └── friend_request_repository.dart │ │ │ │ └── widgets │ │ │ │ │ ├── friend_list.dart │ │ │ │ │ └── friend_request_list.dart │ │ │ ├── gallery_detail │ │ │ │ ├── controllers │ │ │ │ │ └── gallery_detail_controller.dart │ │ │ │ ├── gallery_detail_page.dart │ │ │ │ └── widgets │ │ │ │ │ ├── gallery_image_scroller_widget.dart │ │ │ │ │ ├── horizontial_image_list.dart │ │ │ │ │ ├── image_model_detail_content_widget.dart │ │ │ │ │ ├── menu_item_widget.dart │ │ │ │ │ ├── my_gallery_photo_view_wrapper.dart │ │ │ │ │ └── share_gallery_bottom_sheet.dart │ │ │ ├── history │ │ │ │ ├── controllers │ │ │ │ │ ├── history_list_controller.dart │ │ │ │ │ └── history_list_repository.dart │ │ │ │ └── history_list_page.dart │ │ │ ├── home │ │ │ │ └── home_navigation_layout.dart │ │ │ ├── home_page.dart │ │ │ ├── login │ │ │ │ └── login_page.dart │ │ │ ├── notifications │ │ │ │ ├── controllers │ │ │ │ │ └── notification_list_repository.dart │ │ │ │ ├── notification_list_page.dart │ │ │ │ └── widgets │ │ │ │ │ ├── notification_content_items.dart │ │ │ │ │ └── notification_list_item_widget.dart │ │ │ ├── play_list │ │ │ │ ├── controllers │ │ │ │ │ ├── play_list_controller.dart │ │ │ │ │ ├── play_list_detail_controller.dart │ │ │ │ │ ├── play_list_detail_repository.dart │ │ │ │ │ └── play_list_repository.dart │ │ │ │ ├── play_list.dart │ │ │ │ ├── play_list_detail.dart │ │ │ │ └── widgets │ │ │ │ │ └── playlist_item_widget.dart │ │ │ ├── popular_media_list │ │ │ │ ├── controllers │ │ │ │ │ ├── base_media_controller.dart │ │ │ │ │ ├── base_media_repository.dart │ │ │ │ │ ├── popular_gallery_controller.dart │ │ │ │ │ ├── popular_gallery_repository.dart │ │ │ │ │ ├── popular_media_list_controller.dart │ │ │ │ │ ├── popular_video_controller.dart │ │ │ │ │ ├── popular_video_repository.dart │ │ │ │ │ └── tag_controller.dart │ │ │ │ ├── popular_gallery_list_page.dart │ │ │ │ ├── popular_media_list_base_page.dart │ │ │ │ ├── popular_video_list_page.dart │ │ │ │ └── widgets │ │ │ │ │ ├── add_search_tag_dialog.dart │ │ │ │ │ ├── animated_preview_widget.dart │ │ │ │ │ ├── common_media_list_widgets.dart │ │ │ │ │ ├── image_model_card_list_item_widget.dart │ │ │ │ │ ├── image_model_tile_list_item_widget.dart │ │ │ │ │ ├── media_description_widget.dart │ │ │ │ │ ├── media_list_view.dart │ │ │ │ │ ├── media_tab_view.dart │ │ │ │ │ ├── popular_media_search_config_widget.dart │ │ │ │ │ ├── post_card_list_item_widget.dart │ │ │ │ │ ├── post_tile_list_item_widget.dart │ │ │ │ │ ├── remove_search_tag_dialog.dart │ │ │ │ │ ├── video_card_list_item_widget.dart │ │ │ │ │ ├── video_preview_modal.dart │ │ │ │ │ └── video_tile_list_item_widget.dart │ │ │ ├── post_detail │ │ │ │ ├── controllers │ │ │ │ │ └── post_detail_controller.dart │ │ │ │ ├── post_detail_page.dart │ │ │ │ └── widgets │ │ │ │ │ ├── post_detail_content_widget.dart │ │ │ │ │ ├── post_detail_shimmer.dart │ │ │ │ │ └── share_post_bottom_sheet.dart │ │ │ ├── search │ │ │ │ ├── repositories │ │ │ │ │ ├── search_repositories.dart │ │ │ │ │ └── search_repository.dart │ │ │ │ ├── search_dialog.dart │ │ │ │ ├── search_result.dart │ │ │ │ └── widgets │ │ │ │ │ ├── base_search_list.dart │ │ │ │ │ └── search_list_widgets.dart │ │ │ ├── settings │ │ │ │ ├── about_page.dart │ │ │ │ ├── app_settings_page.dart │ │ │ │ ├── history_update_logs_page.dart │ │ │ │ ├── player_settings_page.dart │ │ │ │ ├── proxy_settings_page.dart │ │ │ │ ├── settings_page.dart │ │ │ │ ├── theme_settings_page.dart │ │ │ │ └── widgets │ │ │ │ │ ├── ai_translation_setting_widget.dart │ │ │ │ │ ├── player_settings_widget.dart │ │ │ │ │ ├── proxy_setting_widget.dart │ │ │ │ │ ├── setting_item_widget.dart │ │ │ │ │ ├── settings_app_bar.dart │ │ │ │ │ ├── signature_edit_dialog_widget.dart │ │ │ │ │ └── three_section_slider.dart │ │ │ ├── sign_in │ │ │ │ ├── controllers │ │ │ │ │ └── sign_in_controller.dart │ │ │ │ ├── sing_in_page.dart │ │ │ │ └── widgets │ │ │ │ │ └── sign_in_heatmap_widget.dart │ │ │ ├── splash │ │ │ │ └── splash_page.dart │ │ │ ├── subscriptions │ │ │ │ ├── controllers │ │ │ │ │ ├── media_list_controller.dart │ │ │ │ │ ├── subscription_image_repository.dart │ │ │ │ │ ├── subscription_post_repository.dart │ │ │ │ │ └── subscription_video_repository.dart │ │ │ │ ├── subscriptions_page.dart │ │ │ │ └── widgets │ │ │ │ │ ├── base_subscription_list.dart │ │ │ │ │ ├── compact_subscription_dropdown.dart │ │ │ │ │ ├── subscription_image_list.dart │ │ │ │ │ ├── subscription_post_list.dart │ │ │ │ │ ├── subscription_select_list_widget.dart │ │ │ │ │ └── subscription_video_list.dart │ │ │ ├── tag_blacklist │ │ │ │ ├── controllers │ │ │ │ │ └── tag_blacklist_controller.dart │ │ │ │ ├── tag_blacklist_page.dart │ │ │ │ └── widgets │ │ │ │ │ └── black_list_search_tag_dialog.dart │ │ │ └── video_detail │ │ │ │ ├── controllers │ │ │ │ ├── my_video_state_controller.dart │ │ │ │ └── related_media_controller.dart │ │ │ │ ├── video_detail_page_v2.dart │ │ │ │ └── widgets │ │ │ │ ├── comment_entry_area_widget.dart │ │ │ │ ├── detail │ │ │ │ ├── add_video_to_playlist_dialog.dart │ │ │ │ ├── expandable_tags_widget.dart │ │ │ │ ├── like_avatars_widget.dart │ │ │ │ ├── share_video_bottom_sheet.dart │ │ │ │ ├── video_description_widget.dart │ │ │ │ └── video_detail_content_widget.dart │ │ │ │ ├── media_tile_list_loading_widget.dart │ │ │ │ ├── player │ │ │ │ ├── bottom_toolbar_widget.dart │ │ │ │ ├── custom_slider_bar_shape_widget.dart │ │ │ │ ├── gesture_area_widget.dart │ │ │ │ ├── my_video_screen.dart │ │ │ │ ├── rapple_painter.dart │ │ │ │ └── top_toolbar_widget.dart │ │ │ │ ├── private_or_deleted_video_widget.dart │ │ │ │ ├── video_detail_info_skeleton_widget.dart │ │ │ │ ├── video_rating_animation.dart │ │ │ │ └── volume_control_widget.dart │ │ └── widgets │ │ │ ├── MDToastWidget.dart │ │ │ ├── add_to_favorite_dialog.dart │ │ │ ├── ai_translation_toggle_button.dart │ │ │ ├── avatar_widget.dart │ │ │ ├── base │ │ │ └── base_input_dialog.dart │ │ │ ├── base_card_list_item_widget.dart │ │ │ ├── common_header.dart │ │ │ ├── common_list_view_helper.dart │ │ │ ├── custom_markdown_body_widget.dart │ │ │ ├── empty_widget.dart │ │ │ ├── error_widget.dart │ │ │ ├── follow_button_widget.dart │ │ │ ├── friend_button_widget.dart │ │ │ ├── global_drawer_content_widget.dart │ │ │ ├── glow_notification_widget.dart │ │ │ ├── google_search_panel_widget.dart │ │ │ ├── lazy_indexed_stack.dart │ │ │ ├── like_button_widget.dart │ │ │ ├── link_input_dialog_widget.dart │ │ │ ├── loading_button_widget.dart │ │ │ ├── markdown_syntax_help_dialog.dart │ │ │ ├── markdown_translation_controller.dart │ │ │ ├── my_loading_more_indicator_widget.dart │ │ │ ├── privacy_over_lay_widget.dart │ │ │ ├── shimmer_card.dart │ │ │ ├── shimmer_loading_widget.dart │ │ │ ├── top_padding_height_widget.dart │ │ │ ├── translation_dialog_widget.dart │ │ │ ├── translation_language_selector.dart │ │ │ ├── translation_powered_by_widget.dart │ │ │ ├── user_card.dart │ │ │ ├── user_name_widget.dart │ │ │ └── window_layout_widget.dart │ └── utils │ │ ├── exit_confirm_util.dart │ │ ├── markdown_formatter.dart │ │ ├── media_layout_utils.dart │ │ └── url_utils.dart ├── common │ ├── constants.dart │ └── enums │ │ └── media_enums.dart ├── db │ ├── database_service.dart │ ├── migration_manager.dart │ ├── migrations │ │ ├── migration.dart │ │ ├── migration_v1.dart │ │ ├── migration_v2.dart │ │ ├── migration_v3.dart │ │ ├── migration_v4.dart │ │ ├── migration_v5.dart │ │ ├── migration_v6_config.dart │ │ ├── migration_v7_config_storage.dart │ │ └── migration_v8_disable_theater.dart │ └── sqlite3 │ │ ├── native.dart │ │ ├── sqlite3.dart │ │ ├── unsupported.dart │ │ └── web.dart ├── i18n │ ├── en.i18n.yaml │ ├── ja.i18n.yaml │ ├── strings.g.dart │ ├── strings_en.g.dart │ ├── strings_ja.g.dart │ ├── strings_zh_CN.g.dart │ ├── strings_zh_TW.g.dart │ ├── zh-CN.i18n.yaml │ └── zh-TW.i18n.yaml ├── main.dart └── utils │ ├── common_utils.dart │ ├── easy_throttle.dart │ ├── image_utils.dart │ ├── logger_utils.dart │ ├── platforms │ └── microsoft_windows_registry_utils.dart │ ├── proxy │ ├── proxy_util.dart │ ├── proxy_util_io.dart │ ├── proxy_util_stub.dart │ ├── proxy_util_web.dart │ └── system_proxy_settings.dart │ ├── vibrate_utils.dart │ ├── widget_extensions.dart │ └── x_version_calculator_utils.dart ├── linux ├── .gitignore ├── CMakeLists.txt ├── flutter │ ├── CMakeLists.txt │ ├── generated_plugin_registrant.cc │ ├── generated_plugin_registrant.h │ └── generated_plugins.cmake ├── main.cc ├── my_application.cc └── my_application.h ├── 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 ├── slang.yaml ├── update_logs.yaml ├── web ├── favicon.png ├── icons │ ├── Icon-192.png │ ├── Icon-512.png │ ├── Icon-maskable-192.png │ └── Icon-maskable-512.png ├── index.html ├── manifest.json └── sqlite3.wasm └── 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 /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report / 问题报告 3 | about: Create a report to help us improve / 创建报告以帮助我们改进 4 | title: '[Bug] ' 5 | labels: 'bug | 问题' 6 | assignees: '' 7 | --- 8 | 9 | ### Bug Description / 问题描述 10 | 11 | 12 | 13 | ### Steps To Reproduce / 复现步骤 14 | 1. 15 | 2. 16 | 3. 17 | 18 | ### Expected Behavior / 预期行为 19 | 20 | 21 | 22 | ### Actual Behavior / 实际行为 23 | 24 | 25 | 26 | ### Environment / 环境信息 27 | - OS / 操作系统: [e.g. Windows 10, macOS] 28 | - Browser / 浏览器: [e.g. Chrome, Safari] 29 | - Version / 版本: [e.g. 22] 30 | 31 | ### Additional Context / 其他信息 32 | 33 | 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request & Improvement / 功能请求和优化建议 3 | about: Suggest a new feature or improvement for this project / 为项目提出新功能或改进建议 4 | title: '[Feature] ' 5 | labels: 'enhancement | 功能增强' 6 | assignees: '' 7 | --- 8 | 9 | ### Type / 类型 10 | 11 | - [ ] New Feature Request / 新功能请求 12 | - [ ] Improvement for Existing Feature / 现有功能优化 13 | 14 | ### Description / 描述 15 | 16 | 17 | 18 | ### Use Case / 使用场景 19 | 20 | 21 | 22 | ### Current Solution / 当前解决方案 23 | 24 | 25 | 26 | ### Proposed Solution / 期望的解决方案 27 | 28 | 29 | 30 | ### Alternative Solutions / 替代方案 31 | 32 | 33 | 34 | ### Additional Context / 其他信息 35 | 36 | 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .build/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | .swiftpm/ 13 | migrate_working_dir/ 14 | 15 | # IntelliJ related 16 | *.iml 17 | *.ipr 18 | *.iws 19 | .idea/ 20 | 21 | # The .vscode folder contains launch configuration and tasks you configure in 22 | # VS Code which you may wish to be included in version control, so this line 23 | # is commented out by default. 24 | #.vscode/ 25 | 26 | # Flutter/Dart/Pub related 27 | **/doc/api/ 28 | **/ios/Flutter/.last_build_id 29 | .dart_tool/ 30 | .flutter-plugins 31 | .flutter-plugins-dependencies 32 | .pub-cache/ 33 | .pub/ 34 | /build/ 35 | 36 | # Symbolication related 37 | app.*.symbols 38 | 39 | # Obfuscation related 40 | app.*.map.json 41 | 42 | # Android Studio will place build artifacts here 43 | /android/app/debug 44 | /android/app/profile 45 | /android/app/release 46 | # build 47 | build/ 48 | -------------------------------------------------------------------------------- /.vscode/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 检查命令行参数 4 | platform=$1 5 | 6 | case $platform in 7 | "android") 8 | flutter build apk --release 9 | ;; 10 | "windows") 11 | flutter build windows --release 12 | ;; 13 | "macos") 14 | flutter build macos --release 15 | ;; 16 | "linux") 17 | flutter build linux --release 18 | ;; 19 | "web") 20 | flutter build web --release 21 | ;; 22 | "all") 23 | flutter build windows --release 24 | flutter build macos --release 25 | flutter build linux --release 26 | flutter build web --release 27 | flutter build apk --release 28 | ;; 29 | *) 30 | echo "Usage: ./build.sh [windows|macos|linux|web|all]" 31 | exit 1 32 | ;; 33 | esac 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 i_iwara 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /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 | analyzer: 12 | enable-experiment: 13 | - macros 14 | linter: 15 | # The lint rules applied to this project can be customized in the 16 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 17 | # included above or to enable additional rules. A list of all available lints 18 | # and their documentation is published at https://dart.dev/lints. 19 | # 20 | # Instead of disabling a lint rule for the entire project in the 21 | # section below, it can also be suppressed for a single line of code 22 | # or a specific dart file by using the `// ignore: name_of_lint` and 23 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 24 | # producing the lint. 25 | rules: 26 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 27 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 28 | 29 | # Additional information about this file can be found at 30 | # https://dart.dev/guides/language/analysis-options 31 | -------------------------------------------------------------------------------- /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/to/reference-keystore 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 | android { 8 | namespace = "m.c.g.a.i_iwara" 9 | compileSdk = flutter.compileSdkVersion 10 | ndkVersion "27.0.12077973" 11 | 12 | compileOptions { 13 | sourceCompatibility = JavaVersion.VERSION_21 14 | targetCompatibility = JavaVersion.VERSION_21 15 | } 16 | 17 | kotlinOptions { 18 | jvmTarget = JavaVersion.VERSION_21 19 | } 20 | 21 | defaultConfig { 22 | applicationId = "m.c.g.a.i_iwara" 23 | minSdk = 24 24 | targetSdk = flutter.targetSdkVersion 25 | versionCode = flutter.versionCode 26 | versionName = flutter.versionName 27 | } 28 | 29 | // 配置正式签名(正式构建时使用) 30 | signingConfigs { 31 | release { 32 | // 这里的路径应该与 GitHub Action 解码后存放的路径一致 33 | storeFile file("keystore.jks") 34 | storePassword System.getenv("KEYSTORE_PASSWORD") ?: project.findProperty("MY_KEYSTORE_PASSWORD") 35 | keyAlias System.getenv("KEY_ALIAS") ?: project.findProperty("MY_KEY_ALIAS") 36 | keyPassword System.getenv("KEY_PASSWORD") ?: project.findProperty("MY_KEY_PASSWORD") 37 | } 38 | } 39 | 40 | buildTypes { 41 | release { 42 | // 正式构建时启用正式签名 43 | signingConfig signingConfigs.release 44 | minifyEnabled false 45 | shrinkResources false 46 | } 47 | debug { 48 | signingConfig signingConfigs.debug 49 | } 50 | } 51 | 52 | splits { 53 | abi { 54 | enable true 55 | reset() 56 | include 'arm64-v8a', 'armeabi-v7a', 'x86_64' 57 | universalApk true 58 | } 59 | } 60 | 61 | packagingOptions { 62 | jniLibs { 63 | useLegacyPackaging = true 64 | } 65 | } 66 | } 67 | 68 | dependencies { 69 | implementation 'com.google.errorprone:error_prone_annotations:2.10.0' 70 | implementation 'com.google.code.findbugs:jsr305:3.0.2' 71 | } 72 | 73 | flutter { 74 | source = "../.." 75 | } 76 | -------------------------------------------------------------------------------- /android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # 已有的规则 2 | -keep class com.google.errorprone.annotations.** { *; } 3 | -keep class javax.annotation.** { *; } 4 | -dontwarn com.google.errorprone.annotations.** 5 | -dontwarn javax.annotation.** 6 | 7 | # 添加 media_kit 相关规则 8 | -keep class com.arthenica.ffmpegkit.** { *; } 9 | -keep class com.google.crypto.tink.** { *; } 10 | -dontwarn com.google.crypto.tink.** 11 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /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-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/launcher_icon.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/android/app/src/main/res/mipmap-hdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/android/app/src/main/res/mipmap-mdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ffffff 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 22 | 23 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | allprojects { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | } 7 | 8 | rootProject.buildDir = "../build" 9 | 10 | subprojects { 11 | project.buildDir = "${rootProject.buildDir}/${project.name}" 12 | project.evaluationDependsOn(":app") 13 | } 14 | 15 | tasks.register("clean", Delete) { 16 | delete rootProject.buildDir 17 | } 18 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | # ????????? JDK ???????????????? JDK 5 | MY_KEYSTORE_PASSWORD=${KEYSTORE_PASSWORD} 6 | MY_KEY_ALIAS=${KEY_ALIAS} 7 | MY_KEY_PASSWORD=${KEY_PASSWORD} 8 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip -------------------------------------------------------------------------------- /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 | 10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") 11 | 12 | repositories { 13 | google() 14 | mavenCentral() 15 | gradlePluginPortal() 16 | } 17 | } 18 | 19 | plugins { 20 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" 21 | id "com.android.application" version "8.7.3" apply false 22 | id "org.jetbrains.kotlin.android" version "2.0.21" apply false 23 | } 24 | 25 | include ":app" 26 | -------------------------------------------------------------------------------- /assets/icon/launcher_icon_v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/assets/icon/launcher_icon_v2.png -------------------------------------------------------------------------------- /assets/images/2.0x/flutter_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/assets/images/2.0x/flutter_logo.png -------------------------------------------------------------------------------- /assets/images/3.0x/flutter_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/assets/images/3.0x/flutter_logo.png -------------------------------------------------------------------------------- /assets/images/flutter_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/assets/images/flutter_logo.png -------------------------------------------------------------------------------- /assets/svg/app_enter_fullscreen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/svg/app_exit_fullscreen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/imgs/all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/docs/imgs/all.png -------------------------------------------------------------------------------- /docs/imgs/dingyue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/docs/imgs/dingyue.png -------------------------------------------------------------------------------- /docs/imgs/download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/docs/imgs/download.png -------------------------------------------------------------------------------- /docs/imgs/filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/docs/imgs/filter.png -------------------------------------------------------------------------------- /docs/imgs/gonggao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/docs/imgs/gonggao.png -------------------------------------------------------------------------------- /docs/imgs/huihua.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/docs/imgs/huihua.png -------------------------------------------------------------------------------- /docs/imgs/localshoucang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/docs/imgs/localshoucang.png -------------------------------------------------------------------------------- /docs/imgs/luntan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/docs/imgs/luntan.png -------------------------------------------------------------------------------- /docs/imgs/luntanxaingqing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/docs/imgs/luntanxaingqing.png -------------------------------------------------------------------------------- /docs/imgs/pinglun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/docs/imgs/pinglun.png -------------------------------------------------------------------------------- /docs/imgs/record.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/docs/imgs/record.png -------------------------------------------------------------------------------- /docs/imgs/shezhi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/docs/imgs/shezhi.png -------------------------------------------------------------------------------- /docs/imgs/shipin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/docs/imgs/shipin.png -------------------------------------------------------------------------------- /docs/imgs/shipin2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/docs/imgs/shipin2.png -------------------------------------------------------------------------------- /docs/imgs/shipinliebiao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/docs/imgs/shipinliebiao.png -------------------------------------------------------------------------------- /docs/imgs/sousuo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/docs/imgs/sousuo.png -------------------------------------------------------------------------------- /docs/imgs/tongzhi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/docs/imgs/tongzhi.png -------------------------------------------------------------------------------- /docs/imgs/tuku.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/docs/imgs/tuku.png -------------------------------------------------------------------------------- /docs/imgs/tukuliebiao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/docs/imgs/tukuliebiao.png -------------------------------------------------------------------------------- /docs/imgs/zuozhe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/docs/imgs/zuozhe.png -------------------------------------------------------------------------------- /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/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '12.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | target 'RunnerTests' do 36 | inherit! :search_paths 37 | end 38 | end 39 | 40 | post_install do |installer| 41 | installer.pods_project.targets.each do |target| 42 | flutter_additional_ios_build_settings(target) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /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/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | {"images":[{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@3x.png","scale":"3x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@3x.png","scale":"3x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@3x.png","scale":"3x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@1x.png","scale":"1x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@3x.png","scale":"3x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@1x.png","scale":"1x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@1x.png","scale":"1x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@1x.png","scale":"1x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@2x.png","scale":"2x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@1x.png","scale":"1x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@2x.png","scale":"2x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@1x.png","scale":"1x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@2x.png","scale":"2x"},{"size":"83.5x83.5","idiom":"ipad","filename":"Icon-App-83.5x83.5@2x.png","scale":"2x"},{"size":"1024x1024","idiom":"ios-marketing","filename":"Icon-App-1024x1024@1x.png","scale":"1x"}],"info":{"version":1,"author":"xcode"}} -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/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/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/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/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/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/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/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/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/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/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/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/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/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/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/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/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/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/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/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/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/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/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/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/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/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/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxSensei001/LoveIwara/60a821aa324ce506080136ffb85b171b8ae08b17/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/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/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/models/api_result.model.dart: -------------------------------------------------------------------------------- 1 | import 'package:i_iwara/i18n/strings.g.dart' as slang; 2 | 3 | /// Result 4 | class ApiResult { 5 | final String message; 6 | final int code; 7 | final T? data; 8 | 9 | bool get isSuccess => code == 200; // 改为通过状态码判断成功 10 | bool get isFail => !isSuccess; 11 | 12 | // 私有化构造函数 13 | ApiResult._(this.message, this.data, this.code); 14 | 15 | // 成功 16 | factory ApiResult.success({ 17 | String? message, 18 | T? data, 19 | int code = 200, 20 | String? custMessage 21 | }) { 22 | return ApiResult._(custMessage ?? message ?? slang.t.common.success, data, code); 23 | } 24 | 25 | // 失败 26 | factory ApiResult.fail(String message, {T? data, int code = 500}) { 27 | return ApiResult._(message, data, code); 28 | } 29 | 30 | @override 31 | String toString() { 32 | return 'ApiResult{message: $message, code: $code, data: $data, isSuccess: $isSuccess}'; 33 | } 34 | } 35 | 36 | // 测试结果模型 37 | class AITestResult { 38 | final String? rawResponse; 39 | final String? translatedText; 40 | final bool connectionValid; 41 | final String custMessage; 42 | 43 | AITestResult({ 44 | this.rawResponse, 45 | this.translatedText, 46 | this.connectionValid = false, 47 | required this.custMessage 48 | }); 49 | 50 | @override 51 | String toString() { 52 | return 'AITestResult{connectionValid: $connectionValid, custMessage: $custMessage, rawResponse: $rawResponse, translatedText: $translatedText}'; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/app/models/captcha.model.dart: -------------------------------------------------------------------------------- 1 | // models/captcha.model.dart 2 | class Captcha { 3 | final String id; 4 | final String data; // Base64 image data URI 5 | 6 | Captcha({required this.id, required this.data}); 7 | 8 | factory Captcha.fromJson(Map json) { 9 | return Captcha( 10 | id: json['id'], 11 | data: json['data'], 12 | ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/app/models/custom_thumbnail.model.dart: -------------------------------------------------------------------------------- 1 | class CustomThumbnail { 2 | final String id; 3 | final String type; 4 | final String path; 5 | final String name; 6 | final String mime; 7 | final int? size; 8 | final int? width; 9 | final int? height; 10 | final DateTime? createdAt; 11 | final DateTime? updatedAt; 12 | 13 | CustomThumbnail({ 14 | required this.id, 15 | required this.type, 16 | required this.path, 17 | required this.name, 18 | required this.mime, 19 | this.size, 20 | this.width, 21 | this.height, 22 | this.createdAt, 23 | this.updatedAt, 24 | }); 25 | 26 | factory CustomThumbnail.fromJson(Map json) { 27 | return CustomThumbnail( 28 | id: json['id'], 29 | type: json['type'], 30 | path: json['path'], 31 | name: json['name'], 32 | mime: json['mime'], 33 | size: json['size'] ?? 0, 34 | width: json['width'] ?? 0, 35 | height: json['height'] ?? 0, 36 | createdAt: json['createdAt'] == null ? null : DateTime.parse(json['createdAt']), 37 | updatedAt: json['updatedAt'] == null ? null : DateTime.parse(json['updatedAt']), 38 | ); 39 | } 40 | 41 | Map toJson() { 42 | return { 43 | 'id': id, 44 | 'type': type, 45 | 'path': path, 46 | 'name': name, 47 | 'mime': mime, 48 | 'size': size, 49 | 'width': width, 50 | 'height': height, 51 | 'createdAt': createdAt?.toIso8601String(), 52 | 'updatedAt': updatedAt?.toIso8601String(), 53 | }; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/app/models/dto/escape_intent.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class EscapeIntent extends Intent { 4 | const EscapeIntent(); 5 | } 6 | -------------------------------------------------------------------------------- /lib/app/models/dto/forum_thread_section_dto.dart: -------------------------------------------------------------------------------- 1 | /// 论坛帖子分类的DTO 2 | class ForumThreadSectionDto { 3 | final String id; // 分类id 4 | String name; // 分类名称 5 | String description; // 分类描述 6 | String group; // 组 7 | bool locked; // 是否锁定 8 | int numPosts; // 帖子数 9 | int numThreads; // 主题数 10 | 11 | ForumThreadSectionDto({ 12 | required this.id, 13 | this.name = '', 14 | this.description = '', 15 | this.group = '', 16 | this.locked = false, 17 | this.numPosts = 0, 18 | this.numThreads = 0, 19 | }); 20 | 21 | factory ForumThreadSectionDto.fromJson(Map json) { 22 | return ForumThreadSectionDto( 23 | id: json['id'], 24 | name: json['name'] ?? '', 25 | description: json['description'] ?? '', 26 | group: json['group'] ?? '', 27 | locked: json['locked'] ?? false, 28 | numPosts: json['numPosts'] ?? 0, 29 | numThreads: json['numThreads'] ?? 0, 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/app/models/dto/profile_user_dto.dart: -------------------------------------------------------------------------------- 1 | import 'package:i_iwara/app/models/tag.model.dart'; 2 | import 'package:i_iwara/app/models/user.model.dart'; 3 | 4 | class ProfileUserDto { 5 | final User? user; 6 | final Profile? profile; 7 | final List? tagBlacklist; 8 | final Notifications? notifications; 9 | final int? balance; 10 | 11 | ProfileUserDto({ 12 | this.user, 13 | this.profile, 14 | this.tagBlacklist, 15 | this.notifications, 16 | this.balance, 17 | }); 18 | 19 | factory ProfileUserDto.fromJson(Map json) { 20 | return ProfileUserDto( 21 | user: json['user'] != null ? User.fromJson(json['user']) : null, 22 | profile: json['profile'] != null ? Profile.fromJson(json['profile']) : null, 23 | tagBlacklist: json['tagBlacklist'] != null 24 | ? (json['tagBlacklist'] as List).map((i) => Tag.fromJson(i)).toList() 25 | : null, 26 | notifications: json['notifications'] != null 27 | ? Notifications.fromJson(json['notifications']) 28 | : null, 29 | balance: json['balance'], 30 | ); 31 | } 32 | } 33 | 34 | class Profile { 35 | final String? body; 36 | final String? header; 37 | final DateTime? createdAt; 38 | final DateTime? updatedAt; 39 | 40 | Profile({ 41 | this.body, 42 | this.header, 43 | this.createdAt, 44 | this.updatedAt, 45 | }); 46 | 47 | factory Profile.fromJson(Map json) { 48 | return Profile( 49 | body: json['body'], 50 | header: json['header'], 51 | createdAt: json['createdAt'] != null 52 | ? DateTime.parse(json['createdAt']) 53 | : null, 54 | updatedAt: json['updatedAt'] != null 55 | ? DateTime.parse(json['updatedAt']) 56 | : null, 57 | ); 58 | } 59 | } 60 | 61 | class Notifications { 62 | final bool? mention; 63 | final bool? reply; 64 | final bool? comment; 65 | 66 | Notifications({ 67 | this.mention, 68 | this.reply, 69 | this.comment, 70 | }); 71 | 72 | factory Notifications.fromJson(Map json) { 73 | return Notifications( 74 | mention: json['mention'], 75 | reply: json['reply'], 76 | comment: json['comment'], 77 | ); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /lib/app/models/dto/user_dto.dart: -------------------------------------------------------------------------------- 1 | class UserDTO { 2 | final String id; 3 | final String name; 4 | final String username; 5 | final String avatarUrl; 6 | DateTime? likedTime; 7 | 8 | UserDTO({ 9 | required this.id, 10 | required this.name, 11 | required this.username, 12 | required this.avatarUrl, 13 | this.likedTime, 14 | }); 15 | 16 | factory UserDTO.fromJson(Map json) { 17 | return UserDTO( 18 | id: json['id'], 19 | name: json['name'] ?? '', 20 | username: json['username'] ?? '', 21 | avatarUrl: json['avatarUrl'] ?? '', 22 | likedTime: json['likedTime'] != null ? DateTime.parse(json['likedTime']) : null, 23 | ); 24 | } 25 | 26 | Map toJson() { 27 | return { 28 | 'id': id, 29 | 'name': name, 30 | 'username': username, 31 | 'avatarUrl': avatarUrl, 32 | 'likedTime': likedTime?.toIso8601String(), 33 | }; 34 | } 35 | 36 | UserDTO copyWith({ 37 | String? name, 38 | String? username, 39 | String? avatarUrl, 40 | DateTime? likedTime, 41 | }) { 42 | return UserDTO( 43 | id: id, 44 | name: name ?? this.name, 45 | username: username ?? this.username, 46 | avatarUrl: avatarUrl ?? this.avatarUrl, 47 | likedTime: likedTime ?? this.likedTime, 48 | ); 49 | } 50 | } -------------------------------------------------------------------------------- /lib/app/models/dto/user_request_dto.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:i_iwara/app/models/user.model.dart'; 3 | 4 | /// 应用在 朋友请求列表 5 | /// target:如果target是自己,那么表示user是别人,别人请求加我 6 | /// target:如果target不是自己,那么表示user是我,我请求加别人 7 | class UserRequestDTO { 8 | final String id; 9 | final DateTime createdAt; 10 | final User target; // 目标用户, 11 | final User user; // 请求用户 12 | 13 | UserRequestDTO({ 14 | required this.id, 15 | required this.createdAt, 16 | required this.target, 17 | required this.user, 18 | }); 19 | 20 | factory UserRequestDTO.fromJson(Map json) { 21 | return UserRequestDTO( 22 | id: json['id'], 23 | createdAt: DateTime.parse(json['createdAt']), 24 | target: User.fromJson(json['target']), 25 | user: User.fromJson(json['user']), 26 | ); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /lib/app/models/favorite/favorite_folder.model.dart: -------------------------------------------------------------------------------- 1 | import 'package:uuid/uuid.dart'; 2 | 3 | class FavoriteFolder { 4 | final String id; 5 | final String title; 6 | final String? description; 7 | final DateTime createdAt; 8 | final DateTime updatedAt; 9 | final int? itemCount; // 收藏项数量,可选 10 | 11 | FavoriteFolder({ 12 | String? id, 13 | required this.title, 14 | this.description, 15 | DateTime? createdAt, 16 | DateTime? updatedAt, 17 | this.itemCount, 18 | }) : id = id ?? const Uuid().v4(), 19 | createdAt = createdAt ?? DateTime.now(), 20 | updatedAt = updatedAt ?? DateTime.now(); 21 | 22 | factory FavoriteFolder.fromJson(Map json) { 23 | return FavoriteFolder( 24 | id: json['id'], 25 | title: json['title'], 26 | description: json['description'], 27 | createdAt: json['created_at'] != null 28 | ? DateTime.fromMillisecondsSinceEpoch(json['created_at'] * 1000) 29 | : null, 30 | updatedAt: json['updated_at'] != null 31 | ? DateTime.fromMillisecondsSinceEpoch(json['updated_at'] * 1000) 32 | : null, 33 | itemCount: json['item_count'], 34 | ); 35 | } 36 | 37 | Map toJson() { 38 | return { 39 | 'id': id, 40 | 'title': title, 41 | 'description': description, 42 | 'created_at': createdAt.millisecondsSinceEpoch ~/ 1000, 43 | 'updated_at': updatedAt.millisecondsSinceEpoch ~/ 1000, 44 | 'item_count': itemCount, 45 | }; 46 | } 47 | 48 | FavoriteFolder copyWith({ 49 | String? id, 50 | String? title, 51 | String? description, 52 | DateTime? createdAt, 53 | DateTime? updatedAt, 54 | int? itemCount, 55 | }) { 56 | return FavoriteFolder( 57 | id: id ?? this.id, 58 | title: title ?? this.title, 59 | description: description ?? this.description, 60 | createdAt: createdAt ?? this.createdAt, 61 | updatedAt: updatedAt ?? this.updatedAt, 62 | itemCount: itemCount ?? this.itemCount, 63 | ); 64 | } 65 | } -------------------------------------------------------------------------------- /lib/app/models/light_play_list.model.dart: -------------------------------------------------------------------------------- 1 | class LightPlaylistModel { 2 | String id; 3 | String title; 4 | int numVideos; 5 | bool added; 6 | 7 | LightPlaylistModel({ 8 | required this.id, 9 | required this.title, 10 | required this.numVideos, 11 | required this.added, 12 | }); 13 | 14 | factory LightPlaylistModel.fromJson(Map json) { 15 | return LightPlaylistModel( 16 | id: json['id'], 17 | title: json['title'], 18 | numVideos: json['numVideos'], 19 | added: json['added'], 20 | ); 21 | } 22 | 23 | Map toJson() { 24 | return { 25 | 'id': id, 26 | 'title': title, 27 | 'numVideos': numVideos, 28 | 'added': added, 29 | }; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/app/models/page_data.model.dart: -------------------------------------------------------------------------------- 1 | class PageData { 2 | final int count; 3 | final int page; 4 | final int limit; 5 | List results = []; 6 | Map? extras; 7 | 8 | PageData({ 9 | required this.count, 10 | required this.page, 11 | required this.limit, 12 | required this.results, 13 | this.extras, 14 | }); 15 | 16 | PageData.fromJson(Map data) 17 | : count = data['count'], 18 | page = data['page'], 19 | limit = data['limit'], 20 | results = [], 21 | extras = Map.from(data) 22 | ..removeWhere((key, value) => 23 | ['count', 'page', 'limit', 'results'].contains(key)); 24 | 25 | /// 工厂构造函数,用于处理泛型类型的转换 26 | static PageData fromJsonWithConverter( 27 | Map data, 28 | T Function(Map) fromJsonT, 29 | ) { 30 | return PageData( 31 | count: data['count'], 32 | page: data['page'], 33 | limit: data['limit'], 34 | results: (data['results'] as List) 35 | .map((item) => fromJsonT(item as Map)) 36 | .toList(), 37 | extras: Map.from(data) 38 | ..removeWhere( 39 | (key, value) => ['count', 'page', 'limit', 'results'].contains(key)), 40 | ); 41 | } 42 | 43 | @override 44 | String toString() { 45 | return 'PageData{count: $count, page: $page, limit: $limit, results: ${results.length} items${extras != null ? ", extras: $extras" : ""}}'; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/app/models/play_list.model.dart: -------------------------------------------------------------------------------- 1 | import 'package:i_iwara/common/constants.dart'; 2 | 3 | class PlaylistModel { 4 | String id; 5 | String title; 6 | int numVideos; 7 | String thumbnailUrl; // 封面图,截至2024-11-29,目前iwara并没有真正的为playlist设置封面图,如果后续iwara添加了,可以在这里添加 8 | 9 | PlaylistModel({ 10 | required this.id, 11 | required this.title, 12 | required this.numVideos, 13 | required this.thumbnailUrl, 14 | }); 15 | 16 | factory PlaylistModel.fromJson(Map json) { 17 | return PlaylistModel( 18 | id: json['id'], 19 | title: json['title'], 20 | numVideos: json['numVideos'], 21 | thumbnailUrl: json['thumbnailUrl'] ?? CommonConstants.defaultPlaylistThumbnailUrl, 22 | ); 23 | } 24 | 25 | Map toJson() { 26 | return { 27 | 'id': id, 28 | 'title': title, 29 | 'numVideos': numVideos, 30 | 'thumbnailUrl': thumbnailUrl, 31 | }; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/app/models/post.model.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:i_iwara/app/models/user.model.dart'; 3 | 4 | class PostModel { 5 | final String id; 6 | final String title; 7 | final String body; 8 | final int? numViews; 9 | final DateTime createdAt; 10 | final DateTime updatedAt; 11 | final User user; 12 | 13 | 14 | PostModel({ 15 | required this.id, 16 | required this.title, 17 | required this.body, 18 | required this.numViews, 19 | required this.createdAt, 20 | required this.updatedAt, 21 | required this.user, 22 | }); 23 | 24 | factory PostModel.fromJson(Map json) { 25 | return PostModel( 26 | id: json['id'], 27 | title: json['title'], 28 | body: json['body'], 29 | numViews: json['numViews'], 30 | createdAt: DateTime.parse(json['createdAt']), 31 | updatedAt: DateTime.parse(json['updatedAt']), 32 | user: User.fromJson(json['user']), 33 | ); 34 | } 35 | 36 | Map toJson() { 37 | return { 38 | 'id': id, 39 | 'title': title, 40 | 'body': body, 41 | 'numViews': numViews, 42 | 'createdAt': createdAt.toIso8601String(), 43 | 'updatedAt': updatedAt.toIso8601String(), 44 | 'user': user.toJson(), 45 | }; 46 | } 47 | 48 | PostModel copyWith({ 49 | String? id, 50 | String? title, 51 | String? body, 52 | int? numViews, 53 | DateTime? createdAt, 54 | DateTime? updatedAt, 55 | User? user, 56 | }) => PostModel( 57 | id: id ?? this.id, 58 | title: title ?? this.title, 59 | body: body ?? this.body, 60 | numViews: numViews ?? this.numViews, 61 | createdAt: createdAt ?? this.createdAt, 62 | updatedAt: updatedAt ?? this.updatedAt, 63 | user: user ?? this.user, 64 | ); 65 | } 66 | 67 | class PostCooldownModel { 68 | final bool limited; 69 | final int remaining; 70 | 71 | PostCooldownModel({ 72 | required this.limited, 73 | required this.remaining, 74 | }); 75 | 76 | factory PostCooldownModel.fromJson(Map json) { 77 | return PostCooldownModel( 78 | limited: json['limited'], 79 | remaining: json['remaining'], 80 | ); 81 | } 82 | 83 | Map toJson() { 84 | return { 85 | 'limited': limited, 86 | 'remaining': remaining, 87 | }; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /lib/app/models/rules.model.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | 3 | class RulesModel { 4 | final String id; 5 | final int weight; // 权重 越小越靠前 6 | final Map title; // 标题 多语言 7 | final Map body; // 内容 使用markdown语法 {@link CustomMarkdownBody disableLinkClick: true} 8 | final String createdAt; // 创建时间 9 | final String updatedAt; // 更新时间 10 | 11 | RulesModel({ 12 | required this.id, 13 | required this.weight, 14 | required this.title, 15 | required this.body, 16 | required this.createdAt, 17 | required this.updatedAt, 18 | }); 19 | 20 | factory RulesModel.fromJson(Map json) { 21 | final Map titleMap = json['title'] as Map; 22 | final Map title = titleMap.map( 23 | (key, value) => MapEntry(key, value.toString()), 24 | ); 25 | 26 | final Map bodyMap = json['body'] as Map; 27 | final Map body = bodyMap.map( 28 | (key, value) => MapEntry(key, value.toString()), 29 | ); 30 | 31 | return RulesModel( 32 | id: json['id'], 33 | weight: json['weight'], 34 | title: title, 35 | body: body, 36 | createdAt: json['createdAt'], 37 | updatedAt: json['updatedAt'], 38 | ); 39 | } 40 | 41 | String getLocalizedTitle() { 42 | final languageCode = Get.deviceLocale?.languageCode ?? 'en'; 43 | // 如果当前语言存在对应的内容,则返回当前语言的内容 44 | if (title.containsKey(languageCode)) { 45 | return title[languageCode]!; 46 | } 47 | // 如果是中文但只有 zh,则返回 zh 48 | if (languageCode.startsWith('zh') && title.containsKey('zh')) { 49 | return title['zh']!; 50 | } 51 | // 如果都没有,返回英文内容,如果英文也没有,返回第一个可用的内容 52 | return title['en'] ?? title.values.first; 53 | } 54 | 55 | String getLocalizedBody() { 56 | final languageCode = Get.deviceLocale?.languageCode ?? 'en'; 57 | // 如果当前语言存在对应的内容,则返回当前语言的内容 58 | if (body.containsKey(languageCode)) { 59 | return body[languageCode]!; 60 | } 61 | // 如果是中文但只有 zh,则返回 zh 62 | if (languageCode.startsWith('zh') && body.containsKey('zh')) { 63 | return body['zh']!; 64 | } 65 | // 如果都没有,返回英文内容,如果英文也没有,返回第一个可用的内容 66 | return body['en'] ?? body.values.first; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/app/models/search_record.model.dart: -------------------------------------------------------------------------------- 1 | class SearchRecord { 2 | final String keyword; 3 | final DateTime lastUsedAt; 4 | int usedTimes; 5 | 6 | SearchRecord({ 7 | required this.keyword, 8 | required this.lastUsedAt, 9 | this.usedTimes = 1, 10 | }); 11 | 12 | Map toJson() => { 13 | 'keyword': keyword, 14 | 'lastUsedAt': lastUsedAt.toIso8601String(), 15 | 'usedTimes': usedTimes, 16 | }; 17 | 18 | factory SearchRecord.fromJson(Map json) => SearchRecord( 19 | keyword: json['keyword'], 20 | lastUsedAt: DateTime.parse(json['lastUsedAt']), 21 | usedTimes: json['usedTimes'], 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /lib/app/models/sort.model.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/material.dart'; 3 | 4 | import '../../common/constants.dart'; 5 | 6 | class Sort { 7 | final SortId id; 8 | final String label; 9 | final String extData; 10 | final StatelessWidget? icon; 11 | 12 | const Sort({required this.id, required this.label, this.icon, this.extData = ''}); 13 | } -------------------------------------------------------------------------------- /lib/app/models/tag.model.dart: -------------------------------------------------------------------------------- 1 | class Tag { 2 | final String id; 3 | final String type; 4 | final bool sensitive; 5 | 6 | Tag( 7 | {required this.id, 8 | required this.type, 9 | this.sensitive = false}); 10 | 11 | factory Tag.fromJson(Map json) { 12 | return Tag( 13 | id: json['id'], 14 | type: json['type'], 15 | sensitive: json['sensitive'] ?? false, 16 | ); 17 | } 18 | 19 | Map toJson() { 20 | return { 21 | 'id': id, 22 | 'type': type, 23 | 'sensitive': sensitive, 24 | }; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/app/models/theme_mode.model.dart: -------------------------------------------------------------------------------- 1 | enum AppThemeMode { 2 | system, // 跟随系统 3 | light, // 亮色模式 4 | dark, // 暗色模式 5 | } -------------------------------------------------------------------------------- /lib/app/models/update_info.model.dart: -------------------------------------------------------------------------------- 1 | class UpdateInfo { 2 | final String version; // 版本号 3 | final String date; // 更新日期 4 | final Map> changes; // 更新内容 5 | final String minVersion; // 最小版本号 6 | 7 | UpdateInfo({ 8 | required this.version, 9 | required this.date, 10 | required this.changes, 11 | this.minVersion = '0.0.1', 12 | }); 13 | 14 | factory UpdateInfo.fromYaml(Map yaml) { 15 | final changesMap = >{}; 16 | final yamlChanges = yaml['changes'] as Map; 17 | 18 | yamlChanges.forEach((key, value) { 19 | changesMap[key.toString()] = List.from(value); 20 | }); 21 | 22 | return UpdateInfo( 23 | version: yaml['version'] ?? '', 24 | date: yaml['date'] ?? '', 25 | changes: changesMap, 26 | minVersion: yaml['minVersion'] ?? '0.0.1', 27 | ); 28 | } 29 | 30 | List getLocalizedChanges(String currentLocale) { 31 | return changes[currentLocale] ?? changes['en'] ?? changes['zh-CN'] ?? changes['zh-TW'] ?? changes['ja'] ?? []; 32 | } 33 | } -------------------------------------------------------------------------------- /lib/app/models/user_avatar.model.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:i_iwara/common/constants.dart'; 3 | 4 | class UserAvatar { 5 | final String id; 6 | final String type; 7 | final String path; 8 | final String name; 9 | final String mime; 10 | final int? size; 11 | final int? width; 12 | final int? height; 13 | final DateTime createdAt; 14 | final DateTime updatedAt; 15 | 16 | UserAvatar({ 17 | required this.id, 18 | required this.type, 19 | required this.path, 20 | required this.name, 21 | required this.mime, 22 | required this.size, 23 | this.width, 24 | this.height, 25 | required this.createdAt, 26 | required this.updatedAt, 27 | }); 28 | 29 | factory UserAvatar.fromJson(Map json) { 30 | return UserAvatar( 31 | id: json['id'], 32 | type: json['type'], 33 | path: json['path'], 34 | name: json['name'], 35 | mime: json['mime'], 36 | size: json['size'], 37 | width: json['width'], 38 | height: json['height'], 39 | createdAt: json['createdAt'] == null ? DateTime.now() : DateTime.parse(json['createdAt']), 40 | updatedAt: json['updatedAt'] == null ? DateTime.now() : DateTime.parse(json['updatedAt']), 41 | ); 42 | } 43 | 44 | Map toJson() { 45 | final data = {}; 46 | data['id'] = id; 47 | data['type'] = type; 48 | data['path'] = path; 49 | data['name'] = name; 50 | data['mime'] = mime; 51 | data['size'] = size; 52 | data['width'] = width; 53 | data['height'] = height; 54 | data['createdAt'] = createdAt.toIso8601String(); 55 | data['updatedAt'] = updatedAt.toIso8601String(); 56 | return data; 57 | } 58 | 59 | String get avatarUrl { 60 | final isAnimated = mime == 'image/gif' || mime == 'image/webp' || mime == 'image/apng'; 61 | if (isAnimated) { 62 | return CommonConstants.avatarOriginalUrl(id, name); 63 | } 64 | return CommonConstants.avatarUrl(id, name); 65 | } 66 | } -------------------------------------------------------------------------------- /lib/app/models/user_notification_count.model.dart: -------------------------------------------------------------------------------- 1 | 2 | class UserNotificationCount { 3 | final int messages; // 消息 4 | final int notifications; // 通知 5 | final int friendRequests; // 好友请求 6 | 7 | UserNotificationCount({required this.messages, required this.notifications, required this.friendRequests}); 8 | 9 | factory UserNotificationCount.fromJson(Map json) { 10 | return UserNotificationCount( 11 | messages: json['messages'], 12 | notifications: json['notifications'], 13 | friendRequests: json['friendRequests'], 14 | ); 15 | } 16 | 17 | // toJson 18 | Map toJson() { 19 | return { 20 | 'messages': messages, 21 | 'notifications': notifications, 22 | 'friendRequests': friendRequests, 23 | }; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/app/models/video_source.model.dart: -------------------------------------------------------------------------------- 1 | // video_source.model.dart 2 | 3 | class VideoSource { 4 | final String id; 5 | final String? name; // 清晰度,例如 "360", "540", "720" 6 | final String? view; 7 | final String? download; 8 | final String? type; // 视频类型,例如 "video/mp4" 9 | final DateTime? createdAt; 10 | final DateTime? updatedAt; 11 | final VideoSrc? src; 12 | 13 | VideoSource({ 14 | required this.id, 15 | this.name, 16 | this.view, 17 | this.download, 18 | this.type, 19 | this.createdAt, 20 | this.updatedAt, 21 | this.src, 22 | }); 23 | 24 | factory VideoSource.fromJson(Map json) { 25 | return VideoSource( 26 | id: json['id'], 27 | name: json['name'], 28 | view: json['src'] != null ? 'https:${json['src']['view']}' : null, 29 | download: 30 | json['src'] != null ? 'https:${json['src']['download']}' : null, 31 | type: json['type'], 32 | createdAt: 33 | json['createdAt'] != null ? DateTime.parse(json['createdAt']) : null, 34 | updatedAt: 35 | json['updatedAt'] != null ? DateTime.parse(json['updatedAt']) : null, 36 | src: json['src'] != null ? VideoSrc.fromJson(json['src']) : null, 37 | ); 38 | } 39 | 40 | Map toJson() { 41 | return { 42 | 'id': id, 43 | 'name': name, 44 | 'type': type, 45 | 'createdAt': createdAt?.toIso8601String(), 46 | 'updatedAt': updatedAt?.toIso8601String(), 47 | 'src': src?.toJson(), 48 | }; 49 | } 50 | } 51 | 52 | class VideoSrc { 53 | final String? view; 54 | final String? download; 55 | 56 | VideoSrc({ 57 | this.view, // 视频播放源 58 | this.download, 59 | }); 60 | 61 | factory VideoSrc.fromJson(Map json) { 62 | return VideoSrc( 63 | view: json['view'], // 视频播放源 64 | download: json['download'], 65 | ); 66 | } 67 | 68 | Map toJson() { 69 | return { 70 | 'view': view, 71 | 'download': download, 72 | }; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /lib/app/repositories/base_repository.dart: -------------------------------------------------------------------------------- 1 | 2 | import '../../db/database_service.dart'; 3 | 4 | abstract class BaseRepository { 5 | final DatabaseService databaseService = DatabaseService(); 6 | 7 | BaseRepository(); 8 | } -------------------------------------------------------------------------------- /lib/app/repositories/commons_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:i_iwara/utils/logger_utils.dart'; 2 | import 'base_repository.dart'; 3 | 4 | class CommonsRepository extends BaseRepository { 5 | CommonsRepository._privateConstructor(); 6 | static final CommonsRepository instance = CommonsRepository._privateConstructor(); 7 | 8 | // 获取配置 9 | Future getData(String key) async { 10 | try { 11 | final db = databaseService.database; 12 | final result = db.select( 13 | 'SELECT data FROM commons WHERE key = ?', 14 | [key], 15 | ); 16 | if (result.isNotEmpty) { 17 | return result.first['data'] as String; 18 | } 19 | return null; 20 | } catch (e) { 21 | LogUtils.e('获取配置数据时出错', error: e); 22 | rethrow; 23 | } 24 | } 25 | 26 | // 保存配置 27 | Future setData(String key, String data) async { 28 | try { 29 | final db = databaseService.database; 30 | db.execute( 31 | ''' 32 | INSERT INTO commons(key, data) 33 | VALUES(?, ?) 34 | ON CONFLICT(key) DO UPDATE SET 35 | data = excluded.data 36 | ''', 37 | [key, data], 38 | ); 39 | } catch (e) { 40 | LogUtils.e('保存配置数据时出错', error: e); 41 | rethrow; 42 | } 43 | } 44 | 45 | // 删除配置 46 | Future deleteData(String key) async { 47 | try { 48 | final db = databaseService.database; 49 | db.execute( 50 | 'DELETE FROM commons WHERE key = ?', 51 | [key], 52 | ); 53 | } catch (e) { 54 | LogUtils.e('删除配置数据时出错', error: e); 55 | rethrow; 56 | } 57 | } 58 | 59 | // 获取所有配置 60 | Future> getAllData() async { 61 | try { 62 | final db = databaseService.database; 63 | final result = db.select('SELECT key, data FROM commons'); 64 | return Map.fromEntries( 65 | result.map((row) => MapEntry( 66 | row['key'] as String, 67 | row['data'] as String, 68 | )), 69 | ); 70 | } catch (e) { 71 | LogUtils.e('获取所有配置数据时出错', error: e); 72 | rethrow; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lib/app/services/message_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | import 'package:i_iwara/app/ui/widgets/MDToastWidget.dart'; 3 | import 'package:oktoast/oktoast.dart'; 4 | 5 | class MessageService extends GetxService { 6 | final List<_QueuedMessage> _messageQueue = []; 7 | bool _isReady = false; 8 | 9 | void markReady() { 10 | _isReady = true; 11 | _processQueue(); 12 | } 13 | 14 | void showMessage(String message, MDToastType type) { 15 | if (!_isReady) { 16 | _messageQueue.add(_QueuedMessage(message, type)); 17 | } else { 18 | _showToast(message, type); 19 | } 20 | } 21 | 22 | void _processQueue() { 23 | if (!_isReady) return; 24 | 25 | for (var message in _messageQueue) { 26 | _showToast(message.message, message.type); 27 | } 28 | _messageQueue.clear(); 29 | } 30 | 31 | void _showToast(String message, MDToastType type) { 32 | showToastWidget( 33 | MDToastWidget( 34 | message: message, 35 | type: type, 36 | ), 37 | ); 38 | } 39 | } 40 | 41 | class _QueuedMessage { 42 | final String message; 43 | final MDToastType type; 44 | 45 | _QueuedMessage(this.message, this.type); 46 | } -------------------------------------------------------------------------------- /lib/app/services/tag_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | import 'package:i_iwara/common/constants.dart'; 3 | import 'package:i_iwara/i18n/strings.g.dart'; 4 | import 'package:i_iwara/utils/logger_utils.dart'; 5 | 6 | import '../models/api_result.model.dart'; 7 | import '../models/page_data.model.dart'; 8 | import '../models/tag.model.dart'; 9 | import 'api_service.dart'; 10 | 11 | class TagService extends GetxService { 12 | final ApiService _apiService = Get.find(); 13 | 14 | /// 获取标签列表。 15 | /// [params] 一个用于过滤标签的查询参数映射。 16 | /// - filter 搜索关键词。 17 | Future>> fetchTags({ 18 | Map params = const {}, 19 | int page = 0, 20 | int limit = 20, 21 | }) async { 22 | try { 23 | var response = await _apiService.get(ApiConstants.tags(), queryParameters: { 24 | ...params, 25 | 'page': page, 26 | 'limit': limit, 27 | }); 28 | 29 | final List results = (response.data['results'] as List) 30 | .map((tag) => Tag.fromJson(tag)) 31 | .toList(); 32 | 33 | final PageData pageData = PageData( 34 | page: response.data['page'], 35 | limit: response.data['limit'], 36 | count: response.data['count'], 37 | results: results, 38 | ); 39 | 40 | return ApiResult.success(data: pageData); 41 | } catch (e) { 42 | LogUtils.e('获取标签列表失败', tag: 'TagService', error: e); 43 | return ApiResult.fail(t.errors.failedToFetchData); 44 | } 45 | 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/app/ui/pages/author_profile/controllers/userz_post_list_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | import 'package:i_iwara/app/models/post.model.dart'; 3 | import 'package:i_iwara/app/services/post_service.dart'; 4 | import 'package:i_iwara/utils/logger_utils.dart'; 5 | import 'package:loading_more_list/loading_more_list.dart'; 6 | 7 | class UserzPostListRepository extends LoadingMoreBase { 8 | final PostService _postService = Get.find(); 9 | final String userId; 10 | UserzPostListRepository({required this.userId}); 11 | 12 | int _pageIndex = 0; 13 | bool _hasMore = true; 14 | bool forceRefresh = false; 15 | 16 | @override 17 | bool get hasMore => _hasMore || forceRefresh; 18 | 19 | @override 20 | Future refresh([bool notifyStateChanged = false]) async { 21 | _hasMore = true; 22 | _pageIndex = 0; 23 | forceRefresh = !notifyStateChanged; 24 | final bool result = await super.refresh(notifyStateChanged); 25 | forceRefresh = false; 26 | return result; 27 | } 28 | 29 | @override 30 | Future loadData([bool isLoadMoreAction = false]) async { 31 | bool isSuccess = false; 32 | try { 33 | final result = await _postService.fetchUserPostList( 34 | userId, 35 | page: _pageIndex, 36 | limit: 10, 37 | ); 38 | 39 | if (!result.isSuccess) { 40 | throw Exception(result.message); 41 | } 42 | 43 | if (_pageIndex == 0) { 44 | clear(); 45 | } 46 | 47 | final posts = result.data?.results ?? []; 48 | for (final post in posts) { 49 | add(post); 50 | } 51 | 52 | _hasMore = posts.isNotEmpty; 53 | _pageIndex++; 54 | isSuccess = true; 55 | } catch (exception, stack) { 56 | isSuccess = false; 57 | LogUtils.e('加载作者帖子列表失败', 58 | error: exception, stack: stack, tag: 'UserzPostListRepository'); 59 | } 60 | return isSuccess; 61 | } 62 | } -------------------------------------------------------------------------------- /lib/app/ui/pages/conversation/controllers/conversation_list_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | import 'package:i_iwara/app/models/message_and_conversation.model.dart'; 3 | import 'package:i_iwara/app/services/conversation_service.dart'; 4 | import 'package:i_iwara/app/services/user_service.dart'; 5 | import 'package:i_iwara/i18n/strings.g.dart'; 6 | import 'package:i_iwara/utils/logger_utils.dart'; 7 | import 'package:loading_more_list/loading_more_list.dart'; 8 | 9 | class ConversationListRepository extends LoadingMoreBase { 10 | final ConversationService _conversationService = Get.find(); 11 | final UserService _userService = Get.find(); 12 | 13 | int _pageIndex = 0; 14 | bool _hasMore = true; 15 | bool forceRefresh = false; 16 | @override 17 | bool get hasMore => _hasMore || forceRefresh; 18 | 19 | ConversationListRepository(); 20 | 21 | @override 22 | Future refresh([bool notifyStateChanged = false]) async { 23 | _hasMore = true; 24 | _pageIndex = 0; 25 | forceRefresh = !notifyStateChanged; 26 | final bool result = await super.refresh(notifyStateChanged); 27 | forceRefresh = false; 28 | return result; 29 | } 30 | 31 | @override 32 | Future loadData([bool isLoadMoreAction = false]) async { 33 | bool isSuccess = false; 34 | try { 35 | final userId = _userService.currentUser.value?.id; 36 | if (userId == null) { 37 | throw Exception(t.errors.pleaseLoginFirst); 38 | } 39 | 40 | final result = await _conversationService.getConversations( 41 | userId, 42 | page: _pageIndex, 43 | limit: 20, 44 | ); 45 | 46 | if (!result.isSuccess) { 47 | throw Exception(result.message); 48 | } 49 | 50 | if (_pageIndex == 0) { 51 | clear(); 52 | } 53 | 54 | final pageData = result.data!; 55 | for (final conversation in pageData.results) { 56 | add(conversation); 57 | } 58 | 59 | _hasMore = pageData.results.length >= 20; 60 | _pageIndex++; 61 | isSuccess = true; 62 | } catch (exception, stack) { 63 | isSuccess = false; 64 | LogUtils.e('加载会话列表失败', 65 | error: exception, stack: stack, tag: 'ConversationListRepository'); 66 | } 67 | return isSuccess; 68 | } 69 | } -------------------------------------------------------------------------------- /lib/app/ui/pages/download/widgets/status_label_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:i_iwara/app/models/download/download_task.model.dart'; 3 | 4 | class StatusLabel extends StatelessWidget { 5 | final DownloadStatus status; 6 | final String text; 7 | 8 | const StatusLabel({ 9 | super.key, 10 | this.status = DownloadStatus.downloading, 11 | this.text = '', 12 | }); 13 | 14 | Color _getStatusColor() { 15 | switch (status) { 16 | case DownloadStatus.failed: 17 | return Colors.red; 18 | case DownloadStatus.completed: 19 | return Colors.green; 20 | case DownloadStatus.paused: 21 | return Colors.orange; 22 | default: 23 | return Colors.blue; 24 | } 25 | } 26 | 27 | @override 28 | Widget build(BuildContext context) { 29 | final statusColor = _getStatusColor(); 30 | if (text.isEmpty) { 31 | return const SizedBox.shrink(); 32 | } 33 | 34 | return Row( 35 | mainAxisSize: MainAxisSize.min, 36 | children: [ 37 | Container( 38 | padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), 39 | decoration: BoxDecoration( 40 | color: statusColor.withOpacity(0.1), 41 | borderRadius: BorderRadius.circular(4), 42 | ), 43 | child: Text( 44 | text, 45 | maxLines: 1, 46 | overflow: TextOverflow.ellipsis, 47 | style: TextStyle( 48 | fontSize: 12, 49 | color: statusColor, 50 | ), 51 | ), 52 | ), 53 | ], 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/app/ui/pages/favorites/repositories/favorite_image_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | import 'package:i_iwara/app/models/image.model.dart'; 3 | import 'package:i_iwara/app/services/gallery_service.dart'; 4 | import 'package:loading_more_list/loading_more_list.dart'; 5 | 6 | class FavoriteImageRepository extends LoadingMoreBase { 7 | final GalleryService _galleryService = Get.find(); 8 | 9 | int _pageIndex = 0; 10 | bool _hasMore = true; 11 | bool forceRefresh = false; 12 | 13 | @override 14 | bool get hasMore => _hasMore || forceRefresh; 15 | 16 | @override 17 | Future refresh([bool notifyStateChanged = false]) async { 18 | _hasMore = true; 19 | _pageIndex = 0; 20 | forceRefresh = !notifyStateChanged; 21 | final bool result = await super.refresh(notifyStateChanged); 22 | forceRefresh = false; 23 | return result; 24 | } 25 | 26 | @override 27 | Future loadData([bool isLoadMoreAction = false]) async { 28 | bool isSuccess = false; 29 | try { 30 | final result = await _galleryService.fetchFavoriteImages( 31 | page: _pageIndex, 32 | ); 33 | 34 | if (result.isSuccess && result.data != null) { 35 | if (_pageIndex == 0) { 36 | clear(); 37 | } 38 | 39 | for (final image in result.data!.results) { 40 | add(image); 41 | } 42 | 43 | _hasMore = result.data!.results.isNotEmpty; 44 | _pageIndex++; 45 | isSuccess = true; 46 | } 47 | } catch (e) { 48 | isSuccess = false; 49 | } 50 | return isSuccess; 51 | } 52 | } -------------------------------------------------------------------------------- /lib/app/ui/pages/favorites/repositories/favorite_video_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | import 'package:i_iwara/app/models/video.model.dart'; 3 | import 'package:i_iwara/app/services/video_service.dart'; 4 | import 'package:loading_more_list/loading_more_list.dart'; 5 | 6 | class FavoriteVideoRepository extends LoadingMoreBase