├── .github ├── ISSUE_TEMPLATE │ ├── bug-反馈.md │ └── 功能请求.md └── workflows │ ├── CI.yml │ └── main.yml ├── .gitignore ├── .metadata ├── .vscode ├── launch.json └── settings.json ├── LICENSE ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── ic_launcher-playstore.png │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── guozhigq │ │ │ │ └── pilipala │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-anydpi-v24 │ │ │ └── ic_notification_icon.xml │ │ │ ├── drawable-hdpi │ │ │ └── ic_notification_icon.png │ │ │ ├── drawable-mdpi │ │ │ └── ic_notification_icon.png │ │ │ ├── drawable-xhdpi │ │ │ └── ic_notification_icon.png │ │ │ ├── drawable-xxhdpi │ │ │ └── ic_notification_icon.png │ │ │ ├── drawable │ │ │ ├── ic_baseline_forward_10_24.xml │ │ │ ├── ic_baseline_replay_10_24.xml │ │ │ ├── ic_launcher_background.xml │ │ │ ├── ic_launcher_foreground.xml │ │ │ ├── ic_notification_icon.xml │ │ │ └── launch_background.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── raw │ │ │ └── keep.xml │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ └── values │ │ │ ├── colors.xml │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets ├── fonts │ ├── Jura-Bold.ttf │ └── fansCard.ttf ├── images │ ├── ai.png │ ├── big-vip.png │ ├── dm.svg │ ├── dm_gray.png │ ├── dm_white.png │ ├── error.svg │ ├── live.gif │ ├── live.png │ ├── live │ │ └── default_bg.webp │ ├── loading.gif │ ├── loading.png │ ├── logo │ │ ├── logo_android.png │ │ ├── logo_android_2.png │ │ └── logo_ios.png │ ├── lv │ │ ├── lv0.png │ │ ├── lv1.png │ │ ├── lv2.png │ │ ├── lv3.png │ │ ├── lv4.png │ │ ├── lv5.png │ │ └── lv6.png │ ├── noface.jpeg │ ├── play.png │ ├── play.svg │ ├── run-pokemon.gif │ ├── tv.svg │ ├── up.svg │ ├── up_gray.png │ ├── view.svg │ ├── view_gray.png │ └── view_white.png └── screenshots │ ├── 174shots_so.png │ ├── 510shots_so.png │ ├── 850shots_so.png │ ├── bangumi.png │ ├── bangumi_detail.png │ ├── dynamic.png │ ├── home.png │ ├── main_screen.png │ ├── media.png │ ├── member.png │ ├── search.png │ └── set.png ├── change_log ├── 1.0.0.0817.md ├── 1.0.1.0817.md ├── 1.0.10.1016.md ├── 1.0.11.1112.md ├── 1.0.12.1114.md ├── 1.0.13.1217.md ├── 1.0.14.1225.md ├── 1.0.15.0101.md ├── 1.0.16.0102.md ├── 1.0.17.0125.md ├── 1.0.18.0130.md ├── 1.0.19.0131.md ├── 1.0.2.0819.md ├── 1.0.3.0821.md ├── 1.0.4.0822.md ├── 1.0.5.0826.md ├── 1.0.6.0902.md ├── 1.0.7.0908.md ├── 1.0.8.0917.md └── 1.0.9.1015.md ├── fastlane └── metadata │ └── android │ ├── en-US │ ├── full_description.txt │ ├── images │ │ ├── featureGraphic.png │ │ ├── icon.png │ │ └── phoneScreenshots │ │ │ ├── 1.png │ │ │ ├── 2.png │ │ │ └── 3.png │ ├── short_description.txt │ └── title.txt │ └── zh-CN │ ├── changelogs │ └── 2001.txt │ ├── full_description.txt │ ├── images │ ├── featureGraphic.png │ ├── icon.png │ └── phoneScreenshots │ │ ├── 1.png │ │ ├── 2.png │ │ └── 3.png │ ├── short_description.txt │ └── title.txt ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── 100.png │ │ ├── 1024.png │ │ ├── 114.png │ │ ├── 120.png │ │ ├── 144.png │ │ ├── 152.png │ │ ├── 167.png │ │ ├── 180.png │ │ ├── 20.png │ │ ├── 29.png │ │ ├── 40.png │ │ ├── 50.png │ │ ├── 57.png │ │ ├── 58.png │ │ ├── 60.png │ │ ├── 72.png │ │ ├── 76.png │ │ ├── 80.png │ │ ├── 87.png │ │ └── Contents.json │ └── 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 ├── lib ├── common │ ├── constants.dart │ ├── skeleton │ │ ├── dynamic_card.dart │ │ ├── media_bangumi.dart │ │ ├── skeleton.dart │ │ ├── video_card_h.dart │ │ ├── video_card_v.dart │ │ └── video_reply.dart │ └── widgets │ │ ├── animated_dialog.dart │ │ ├── app_expansion_panel_list.dart │ │ ├── appbar.dart │ │ ├── badge.dart │ │ ├── content_container.dart │ │ ├── custom_toast.dart │ │ ├── html_render.dart │ │ ├── http_error.dart │ │ ├── live_card.dart │ │ ├── network_img_layer.dart │ │ ├── no_data.dart │ │ ├── overlay_pop.dart │ │ ├── pull_to_refresh_header.dart │ │ ├── sliver_header.dart │ │ ├── stat │ │ ├── danmu.dart │ │ └── view.dart │ │ ├── video_card_h.dart │ │ ├── video_card_v.dart │ │ └── video_popup_menu.dart ├── http │ ├── api.dart │ ├── bangumi.dart │ ├── black.dart │ ├── common.dart │ ├── constants.dart │ ├── danmaku.dart │ ├── dynamics.dart │ ├── fan.dart │ ├── follow.dart │ ├── html.dart │ ├── index.dart │ ├── init.dart │ ├── interceptor.dart │ ├── interceptor_anonymity.dart │ ├── live.dart │ ├── login.dart │ ├── member.dart │ ├── msg.dart │ ├── reply.dart │ ├── search.dart │ ├── user.dart │ └── video.dart ├── main.dart ├── models │ ├── bangumi │ │ ├── info.dart │ │ └── list.dart │ ├── common │ │ ├── business_type.dart │ │ ├── color_type.dart │ │ ├── dynamic_badge_mode.dart │ │ ├── dynamics_type.dart │ │ ├── rcmd_type.dart │ │ ├── reply_sort_type.dart │ │ ├── reply_type.dart │ │ ├── search_type.dart │ │ ├── tab_type.dart │ │ └── theme_type.dart │ ├── danmaku │ │ ├── dm.pb.dart │ │ ├── dm.pbenum.dart │ │ ├── dm.pbjson.dart │ │ ├── dm.pbserver.dart │ │ └── dm.proto │ ├── dynamics │ │ ├── result.dart │ │ └── up.dart │ ├── fans │ │ └── result.dart │ ├── follow │ │ └── result.dart │ ├── github │ │ └── latest.dart │ ├── home │ │ └── rcmd │ │ │ └── result.dart │ ├── live │ │ ├── item.dart │ │ ├── room_info.dart │ │ └── room_info_h5.dart │ ├── login │ │ └── index.dart │ ├── member │ │ ├── archive.dart │ │ ├── coin.dart │ │ ├── info.dart │ │ ├── seasons.dart │ │ └── tags.dart │ ├── model_hot_video_item.dart │ ├── model_owner.dart │ ├── model_owner.g.dart │ ├── model_rec_video_item.dart │ ├── model_rec_video_item.g.dart │ ├── msg │ │ ├── account.dart │ │ ├── msgfeed_at_me.dart │ │ ├── msgfeed_like_me.dart │ │ ├── msgfeed_reply_me.dart │ │ ├── msgfeed_unread.dart │ │ └── session.dart │ ├── search │ │ ├── hot.dart │ │ ├── hot.g.dart │ │ ├── result.dart │ │ └── suggest.dart │ ├── user │ │ ├── black.dart │ │ ├── fav_detail.dart │ │ ├── fav_folder.dart │ │ ├── history.dart │ │ ├── info.dart │ │ ├── info.g.dart │ │ ├── stat.dart │ │ └── stat.g.dart │ ├── video │ │ ├── ai.dart │ │ ├── play │ │ │ ├── quality.dart │ │ │ └── url.dart │ │ └── reply │ │ │ ├── config.dart │ │ │ ├── content.dart │ │ │ ├── data.dart │ │ │ ├── item.dart │ │ │ ├── member.dart │ │ │ ├── page.dart │ │ │ ├── top_replies.dart │ │ │ └── upper.dart │ └── video_detail_res.dart ├── pages │ ├── about │ │ └── index.dart │ ├── bangumi │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── introduction │ │ │ ├── controller.dart │ │ │ ├── index.dart │ │ │ ├── view.dart │ │ │ └── widgets │ │ │ │ └── intro_detail.dart │ │ ├── view.dart │ │ └── widgets │ │ │ ├── bangumi_panel.dart │ │ │ └── bangumu_card_v.dart │ ├── blacklist │ │ └── index.dart │ ├── danmaku │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── dynamics │ │ ├── controller.dart │ │ ├── detail │ │ │ ├── controller.dart │ │ │ ├── index.dart │ │ │ └── view.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widgets │ │ │ ├── action_panel.dart │ │ │ ├── additional_panel.dart │ │ │ ├── article_panel.dart │ │ │ ├── author_panel.dart │ │ │ ├── content_panel.dart │ │ │ ├── dynamic_panel.dart │ │ │ ├── forward_panel.dart │ │ │ ├── live_panel.dart │ │ │ ├── live_rcmd_panel.dart │ │ │ ├── pic_panel.dart │ │ │ ├── rich_node_panel.dart │ │ │ ├── up_panel.dart │ │ │ └── video_panel.dart │ ├── fan │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widgets │ │ │ └── fan_item.dart │ ├── fav │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widgets │ │ │ └── item.dart │ ├── fav_detail │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widget │ │ │ └── fav_video_card.dart │ ├── fav_search │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── follow │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widgets │ │ │ ├── follow_item.dart │ │ │ ├── follow_list.dart │ │ │ └── owner_follow_list.dart │ ├── follow_search │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── history │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widgets │ │ │ └── item.dart │ ├── history_search │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── home │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widgets │ │ │ └── app_bar.dart │ ├── hot │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── html │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── later │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── live │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widgets │ │ │ └── live_item.dart │ ├── live_room │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widgets │ │ │ └── bottom_control.dart │ ├── login │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── main │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── media │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── member │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widgets │ │ │ ├── conis.dart │ │ │ ├── profile.dart │ │ │ └── seasons.dart │ ├── member_archive │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── member_coin │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widgets │ │ │ └── item.dart │ ├── member_dynamics │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── member_like │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── member_search │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── member_seasons │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widgets │ │ │ └── item.dart │ ├── mine │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── msg_feed_top │ │ ├── at_me │ │ │ ├── controller.dart │ │ │ ├── index.dart │ │ │ └── view.dart │ │ ├── like_me │ │ │ ├── controller.dart │ │ │ ├── index.dart │ │ │ └── view.dart │ │ └── reply_me │ │ │ ├── controller.dart │ │ │ ├── index.dart │ │ │ └── view.dart │ ├── preview │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── rcmd │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── search │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widgets │ │ │ ├── hot_keyword.dart │ │ │ └── search_text.dart │ ├── search_panel │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widgets │ │ │ ├── article_panel.dart │ │ │ ├── live_panel.dart │ │ │ ├── media_bangumi_panel.dart │ │ │ ├── user_panel.dart │ │ │ └── video_panel.dart │ ├── search_result │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── setting │ │ ├── controller.dart │ │ ├── extra_setting.dart │ │ ├── hidden_settings.dart │ │ ├── index.dart │ │ ├── pages │ │ │ ├── color_select.dart │ │ │ ├── display_mode.dart │ │ │ ├── font_size_select.dart │ │ │ ├── home_tabbar_set.dart │ │ │ ├── logs.dart │ │ │ └── play_speed_set.dart │ │ ├── play_setting.dart │ │ ├── privacy_setting.dart │ │ ├── recommend_setting.dart │ │ ├── style_setting.dart │ │ ├── view.dart │ │ └── widgets │ │ │ ├── select_dialog.dart │ │ │ ├── select_item.dart │ │ │ ├── slide_dialog.dart │ │ │ └── switch_item.dart │ ├── video │ │ ├── README.md │ │ └── detail │ │ │ ├── controller.dart │ │ │ ├── index.dart │ │ │ ├── introduction │ │ │ ├── controller.dart │ │ │ ├── index.dart │ │ │ ├── view.dart │ │ │ └── widgets │ │ │ │ ├── action_item.dart │ │ │ │ ├── action_row_item.dart │ │ │ │ ├── fav_panel.dart │ │ │ │ ├── group_panel.dart │ │ │ │ ├── intro_detail.dart │ │ │ │ ├── menu_row.dart │ │ │ │ ├── page.dart │ │ │ │ └── season.dart │ │ │ ├── related │ │ │ ├── controller.dart │ │ │ ├── index.dart │ │ │ └── view.dart │ │ │ ├── reply │ │ │ ├── controller.dart │ │ │ ├── index.dart │ │ │ ├── view.dart │ │ │ └── widgets │ │ │ │ ├── reply_item.dart │ │ │ │ └── zan.dart │ │ │ ├── reply_new │ │ │ ├── index.dart │ │ │ └── view.dart │ │ │ ├── reply_reply │ │ │ ├── controller.dart │ │ │ ├── index.dart │ │ │ └── view.dart │ │ │ ├── view.dart │ │ │ └── widgets │ │ │ ├── ai_detail.dart │ │ │ ├── app_bar.dart │ │ │ ├── expandable_section.dart │ │ │ └── header_control.dart │ ├── webview │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── whisper │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ └── whisper_detail │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widget │ │ └── chat_item.dart ├── plugin │ └── pl_player │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── models │ │ ├── bottom_progress_behavior.dart │ │ ├── data_source.dart │ │ ├── data_status.dart │ │ ├── duration.dart │ │ ├── fullscreen_mode.dart │ │ ├── play_repeat.dart │ │ ├── play_speed.dart │ │ └── play_status.dart │ │ ├── utils.dart │ │ ├── utils │ │ └── fullscreen.dart │ │ ├── view.dart │ │ └── widgets │ │ ├── app_bar_ani.dart │ │ ├── backward_seek.dart │ │ ├── bottom_control.dart │ │ ├── common_btn.dart │ │ ├── forward_seek.dart │ │ └── play_pause_btn.dart ├── router │ └── app_pages.dart ├── scripts │ └── build.sh ├── services │ ├── audio_handler.dart │ ├── audio_session.dart │ ├── loggeer.dart │ ├── service_locator.dart │ └── shutdown_timer_service.dart └── utils │ ├── app_scheme.dart │ ├── cache_manage.dart │ ├── cookie.dart │ ├── danmaku.dart │ ├── data.dart │ ├── download.dart │ ├── em.dart │ ├── event_bus.dart │ ├── extension.dart │ ├── feed_back.dart │ ├── grid.dart │ ├── id_utils.dart │ ├── login.dart │ ├── proxy.dart │ ├── recommend_filter.dart │ ├── storage.dart │ ├── url_utils.dart │ ├── utils.dart │ ├── video_utils.dart │ └── wbi_sign.dart ├── linux ├── .gitignore ├── CMakeLists.txt ├── flutter │ └── CMakeLists.txt ├── main.cc ├── my_application.cc └── my_application.h ├── macos ├── .gitignore ├── Flutter │ ├── Flutter-Debug.xcconfig │ └── Flutter-Release.xcconfig ├── Podfile ├── Podfile.lock ├── 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 │ │ ├── 1024.png │ │ ├── 128.png │ │ ├── 16.png │ │ ├── 256.png │ │ ├── 32.png │ │ ├── 512.png │ │ ├── 64.png │ │ └── Contents.json │ ├── Base.lproj │ └── MainMenu.xib │ ├── Configs │ ├── AppInfo.xcconfig │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── Warnings.xcconfig │ ├── DebugProfile.entitlements │ ├── Info.plist │ ├── MainFlutterWindow.swift │ └── Release.entitlements ├── pubspec.lock ├── pubspec.yaml ├── test └── widget_test.dart ├── web ├── favicon.png ├── icons │ ├── Icon-192.png │ ├── Icon-512.png │ ├── Icon-maskable-192.png │ └── Icon-maskable-512.png ├── index.html └── manifest.json └── windows ├── .gitignore ├── CMakeLists.txt ├── flutter └── CMakeLists.txt └── 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-反馈.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug 反馈 3 | about: 描述你所遇到的bug 4 | title: '' 5 | labels: 问题反馈 6 | assignees: orz12 7 | 8 | --- 9 | 10 | ### 问题描述 11 | 请提供一个清晰而简明的问题描述。 12 | 13 | ### 复现步骤 14 | 请提供复现该问题所需的具体步骤。 15 | 16 | ### 预期行为 17 | 请描述你期望的正确行为或结果。 18 | 19 | ### 错误日志 20 | 请提供关于-错误日志中的内容。如果没有,请提供您的app版本号、系统版本、设备型号等相关信息。 21 | 22 | ### 相关信息 23 | 请补充截图、录屏、BV号等其他有助于解决问题的信息。 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/功能请求.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 功能请求 3 | about: 对于功能的一些建议 4 | title: '' 5 | labels: 功能 6 | assignees: orz12 7 | 8 | --- 9 | 10 | ### 功能描述 11 | 请提供对所请求功能的清晰描述。 12 | 13 | ### 目标 14 | 请描述你希望通过这个功能实现的目标。 15 | 16 | ### 解决方案 17 | 如果你有任何关于如何实现这个功能的想法或建议,请在这里提供。 18 | 19 | ### 其他 20 | 请提供已实现该功能或类似功能的应用 21 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled. 5 | 6 | version: 7 | revision: 4b12645012342076800eb701bcdfe18f87da21cf 8 | channel: stable 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 4b12645012342076800eb701bcdfe18f87da21cf 17 | base_revision: 4b12645012342076800eb701bcdfe18f87da21cf 18 | - platform: android 19 | create_revision: 4b12645012342076800eb701bcdfe18f87da21cf 20 | base_revision: 4b12645012342076800eb701bcdfe18f87da21cf 21 | - platform: ios 22 | create_revision: 4b12645012342076800eb701bcdfe18f87da21cf 23 | base_revision: 4b12645012342076800eb701bcdfe18f87da21cf 24 | - platform: linux 25 | create_revision: 4b12645012342076800eb701bcdfe18f87da21cf 26 | base_revision: 4b12645012342076800eb701bcdfe18f87da21cf 27 | - platform: macos 28 | create_revision: 4b12645012342076800eb701bcdfe18f87da21cf 29 | base_revision: 4b12645012342076800eb701bcdfe18f87da21cf 30 | - platform: web 31 | create_revision: 4b12645012342076800eb701bcdfe18f87da21cf 32 | base_revision: 4b12645012342076800eb701bcdfe18f87da21cf 33 | - platform: windows 34 | create_revision: 4b12645012342076800eb701bcdfe18f87da21cf 35 | base_revision: 4b12645012342076800eb701bcdfe18f87da21cf 36 | 37 | # User provided section 38 | 39 | # List of Local paths (relative to this file) that should be 40 | # ignored by the migrate tool. 41 | # 42 | # Files that are not part of the templates will be ignored by default. 43 | unmanaged_files: 44 | - 'lib/main.dart' 45 | - 'ios/Runner.xcodeproj/project.pbxproj' 46 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "pilipala", 9 | "request": "launch", 10 | "type": "dart" 11 | }, 12 | { 13 | "name": "pilipala (profile mode)", 14 | "request": "launch", 15 | "type": "dart", 16 | "flutterMode": "profile" 17 | }, 18 | { 19 | "name": "pilipala (release mode)", 20 | "request": "launch", 21 | "type": "dart", 22 | "flutterMode": "release" 23 | } 24 | ] 25 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "[dart]": { 4 | "editor.formatOnType": true 5 | } 6 | } -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/android/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/guozhigq/pilipala/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.orz12.PiliPalaX 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-anydpi-v24/ic_notification_icon.xml: -------------------------------------------------------------------------------- 1 | 7 | 11 | 16 | 21 | 26 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/ic_notification_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/android/app/src/main/res/drawable-hdpi/ic_notification_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/ic_notification_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/android/app/src/main/res/drawable-mdpi/ic_notification_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/ic_notification_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/android/app/src/main/res/drawable-xhdpi/ic_notification_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/ic_notification_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/android/app/src/main/res/drawable-xxhdpi/ic_notification_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_baseline_forward_10_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_baseline_replay_10_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_notification_icon.xml: -------------------------------------------------------------------------------- 1 | 7 | 11 | 16 | 21 | 26 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /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/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/raw/keep.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /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 | 15 | 19 | 20 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.9.22' 3 | repositories { 4 | maven { url "https://maven.aliyun.com/repository/google" } 5 | maven { url "https://maven.aliyun.com/repository/central" } 6 | maven { url "https://maven.aliyun.com/repository/jcenter" } 7 | maven { url "https://maven.aliyun.com/repository/public" } 8 | maven { url "http://download.flutter.io" 9 | allowInsecureProtocol = true 10 | } 11 | google() 12 | mavenCentral() 13 | } 14 | 15 | dependencies { 16 | classpath 'com.android.tools.build:gradle:7.2.0' 17 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 18 | } 19 | } 20 | 21 | allprojects { 22 | repositories { 23 | maven { url "https://maven.aliyun.com/repository/google" } 24 | maven { url "https://maven.aliyun.com/repository/central" } 25 | maven { url "https://maven.aliyun.com/repository/jcenter" } 26 | maven { url "https://maven.aliyun.com/repository/public" } 27 | maven { url "http://download.flutter.io" 28 | allowInsecureProtocol = true 29 | } 30 | google() 31 | mavenCentral() 32 | } 33 | } 34 | 35 | rootProject.buildDir = '../build' 36 | subprojects { 37 | project.buildDir = "${rootProject.buildDir}/${project.name}" 38 | } 39 | subprojects { 40 | project.evaluationDependsOn(':app') 41 | } 42 | 43 | tasks.register("clean", Delete) { 44 | delete rootProject.buildDir 45 | } 46 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip 6 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /assets/fonts/Jura-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/fonts/Jura-Bold.ttf -------------------------------------------------------------------------------- /assets/fonts/fansCard.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/fonts/fansCard.ttf -------------------------------------------------------------------------------- /assets/images/ai.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/images/ai.png -------------------------------------------------------------------------------- /assets/images/big-vip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/images/big-vip.png -------------------------------------------------------------------------------- /assets/images/dm_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/images/dm_gray.png -------------------------------------------------------------------------------- /assets/images/dm_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/images/dm_white.png -------------------------------------------------------------------------------- /assets/images/live.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/images/live.gif -------------------------------------------------------------------------------- /assets/images/live.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/images/live.png -------------------------------------------------------------------------------- /assets/images/live/default_bg.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/images/live/default_bg.webp -------------------------------------------------------------------------------- /assets/images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/images/loading.gif -------------------------------------------------------------------------------- /assets/images/loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/images/loading.png -------------------------------------------------------------------------------- /assets/images/logo/logo_android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/images/logo/logo_android.png -------------------------------------------------------------------------------- /assets/images/logo/logo_android_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/images/logo/logo_android_2.png -------------------------------------------------------------------------------- /assets/images/logo/logo_ios.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/images/logo/logo_ios.png -------------------------------------------------------------------------------- /assets/images/lv/lv0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/images/lv/lv0.png -------------------------------------------------------------------------------- /assets/images/lv/lv1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/images/lv/lv1.png -------------------------------------------------------------------------------- /assets/images/lv/lv2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/images/lv/lv2.png -------------------------------------------------------------------------------- /assets/images/lv/lv3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/images/lv/lv3.png -------------------------------------------------------------------------------- /assets/images/lv/lv4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/images/lv/lv4.png -------------------------------------------------------------------------------- /assets/images/lv/lv5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/images/lv/lv5.png -------------------------------------------------------------------------------- /assets/images/lv/lv6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/images/lv/lv6.png -------------------------------------------------------------------------------- /assets/images/noface.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/images/noface.jpeg -------------------------------------------------------------------------------- /assets/images/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/images/play.png -------------------------------------------------------------------------------- /assets/images/play.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/images/run-pokemon.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/images/run-pokemon.gif -------------------------------------------------------------------------------- /assets/images/tv.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/images/up_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/images/up_gray.png -------------------------------------------------------------------------------- /assets/images/view.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /assets/images/view_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/images/view_gray.png -------------------------------------------------------------------------------- /assets/images/view_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/images/view_white.png -------------------------------------------------------------------------------- /assets/screenshots/174shots_so.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/screenshots/174shots_so.png -------------------------------------------------------------------------------- /assets/screenshots/510shots_so.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/screenshots/510shots_so.png -------------------------------------------------------------------------------- /assets/screenshots/850shots_so.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/screenshots/850shots_so.png -------------------------------------------------------------------------------- /assets/screenshots/bangumi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/screenshots/bangumi.png -------------------------------------------------------------------------------- /assets/screenshots/bangumi_detail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/screenshots/bangumi_detail.png -------------------------------------------------------------------------------- /assets/screenshots/dynamic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/screenshots/dynamic.png -------------------------------------------------------------------------------- /assets/screenshots/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/screenshots/home.png -------------------------------------------------------------------------------- /assets/screenshots/main_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/screenshots/main_screen.png -------------------------------------------------------------------------------- /assets/screenshots/media.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/screenshots/media.png -------------------------------------------------------------------------------- /assets/screenshots/member.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/screenshots/member.png -------------------------------------------------------------------------------- /assets/screenshots/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/screenshots/search.png -------------------------------------------------------------------------------- /assets/screenshots/set.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/assets/screenshots/set.png -------------------------------------------------------------------------------- /change_log/1.0.0.0817.md: -------------------------------------------------------------------------------- 1 | ## 1.0.0 2 | 3 | ### 初始版本 4 | + 直播、推荐、动态功能 5 | + 投稿、番剧播放功能 6 | + 播放器手势支持 7 | + 画质、音质、解码格式支持 8 | + 点赞、投币、收藏功能 9 | + 关注/取关、用户主页功能 10 | + 评论功能 11 | + 历史记录、稍后再看功能 -------------------------------------------------------------------------------- /change_log/1.0.1.0817.md: -------------------------------------------------------------------------------- 1 | ## 1.0.1 2 | 3 | ### 修复 4 | + 升级播放器依赖 5 | + android平台 AV1格式视频支持 6 | + 视频全屏功能 7 | 8 | -------------------------------------------------------------------------------- /change_log/1.0.10.1016.md: -------------------------------------------------------------------------------- 1 | ## 1.0.10 2 | 3 | ### 修复 4 | + 长按倍速抬起后未恢复默认倍速 -------------------------------------------------------------------------------- /change_log/1.0.11.1112.md: -------------------------------------------------------------------------------- 1 | ## 1.0.11 2 | 3 | ### 新功能 4 | + 适配了原生媒体通知栏 @Daydreamer-riri 5 | + 视频主题图标 @Daydreamer-riri 6 | + 关闭软件后自动画中画播放 7 | + UP主分组管理 8 | + md2样式底栏 9 | + 10 | 11 | 12 | ### 修复 13 | + 历史记录记忆播放 14 | + 部分类型视频连播 15 | + 播放速度选择框不支持返回手势 16 | + 播放速度选择框不支持返回手势 17 | + 视频播放速度总是显示1.0X 18 | + 评论页面计数错误 19 | + 退出视频还有声音 20 | 21 | 22 | ### 优化 23 | + 视频加载速度 24 | 25 | 更多更新日志可在Github上查看 26 | 问题反馈、功能建议请查看「关于」页面。 -------------------------------------------------------------------------------- /change_log/1.0.12.1114.md: -------------------------------------------------------------------------------- 1 | ## 1.0.12 2 | 3 | 4 | ### 修复 5 | + iOS端视频播放时没有声音 6 | + 超过6分钟弹幕不显示 7 | + 视频详情页网络异常 8 | 9 | 10 | 更多更新日志可在Github上查看 11 | 问题反馈、功能建议请查看「关于」页面。 -------------------------------------------------------------------------------- /change_log/1.0.13.1217.md: -------------------------------------------------------------------------------- 1 | ## 1.0.13 2 | 3 | 4 | ### 新功能 5 | + 视频详情页稍后再看 6 | + 发送弹幕 感谢@orz12 7 | + 消息展示 8 | + up主页显示获赞数 9 | + up主页显示合集 10 | + 视频详情页「ai总结」增加开关 11 | 12 | ### 修复 13 | + 首页推荐问题(需要重新登录) 14 | + 长按倍速逻辑 15 | + 视频详情页网络异常 16 | 17 | ### 优化 18 | + 设置面板样式 感谢@GuMengYu @KoolShow 19 | 20 | 21 | 更多更新日志可在Github上查看 22 | 问题反馈、功能建议请查看「关于」页面。 23 | -------------------------------------------------------------------------------- /change_log/1.0.14.1225.md: -------------------------------------------------------------------------------- 1 | ## 1.0.14 2 | 3 | 圣诞节快乐~ 🎉 4 | 5 | 大部分内容由@orz12提供,感谢👏 6 | 7 | ### 修复 8 | + 全屏弹幕消失 9 | + iOS全屏/退出全屏视频暂停 10 | + 个人主页关注状态 11 | + 视频合集向下滑动UI问题 12 | + 媒体库滑动底栏不隐藏 13 | + 个人主页动态加载问题 * 2 14 | + 未登录状态访问个人主页异常 15 | + 视频搜索标题特殊字符转义 16 | + iOS闪退 17 | + 消息页面夜间模式异常 18 | + 消息页面含有撤回消息时异常 19 | + 弹幕速度 20 | 21 | ### 优化 22 | + 全屏播放方案优化 23 | + 弹幕加载逻辑优化 24 | + 点赞、投币逻辑优化 25 | + 进度条及播放时间渲染优化 26 | 27 | 更多更新日志可在Github上查看 28 | 问题反馈、功能建议请查看「关于」页面。 29 | -------------------------------------------------------------------------------- /change_log/1.0.15.0101.md: -------------------------------------------------------------------------------- 1 | ## 1.0.15 2 | 3 | 元旦快乐~ 🎉 4 | 5 | ### 功能 6 | + 转发动态评论展示 7 | + 推荐、最热、收藏视频增肌日期显示 8 | 9 | ### 修复 10 | + 全屏播放相关问题 11 | + 评论区@用户展示问题 12 | + 登录状态闪退问题 13 | + pip意外触发问题 14 | + 动态页tab切换样式问题 15 | 16 | ### 优化 17 | + 首页默认使用web端推荐 18 | + 取消iOS路由切换效果 19 | + 视频分享中添加Up主 20 | 21 | 更多更新日志可在Github上查看 22 | 问题反馈、功能建议请查看「关于」页面。 23 | -------------------------------------------------------------------------------- /change_log/1.0.16.0102.md: -------------------------------------------------------------------------------- 1 | ## 1.0.16 2 | 3 | 4 | ### 功能 5 | + toast 背景支持透明度调节 6 | 7 | ### 修复 8 | + web端推荐未展示【已关注】 9 | + up主动态页异常 10 | + 未打开自动播放时,视频详情页异常 11 | + 视频暂停状态取消自动ip 12 | 13 | 14 | 更多更新日志可在Github上查看 15 | 问题反馈、功能建议请查看「关于」页面。 16 | -------------------------------------------------------------------------------- /change_log/1.0.17.0125.md: -------------------------------------------------------------------------------- 1 | ## 1.0.17 2 | 3 | 4 | ### 功能 5 | + 视频全屏时隐藏进度条 6 | + 动态内容增加投稿跳转 7 | + 未开启自动播放时点击封面播放 8 | + 弹幕发送标识 9 | + 定时关闭 10 | + 推荐视频卡片拉黑up功能 11 | + 首页tabbar编辑排序 12 | 13 | ### 修复 14 | + 连续跳转搜索页未刷新 15 | + 搜索结果为空时页面异常 16 | + 评论区链接解析 17 | + 视频全屏状态栏背景色 18 | + 私信对话气泡位置 19 | + 设置up关注分组样式 20 | + 每次推荐请求数据相同 21 | + iOS代理网络异常 22 | + 双击切换播放状态无声 23 | + 设置自定义倍速白屏 24 | + 免登录查看1080p 25 | 26 | ### 优化 27 | + 首页web端推荐观看数展示 28 | + 首页web端推荐接口更新 29 | + 首页样式 30 | + 搜索页跳转 31 | + 弹幕资源优化 32 | + 图片渲染占用内存优化(部分) 33 | + 两次返回退出应用 34 | + schame 补充 35 | 36 | 37 | 38 | 更多更新日志可在Github上查看 39 | 问题反馈、功能建议请查看「关于」页面。 40 | -------------------------------------------------------------------------------- /change_log/1.0.18.0130.md: -------------------------------------------------------------------------------- 1 | ## 1.0.18 2 | 3 | 4 | ### 功能 5 | 6 | 7 | ### 修复 8 | 9 | 10 | ### 优化 11 | 12 | 13 | 14 | 15 | 更多更新日志可在Github上查看 16 | 问题反馈、功能建议请查看「关于」页面。 17 | -------------------------------------------------------------------------------- /change_log/1.0.19.0131.md: -------------------------------------------------------------------------------- 1 | ## 1.0.19 2 | 3 | 4 | ### 修复 5 | + 视频404、评论加载错误 6 | + bvav转换 7 | 8 | ### 优化 9 | + 视频详情页内存占用 10 | 11 | 12 | 13 | 14 | 更多更新日志可在Github上查看 15 | 问题反馈、功能建议请查看「关于」页面。 16 | -------------------------------------------------------------------------------- /change_log/1.0.2.0819.md: -------------------------------------------------------------------------------- 1 | ## 1.0.2 2 | 3 | ### 新功能 4 | + 自动检查更新 5 | + 封面图片保存 6 | + 动态跳转番剧 7 | + 历史记录番剧记忆播放 8 | + 一键清空稍后再看 9 | 10 | ### 修复 11 | + 切换分P cid未切换 12 | + cookie存储问题 13 | + 登录/退出登录问题 14 | 15 | ### 优化 16 | + 页面空/异常状态样式 17 | + 退出登录提示 18 | + 请求节流 19 | + 全屏播放 -------------------------------------------------------------------------------- /change_log/1.0.3.0821.md: -------------------------------------------------------------------------------- 1 | ## 1.0.3 2 | 3 | 建议卸载1.0.2版本,重新安装 4 | ### 新功能 5 | + 底部播放进度条设置 6 | + 复制图片链接 7 | 8 | 9 | ### 修复 10 | + 用户数据格式修改 11 | + video Fit 12 | + 没有audio 资源的视频异常 13 | + 评论区域图片无法点击 14 | + 视频进度条拖动问题 15 | 16 | ### 优化 17 | + 页面空/异常状态样式 18 | + 部分页面样式 19 | + 图片预览页面样式 -------------------------------------------------------------------------------- /change_log/1.0.4.0822.md: -------------------------------------------------------------------------------- 1 | ## 1.0.4 2 | 3 | ### 新功能 4 | + 热搜刷新 5 | + 视频搜索排序、筛选 6 | + app字体大小自定义 7 | + app主题色自定义 8 | + 「课堂」类动态渲染 9 | 10 | 11 | ### 修复 12 | + 搜索词联想richText渲染异常 13 | + 部分动态点赞异常 14 | + 默认视频解码格式 15 | + 搜索页面返回搜索词未清空 16 | + 动态详情评论加载异常 17 | + 动态页面下拉刷新数据异常 18 | 19 | ### 优化 20 | + 一些样式修改 21 | + 取消热搜词缓存 -------------------------------------------------------------------------------- /change_log/1.0.5.0826.md: -------------------------------------------------------------------------------- 1 | ## 1.0.5 2 | 3 | 主要是bug修复跟一部分小功能,弹幕功能需要下一版。 4 | 问题反馈请前往QQ频道或提交issues。 5 | 感谢🙏酷友「无力感*」「斤斤计较呀」「Pseudopamine」 6 | 7 | ### 新功能 8 | + 高帧率支持 9 | + 默认评论排序设置 10 | + 默认动态类别设置 11 | + 动态合集查看 12 | + 同时观看人数 13 | + iOS路由切换效果 14 | 15 | 16 | ### 修复 17 | + 收藏夹翻页 18 | + 首页搜索框频繁点击消失 19 | + 评论排序切换空白 20 | + 快速返回首页 21 | + 重复进入个人中心页面数据未刷新 22 | + 动态goods数据异常 23 | + 大会员切换番剧 24 | + 高画质codes匹配 25 | 26 | 27 | ### 优化 28 | + 倍速选择 29 | + 播放器亮度记忆 30 | + 下载对应版本apk -------------------------------------------------------------------------------- /change_log/1.0.6.0902.md: -------------------------------------------------------------------------------- 1 | ## 1.0.6 2 | 3 | 问题反馈、功能建议请查看「关于」页面。 4 | 5 | ### 新功能 6 | + 首页单列布局 7 | + 首页推荐展示播放量、弹幕数 8 | + 简单弹幕功能实现(持续开发中...) 9 | + 评论区搜索关键词开关 issues#46 10 | + 热搜榜隐藏功能 issues#35 11 | + 自动全屏 issues#37 12 | + 快速收藏功能 13 | + 双击快进/快退开关 14 | + 评论链接跳转视频 15 | + 支持移除单个稍后再看 16 | + app scheme外链跳转 17 | 18 | 19 | ### 修复 20 | + 杜比、无损音频切换 21 | + 收藏夹展示 issues#42 22 | + 搜索建议次 issues#47 23 | 24 | 25 | ### 优化 26 | + 倍速选择优化 27 | + 导航条沉浸 28 | + 取消Hero动画 29 | + 视频锁定逻辑 30 | + 登录逻辑优化 31 | + 图片预览样式 32 | + +评论区用户点击范围 33 | + 关注、粉丝页面优化 34 | + 关闭自动播放时播放器初始化逻辑 -------------------------------------------------------------------------------- /change_log/1.0.7.0908.md: -------------------------------------------------------------------------------- 1 | ## 1.0.7 2 | 3 | 默认倍速、直播弹幕、专栏等功能开发中 4 | 5 | ### 新功能 6 | + 弹幕设置、屏蔽功能 7 | + 不是很完美的后台播放功能 8 | + 不是很完美的画中画(pip)功能(Android端) 9 | 10 | ### 修复 11 | + 动态页面加载异常 12 | + 网络异常时页面空白 13 | + 竖屏全屏状态栏问题 14 | + iOS端代理请求异常 15 | 16 | ### 优化 17 | + 图片预览 18 | + 全屏播放时自动旋转 19 | + 转发内容增加视频标题 20 | 21 | 更多更新日志可在Github上查看 22 | 问题反馈、功能建议请查看「关于」页面。 23 | -------------------------------------------------------------------------------- /change_log/1.0.8.0917.md: -------------------------------------------------------------------------------- 1 | ## 1.0.8 2 | 3 | 直播弹幕、循环播放等功能开发中 4 | 5 | ### 新功能 6 | + 用户拉黑功能 7 | + gif图片保存 8 | + 删除已看历史记录 9 | 10 | ### 修复 11 | + 弹幕数量较少 12 | + 弹幕屏蔽设置自动记忆 13 | + 动态页面渲染 14 | + 用户主页数据错乱 15 | + 大家都在搜空白 16 | + 默认自动全屏,顶部操作栏丢失 17 | 18 | 19 | ### 优化 20 | + 全屏状态栏区域显示优化 21 | + 图片保存至PiliPala文件夹 22 | 23 | 更多更新日志可在Github上查看 24 | 问题反馈、功能建议请查看「关于」页面。 25 | -------------------------------------------------------------------------------- /change_log/1.0.9.1015.md: -------------------------------------------------------------------------------- 1 | ## 1.0.9 2 | 3 | 4 | ### 新功能 5 | + 自定义倍速、默认倍速 6 | + 历史记录搜索 7 | + 收藏夹搜索 8 | + 历史记录多选删除 9 | + 视频循环播放 10 | + 免登录看1080P 11 | + 评论区视频链接跳转 12 | + up主分组 13 | + up主投稿搜索 14 | 15 | ### 修复 16 | + 搜索视频标题乱码 17 | + 屏幕帧率 18 | + 动态页面渲染 19 | 20 | 21 | 22 | ### 优化 23 | + 快进手势 24 | + 视频简介链接匹配 25 | + 视频全屏时安全区域 26 | 27 | 更多更新日志可在Github上查看 28 | 问题反馈、功能建议请查看「关于」页面。 29 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/full_description.txt: -------------------------------------------------------------------------------- 1 | PiliPala is a third-party Bilibili client developed in Flutter. 2 | 3 | Top Features: 4 | 5 | * List of recommended videos 6 | * List of hottest videos 7 | * Popular live streams 8 | * List of bangumis 9 | * Block videos from blacklisted users 10 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/featureGraphic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/fastlane/metadata/android/en-US/images/featureGraphic.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/fastlane/metadata/android/en-US/images/icon.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/short_description.txt: -------------------------------------------------------------------------------- 1 | A third-party Bilibili client developed in Flutter 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/title.txt: -------------------------------------------------------------------------------- 1 | PiliPala 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/zh-CN/changelogs/2001.txt: -------------------------------------------------------------------------------- 1 | 修复 2 | 3 | * 全屏弹幕消失 4 | * iOS 全屏/退出全屏视频暂停 5 | * 个人主页关注状态 6 | * 视频合集向下滑动UI问题 7 | * 媒体库滑动底栏不隐藏 8 | * 个人主页动态加载问题 * 2 9 | * 未登录状态访问个人主页异常 10 | * 视频搜索标题特殊字符转义 11 | * iOS 闪退 12 | * 消息页面夜间模式异常 13 | * 消息页面含有撤回消息时异常 14 | * 弹幕速度 15 | 16 | 优化 17 | 18 | * 全屏播放方案优化 19 | * 弹幕加载逻辑优化 20 | * 点赞、投币逻辑优化 21 | * 进度条及播放时间渲染优化 22 | -------------------------------------------------------------------------------- /fastlane/metadata/android/zh-CN/full_description.txt: -------------------------------------------------------------------------------- 1 | PiliPala 是使用 Flutter 开发的 BiliBili 第三方客户端。 2 | 3 | 主要功能: 4 | 5 | * 推荐视频列表 (app 端) 6 | * 最热视频列表 7 | * 热门直播 8 | * 番剧列表 9 | * 屏蔽黑名单内用户视频 10 | -------------------------------------------------------------------------------- /fastlane/metadata/android/zh-CN/images/featureGraphic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/fastlane/metadata/android/zh-CN/images/featureGraphic.png -------------------------------------------------------------------------------- /fastlane/metadata/android/zh-CN/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/fastlane/metadata/android/zh-CN/images/icon.png -------------------------------------------------------------------------------- /fastlane/metadata/android/zh-CN/images/phoneScreenshots/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/fastlane/metadata/android/zh-CN/images/phoneScreenshots/1.png -------------------------------------------------------------------------------- /fastlane/metadata/android/zh-CN/images/phoneScreenshots/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/fastlane/metadata/android/zh-CN/images/phoneScreenshots/2.png -------------------------------------------------------------------------------- /fastlane/metadata/android/zh-CN/images/phoneScreenshots/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/fastlane/metadata/android/zh-CN/images/phoneScreenshots/3.png -------------------------------------------------------------------------------- /fastlane/metadata/android/zh-CN/short_description.txt: -------------------------------------------------------------------------------- 1 | 使用 Flutter 开发的 BiliBili 第三方客户端 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/zh-CN/title.txt: -------------------------------------------------------------------------------- 1 | PiliPala 2 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 11.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, '13.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | target.build_configurations.each do |config| 41 | deployment_target = config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] 42 | if !deployment_target.nil? && !deployment_target.empty? && deployment_target.to_f < 12.0 43 | config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0' 44 | end 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/ios/Runner/Assets.xcassets/AppIcon.appiconset/100.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/ios/Runner/Assets.xcassets/AppIcon.appiconset/1024.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/ios/Runner/Assets.xcassets/AppIcon.appiconset/114.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/ios/Runner/Assets.xcassets/AppIcon.appiconset/120.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/ios/Runner/Assets.xcassets/AppIcon.appiconset/144.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/ios/Runner/Assets.xcassets/AppIcon.appiconset/152.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/ios/Runner/Assets.xcassets/AppIcon.appiconset/167.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/ios/Runner/Assets.xcassets/AppIcon.appiconset/180.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/ios/Runner/Assets.xcassets/AppIcon.appiconset/20.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/ios/Runner/Assets.xcassets/AppIcon.appiconset/29.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/ios/Runner/Assets.xcassets/AppIcon.appiconset/40.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/ios/Runner/Assets.xcassets/AppIcon.appiconset/50.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/ios/Runner/Assets.xcassets/AppIcon.appiconset/57.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/ios/Runner/Assets.xcassets/AppIcon.appiconset/58.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/ios/Runner/Assets.xcassets/AppIcon.appiconset/60.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/ios/Runner/Assets.xcassets/AppIcon.appiconset/72.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/ios/Runner/Assets.xcassets/AppIcon.appiconset/76.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/ios/Runner/Assets.xcassets/AppIcon.appiconset/80.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/87.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/ios/Runner/Assets.xcassets/AppIcon.appiconset/87.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/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/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 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /lib/common/constants.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class StyleString { 4 | static const double cardSpace = 8; 5 | static const double safeSpace = 12; 6 | static BorderRadius mdRadius = BorderRadius.circular(10); 7 | static const Radius imgRadius = Radius.circular(10); 8 | static const double aspectRatio = 16 / 10; 9 | } 10 | 11 | class Constants { 12 | // 27eb53fc9058f8c3 移动端 Android 13 | // 4409e2ce8ffd12b8 TV端 14 | static const String appKey = '4409e2ce8ffd12b8'; 15 | // 59b43e04ad6965f34319062b478f83dd TV端 16 | static const String appSec = '59b43e04ad6965f34319062b478f83dd'; 17 | static const String thirdSign = '04224646d1fea004e79606d3b038c84a'; 18 | static const String thirdApi = 19 | 'https://www.mcbbs.net/template/mcbbs/image/special_photo_bg.png'; 20 | } 21 | -------------------------------------------------------------------------------- /lib/common/widgets/animated_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class AnimatedDialog extends StatefulWidget { 4 | const AnimatedDialog({Key? key, required this.child, this.closeFn}) 5 | : super(key: key); 6 | 7 | final Widget child; 8 | final Function? closeFn; 9 | 10 | @override 11 | State createState() => AnimatedDialogState(); 12 | } 13 | 14 | class AnimatedDialogState extends State 15 | with SingleTickerProviderStateMixin { 16 | late AnimationController? controller; 17 | late Animation? opacityAnimation; 18 | late Animation? scaleAnimation; 19 | 20 | @override 21 | void initState() { 22 | super.initState(); 23 | 24 | controller = AnimationController( 25 | vsync: this, duration: const Duration(milliseconds: 800)); 26 | opacityAnimation = Tween(begin: 0.0, end: 0.6).animate( 27 | CurvedAnimation(parent: controller!, curve: Curves.easeOutExpo)); 28 | scaleAnimation = 29 | CurvedAnimation(parent: controller!, curve: Curves.easeOutExpo); 30 | controller!.addListener(() => setState(() {})); 31 | controller!.forward(); 32 | } 33 | 34 | @override 35 | void dispose() { 36 | controller!.dispose(); 37 | super.dispose(); 38 | } 39 | 40 | @override 41 | Widget build(BuildContext context) { 42 | return Material( 43 | color: Colors.black.withOpacity(opacityAnimation!.value), 44 | child: InkWell( 45 | splashColor: Colors.transparent, 46 | onTap: () => widget.closeFn!(), 47 | child: Center( 48 | child: FadeTransition( 49 | opacity: scaleAnimation!, 50 | child: ScaleTransition( 51 | scale: scaleAnimation!, 52 | child: widget.child, 53 | ), 54 | ), 55 | ), 56 | ), 57 | ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /lib/common/widgets/appbar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class AppBarWidget extends StatelessWidget implements PreferredSizeWidget { 4 | const AppBarWidget({ 5 | required this.child, 6 | required this.controller, 7 | required this.visible, 8 | Key? key, 9 | }) : super(key: key); 10 | 11 | final PreferredSizeWidget child; 12 | final AnimationController controller; 13 | final bool visible; 14 | 15 | @override 16 | Size get preferredSize => child.preferredSize; 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | visible ? controller.reverse() : controller.forward(); 21 | return SlideTransition( 22 | position: Tween( 23 | begin: Offset.zero, 24 | end: const Offset(0, -1), 25 | ).animate(CurvedAnimation( 26 | parent: controller, 27 | curve: Curves.easeInOutBack, 28 | )), 29 | child: child, 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/common/widgets/content_container.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ContentContainer extends StatelessWidget { 4 | final Widget? contentWidget; 5 | final Widget? bottomWidget; 6 | final bool isScrollable; 7 | final Clip? childClipBehavior; 8 | 9 | const ContentContainer( 10 | {Key? key, 11 | this.contentWidget, 12 | this.bottomWidget, 13 | this.isScrollable = true, 14 | this.childClipBehavior}) 15 | : super(key: key); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return LayoutBuilder( 20 | builder: (BuildContext context, BoxConstraints constraints) { 21 | return SingleChildScrollView( 22 | clipBehavior: childClipBehavior ?? Clip.hardEdge, 23 | physics: isScrollable ? null : const NeverScrollableScrollPhysics(), 24 | child: ConstrainedBox( 25 | constraints: constraints.copyWith( 26 | minHeight: constraints.maxHeight, 27 | maxHeight: double.infinity, 28 | ), 29 | child: IntrinsicHeight( 30 | child: Column( 31 | children: [ 32 | if (contentWidget != null) 33 | Expanded( 34 | child: contentWidget!, 35 | ) 36 | else 37 | const Spacer(), 38 | if (bottomWidget != null) bottomWidget!, 39 | ], 40 | ), 41 | ), 42 | ), 43 | ); 44 | }, 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/common/widgets/custom_toast.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:hive/hive.dart'; 3 | import 'package:PiliPalaX/utils/storage.dart'; 4 | 5 | Box setting = GStrorage.setting; 6 | 7 | class CustomToast extends StatelessWidget { 8 | const CustomToast({super.key, required this.msg}); 9 | 10 | final String msg; 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | final double toastOpacity = 15 | setting.get(SettingBoxKey.defaultToastOp, defaultValue: 1.0) as double; 16 | return Container( 17 | margin: 18 | EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom + 30), 19 | padding: const EdgeInsets.symmetric(horizontal: 17, vertical: 10), 20 | decoration: BoxDecoration( 21 | color: Theme.of(context) 22 | .colorScheme 23 | .primaryContainer 24 | .withOpacity(toastOpacity), 25 | borderRadius: BorderRadius.circular(20), 26 | ), 27 | child: Text( 28 | msg, 29 | style: TextStyle( 30 | fontSize: 13, 31 | color: Theme.of(context).colorScheme.primary, 32 | ), 33 | ), 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/common/widgets/http_error.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_svg/flutter_svg.dart'; 3 | 4 | class HttpError extends StatelessWidget { 5 | const HttpError( 6 | {required this.errMsg, required this.fn, this.btnText, super.key}); 7 | 8 | final String? errMsg; 9 | final Function()? fn; 10 | final String? btnText; 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return SliverToBoxAdapter( 15 | child: SizedBox( 16 | height: 400, 17 | child: Column( 18 | crossAxisAlignment: CrossAxisAlignment.center, 19 | mainAxisAlignment: MainAxisAlignment.center, 20 | children: [ 21 | SvgPicture.asset( 22 | "assets/images/error.svg", 23 | height: 200, 24 | ), 25 | const SizedBox(height: 30), 26 | Text( 27 | errMsg ?? '请求异常', 28 | textAlign: TextAlign.center, 29 | style: Theme.of(context).textTheme.titleSmall, 30 | ), 31 | const SizedBox(height: 20), 32 | FilledButton.tonal( 33 | onPressed: () { 34 | fn!(); 35 | }, 36 | style: ButtonStyle( 37 | backgroundColor: MaterialStateProperty.resolveWith((states) { 38 | return Theme.of(context).colorScheme.primary.withAlpha(20); 39 | }), 40 | ), 41 | child: Text( 42 | btnText ?? '点击重试', 43 | style: TextStyle(color: Theme.of(context).colorScheme.primary), 44 | ), 45 | ), 46 | ], 47 | ), 48 | ), 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/common/widgets/no_data.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_svg/flutter_svg.dart'; 3 | 4 | class NoData extends StatelessWidget { 5 | const NoData({super.key}); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return SliverToBoxAdapter( 10 | child: SizedBox( 11 | height: 400, 12 | child: Column( 13 | crossAxisAlignment: CrossAxisAlignment.center, 14 | mainAxisAlignment: MainAxisAlignment.center, 15 | children: [ 16 | SvgPicture.asset( 17 | "assets/images/error.svg", 18 | height: 200, 19 | ), 20 | const SizedBox(height: 20), 21 | Text( 22 | '没有数据', 23 | textAlign: TextAlign.center, 24 | style: Theme.of(context).textTheme.titleSmall, 25 | ), 26 | ], 27 | ), 28 | ), 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/common/widgets/sliver_header.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class SliverHeaderDelegate extends SliverPersistentHeaderDelegate { 4 | SliverHeaderDelegate({required this.height, required this.child}); 5 | 6 | final double height; 7 | final Widget child; 8 | 9 | @override 10 | Widget build( 11 | BuildContext context, double shrinkOffset, bool overlapsContent) { 12 | return child; 13 | } 14 | 15 | @override 16 | double get maxExtent => height; 17 | 18 | @override 19 | double get minExtent => height; 20 | 21 | @override 22 | bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) => 23 | true; 24 | } 25 | -------------------------------------------------------------------------------- /lib/common/widgets/stat/danmu.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:PiliPalaX/utils/utils.dart'; 3 | 4 | class StatDanMu extends StatelessWidget { 5 | final String? theme; 6 | final dynamic danmu; 7 | final String? size; 8 | 9 | const StatDanMu({Key? key, this.theme, this.danmu, this.size}) 10 | : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | Map colorObject = { 15 | 'white': Colors.white, 16 | 'gray': Theme.of(context).colorScheme.outline, 17 | 'black': Theme.of(context).colorScheme.onBackground.withOpacity(0.8), 18 | }; 19 | Color color = colorObject[theme]!; 20 | return Row( 21 | children: [ 22 | Icon( 23 | Icons.subtitles_outlined, 24 | size: 14, 25 | color: color, 26 | ), 27 | const SizedBox(width: 2), 28 | Text( 29 | Utils.numFormat(danmu!), 30 | style: TextStyle( 31 | fontSize: size == 'medium' ? 12 : 11, 32 | color: color, 33 | ), 34 | ) 35 | ], 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/common/widgets/stat/view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:PiliPalaX/utils/utils.dart'; 3 | 4 | class StatView extends StatelessWidget { 5 | final String? theme; 6 | final dynamic view; 7 | final String? size; 8 | 9 | const StatView({Key? key, this.theme, this.view, this.size}) 10 | : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | Map colorObject = { 15 | 'white': Colors.white, 16 | 'gray': Theme.of(context).colorScheme.outline, 17 | 'black': Theme.of(context).colorScheme.onBackground.withOpacity(0.8), 18 | }; 19 | Color color = colorObject[theme]!; 20 | return Row( 21 | children: [ 22 | Icon( 23 | Icons.play_circle_outlined, 24 | size: 13, 25 | color: color, 26 | ), 27 | const SizedBox(width: 2), 28 | Text( 29 | Utils.numFormat(view!), 30 | style: TextStyle( 31 | fontSize: size == 'medium' ? 12 : 11, 32 | color: color, 33 | ), 34 | ), 35 | ], 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/http/bangumi.dart: -------------------------------------------------------------------------------- 1 | import '../models/bangumi/list.dart'; 2 | import 'index.dart'; 3 | 4 | class BangumiHttp { 5 | static Future bangumiList({int? page}) async { 6 | var res = await Request().get(Api.bangumiList, data: {'page': page}); 7 | if (res.data['code'] == 0) { 8 | return { 9 | 'status': true, 10 | 'data': BangumiListDataModel.fromJson(res.data['data']) 11 | }; 12 | } else { 13 | return { 14 | 'status': false, 15 | 'data': [], 16 | 'msg': res.data['message'], 17 | }; 18 | } 19 | } 20 | 21 | static Future bangumiFollow({int? mid}) async { 22 | var res = await Request().get(Api.bangumiFollow, data: {'vmid': mid}); 23 | if (res.data['code'] == 0) { 24 | return { 25 | 'status': true, 26 | 'data': BangumiListDataModel.fromJson(res.data['data']) 27 | }; 28 | } else { 29 | return { 30 | 'status': false, 31 | 'data': [], 32 | 'msg': res.data['message'], 33 | }; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/http/black.dart: -------------------------------------------------------------------------------- 1 | import '../models/user/black.dart'; 2 | import 'index.dart'; 3 | 4 | class BlackHttp { 5 | static Future blackList({required int pn, int? ps}) async { 6 | var res = await Request().get(Api.blackLst, data: { 7 | 'pn': pn, 8 | 'ps': ps ?? 50, 9 | 're_version': 0, 10 | 'jsonp': 'jsonp', 11 | 'csrf': await Request.getCsrf(), 12 | }); 13 | if (res.data['code'] == 0) { 14 | return { 15 | 'status': true, 16 | 'data': BlackListDataModel.fromJson(res.data['data']) 17 | }; 18 | } else { 19 | return { 20 | 'status': false, 21 | 'data': [], 22 | 'msg': res.data['message'], 23 | }; 24 | } 25 | } 26 | 27 | // 移除黑名单 28 | static Future removeBlack({required int fid}) async { 29 | var res = await Request().post( 30 | Api.removeBlack, 31 | queryParameters: { 32 | 'act': 6, 33 | 'csrf': await Request.getCsrf(), 34 | 'fid': fid, 35 | 'jsonp': 'jsonp', 36 | 're_src': 116, 37 | }, 38 | ); 39 | if (res.data['code'] == 0) { 40 | return { 41 | 'status': true, 42 | 'data': [], 43 | 'msg': '操作成功', 44 | }; 45 | } else { 46 | return { 47 | 'status': false, 48 | 'data': [], 49 | 'msg': res.data['message'], 50 | }; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/http/common.dart: -------------------------------------------------------------------------------- 1 | import 'index.dart'; 2 | 3 | class CommonHttp { 4 | static Future unReadDynamic() async { 5 | var res = await Request().get(Api.getUnreadDynamic, 6 | data: {'alltype_offset': 0, 'video_offset': '', 'article_offset': 0}); 7 | if (res.data['code'] == 0) { 8 | return {'status': true, 'data': res.data['data']['dyn_basic_infos']}; 9 | } else { 10 | return { 11 | 'status': false, 12 | 'data': [], 13 | 'msg': res.data['message'], 14 | }; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/http/constants.dart: -------------------------------------------------------------------------------- 1 | class HttpString { 2 | static const String baseUrl = 'https://www.bilibili.com'; 3 | static const String apiBaseUrl = 'https://api.bilibili.com'; 4 | static const String tUrl = 'https://api.vc.bilibili.com'; 5 | static const String appBaseUrl = 'https://app.bilibili.com'; 6 | static const String liveBaseUrl = 'https://api.live.bilibili.com'; 7 | static const String passBaseUrl = 'https://passport.bilibili.com'; 8 | static const String messageBaseUrl = 'https://message.bilibili.com'; 9 | static const List validateStatusCodes = [ 10 | 302, 11 | 304, 12 | 307, 13 | 400, 14 | 401, 15 | 403, 16 | 404, 17 | 405, 18 | 409, 19 | 412, 20 | 500, 21 | 503, 22 | 504, 23 | 509, 24 | 616, 25 | 617, 26 | 625, 27 | 626, 28 | 628, 29 | 629, 30 | 632, 31 | 643, 32 | 650, 33 | 652, 34 | 658, 35 | 662, 36 | 688, 37 | 689, 38 | 701, 39 | 799, 40 | 8888 41 | ]; 42 | } 43 | -------------------------------------------------------------------------------- /lib/http/fan.dart: -------------------------------------------------------------------------------- 1 | import '../models/fans/result.dart'; 2 | import 'index.dart'; 3 | 4 | class FanHttp { 5 | static Future fans({int? vmid, int? pn, int? ps, String? orderType}) async { 6 | var res = await Request().get(Api.fans, data: { 7 | 'vmid': vmid, 8 | 'pn': pn, 9 | 'ps': ps, 10 | 'order': 'desc', 11 | 'order_type': orderType, 12 | }); 13 | if (res.data['code'] == 0) { 14 | return {'status': true, 'data': FansDataModel.fromJson(res.data['data'])}; 15 | } else { 16 | return { 17 | 'status': false, 18 | 'data': [], 19 | 'msg': res.data['message'], 20 | }; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/http/follow.dart: -------------------------------------------------------------------------------- 1 | import '../models/follow/result.dart'; 2 | import 'index.dart'; 3 | 4 | class FollowHttp { 5 | static Future followings( 6 | {int? vmid, int? pn, int? ps, String? orderType}) async { 7 | var res = await Request().get(Api.followings, data: { 8 | 'vmid': vmid, 9 | 'pn': pn, 10 | 'ps': ps, 11 | 'order': 'desc', 12 | 'order_type': orderType, 13 | }); 14 | if (res.data['code'] == 0) { 15 | return { 16 | 'status': true, 17 | 'data': FollowDataModel.fromJson(res.data['data']) 18 | }; 19 | } else { 20 | return { 21 | 'status': false, 22 | 'data': [], 23 | 'msg': res.data['message'], 24 | }; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/http/index.dart: -------------------------------------------------------------------------------- 1 | export 'api.dart'; 2 | export 'init.dart'; 3 | -------------------------------------------------------------------------------- /lib/http/interceptor_anonymity.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: avoid_print 2 | 3 | import 'dart:io'; 4 | import 'package:dio/dio.dart'; 5 | // import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; 6 | import '../pages/mine/controller.dart'; 7 | import 'api.dart'; 8 | 9 | class AnonymityInterceptor extends Interceptor { 10 | static const List anonymityList = [ 11 | Api.videoUrl, 12 | Api.videoIntro, 13 | Api.relatedList, 14 | Api.replyList, 15 | Api.replyReplyList, 16 | Api.searchSuggest, 17 | Api.searchByType, 18 | Api.heartBeat, 19 | Api.ab2c, 20 | Api.bangumiInfo, 21 | Api.liveRoomInfo, 22 | Api.onlineTotal, 23 | Api.webDanmaku, 24 | Api.dynamicDetail, 25 | Api.aiConclusion, 26 | Api.getMemberViewApi, 27 | Api.getSeasonDetailApi, 28 | ]; 29 | 30 | 31 | @override 32 | void onRequest(RequestOptions options, RequestInterceptorHandler handler) { 33 | if (MineController.anonymity) { 34 | String uri = options.uri.toString(); 35 | for (var i in anonymityList) { 36 | // 如果请求的url包含无痕列表中的url,则清空cookie 37 | // 但需要保证匹配到url的后半部分不再出现/符号,否则会误伤 38 | int index = uri.indexOf(i); 39 | if (index == -1) continue; 40 | if (uri.lastIndexOf('/') >= index + i.length) continue; 41 | //SmartDialog.showToast('触发无痕模式\n\n$i\n\n${options.uri}'); 42 | options.headers[HttpHeaders.cookieHeader] = ""; 43 | if(options.data != null && options.data.csrf != null) { 44 | options.data.csrf = ""; 45 | } 46 | break; 47 | } 48 | } 49 | handler.next(options); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/http/live.dart: -------------------------------------------------------------------------------- 1 | import '../models/live/item.dart'; 2 | import '../models/live/room_info.dart'; 3 | import '../models/live/room_info_h5.dart'; 4 | import 'api.dart'; 5 | import 'init.dart'; 6 | 7 | class LiveHttp { 8 | static Future liveList( 9 | {int? vmid, int? pn, int? ps, String? orderType}) async { 10 | var res = await Request().get(Api.liveList, 11 | data: {'page': pn, 'page_size': 30, 'platform': 'web'}); 12 | if (res.data['code'] == 0) { 13 | return { 14 | 'status': true, 15 | 'data': res.data['data']['list'] 16 | .map((e) => LiveItemModel.fromJson(e)) 17 | .toList() 18 | }; 19 | } else { 20 | return { 21 | 'status': false, 22 | 'data': [], 23 | 'msg': res.data['message'], 24 | }; 25 | } 26 | } 27 | 28 | static Future liveRoomInfo({roomId, qn}) async { 29 | var res = await Request().get(Api.liveRoomInfo, data: { 30 | 'room_id': roomId, 31 | 'protocol': '0, 1', 32 | 'format': '0, 1, 2', 33 | 'codec': '0, 1', 34 | 'qn': qn, 35 | 'platform': 'web', 36 | 'ptype': 8, 37 | 'dolby': 5, 38 | 'panorama': 1, 39 | }); 40 | if (res.data['code'] == 0) { 41 | return {'status': true, 'data': RoomInfoModel.fromJson(res.data['data'])}; 42 | } else { 43 | return { 44 | 'status': false, 45 | 'data': [], 46 | 'msg': res.data['message'], 47 | }; 48 | } 49 | } 50 | 51 | static Future liveRoomInfoH5({roomId, qn}) async { 52 | var res = await Request().get(Api.liveRoomInfoH5, data: { 53 | 'room_id': roomId, 54 | }); 55 | if (res.data['code'] == 0) { 56 | return { 57 | 'status': true, 58 | 'data': RoomInfoH5Model.fromJson(res.data['data']) 59 | }; 60 | } else { 61 | return { 62 | 'status': false, 63 | 'data': [], 64 | 'msg': res.data['message'], 65 | }; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/models/common/business_type.dart: -------------------------------------------------------------------------------- 1 | enum BusinessType { 2 | // 普通视频 3 | archive, 4 | // 剧集(番剧 / 影视) 5 | pgc, 6 | // 直播 7 | live, 8 | // 文章 9 | articleList, 10 | // 文章 11 | article, 12 | hiddenDurationType, 13 | showBadge 14 | } 15 | 16 | extension BusinessTypeExtension on BusinessType { 17 | String get type => 18 | ['archive', 'pgc', 'live', 'article-list', 'article'][index]; 19 | // 隐藏时长 20 | List get hiddenDurationType => ['live', 'article-list', 'article']; 21 | // 右上 22 | List get showBadge => ['pgc', 'article-list', 'article']; 23 | } 24 | -------------------------------------------------------------------------------- /lib/models/common/color_type.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | final List> colorThemeTypes = [ 4 | {'color': const Color.fromARGB(255, 92, 182, 123), 'label': '默认绿'}, 5 | {'color': Colors.pink, 'label': '粉红色'}, 6 | {'color': Colors.red, 'label': '红色'}, 7 | {'color': Colors.orange, 'label': '橙色'}, 8 | {'color': Colors.amber, 'label': '琥珀色'}, 9 | {'color': Colors.yellow, 'label': '黄色'}, 10 | {'color': Colors.lime, 'label': '酸橙色'}, 11 | {'color': Colors.lightGreen, 'label': '浅绿色'}, 12 | {'color': Colors.green, 'label': '绿色'}, 13 | {'color': Colors.teal, 'label': '青色'}, 14 | {'color': Colors.cyan, 'label': '蓝绿色'}, 15 | {'color': Colors.lightBlue, 'label': '浅蓝色'}, 16 | {'color': Colors.blue, 'label': '蓝色'}, 17 | {'color': Colors.indigo, 'label': '靛蓝色'}, 18 | {'color': Colors.purple, 'label': '紫色'}, 19 | {'color': Colors.deepPurple, 'label': '深紫色'}, 20 | {'color': Colors.blueGrey, 'label': '蓝灰色'}, 21 | {'color': Colors.brown, 'label': '棕色'}, 22 | {'color': Colors.grey, 'label': '灰色'}, 23 | ]; 24 | -------------------------------------------------------------------------------- /lib/models/common/dynamic_badge_mode.dart: -------------------------------------------------------------------------------- 1 | enum DynamicBadgeMode { hidden, point, number } 2 | 3 | extension DynamicBadgeModeDesc on DynamicBadgeMode { 4 | String get description => ['隐藏', '红点', '数字'][index]; 5 | } 6 | 7 | extension DynamicBadgeModeCode on DynamicBadgeMode { 8 | int get code => [0, 1, 2][index]; 9 | } 10 | -------------------------------------------------------------------------------- /lib/models/common/dynamics_type.dart: -------------------------------------------------------------------------------- 1 | enum DynamicsType { 2 | all, 3 | video, 4 | pgc, 5 | article, 6 | } 7 | 8 | extension BusinessTypeExtension on DynamicsType { 9 | String get values => ['all', 'video', 'pgc', 'article'][index]; 10 | String get labels => ['全部', '视频', '追番', '专栏'][index]; 11 | } 12 | -------------------------------------------------------------------------------- /lib/models/common/rcmd_type.dart: -------------------------------------------------------------------------------- 1 | // 首页推荐类型 2 | enum RcmdType { web, app, notLogin } 3 | 4 | extension RcmdTypeExtension on RcmdType { 5 | String get values => ['web', 'app', 'notLogin'][index]; 6 | String get labels => ['web端', 'app端', '游客模式'][index]; 7 | } 8 | -------------------------------------------------------------------------------- /lib/models/common/reply_sort_type.dart: -------------------------------------------------------------------------------- 1 | enum ReplySortType { time, like } 2 | 3 | extension ReplySortTypeExtension on ReplySortType { 4 | String get titles => ['最新评论', '最热评论'][index]; 5 | String get labels => ['最新', '最热'][index]; 6 | } 7 | -------------------------------------------------------------------------------- /lib/models/common/reply_type.dart: -------------------------------------------------------------------------------- 1 | enum ReplyType { 2 | unset, 3 | // 视频 4 | video, 5 | // 话题 6 | topic, 7 | // 8 | unset2, 9 | // 活动 10 | activity, 11 | // 小视频 12 | videoS, 13 | // 小黑屋封禁信息 14 | blockMsg, 15 | // 公告信息 16 | publicMsg, 17 | // 直播活动 18 | liveActivity, 19 | // 活动稿件 20 | activityFile, 21 | // 直播公告 22 | livePublic, 23 | // 相簿 24 | album, 25 | // 专栏 26 | column, 27 | // 票务 28 | ticket, 29 | // 音频 30 | audio, 31 | // 风纪委员会 32 | unset3, 33 | // 点评 34 | comment, 35 | // 动态 36 | dynamics, 37 | // 播单 38 | playList, 39 | // 音乐播单 40 | musicPlayList, 41 | // 漫画 42 | comics1, 43 | // 漫画 44 | comics2, 45 | // 漫画 46 | comics3, 47 | // 课程 48 | course, 49 | } 50 | -------------------------------------------------------------------------------- /lib/models/common/search_type.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: constant_identifier_names 2 | enum SearchType { 3 | // 视频:video 4 | video, 5 | // 番剧:media_bangumi, 6 | media_bangumi, 7 | // 影视:media_ft 8 | // media_ft, 9 | // 直播间及主播:live 10 | // live, 11 | // 直播间:live_room 12 | live_room, 13 | // 主播:live_user 14 | // live_user, 15 | // 话题:topic 16 | // topic, 17 | // 用户:bili_user 18 | bili_user, 19 | // 专栏:article 20 | article, 21 | // 相簿:photo 22 | // photo 23 | } 24 | 25 | extension SearchTypeExtension on SearchType { 26 | String get type => 27 | ['video', 'media_bangumi', 'live_room', 'bili_user', 'article'][index]; 28 | String get label => ['视频', '番剧', '直播间', '用户', '专栏'][index]; 29 | } 30 | 31 | // 搜索类型为视频、专栏及相簿时 32 | enum ArchiveFilterType { 33 | totalrank, 34 | click, 35 | pubdate, 36 | dm, 37 | stow, 38 | scores, 39 | // 专栏 40 | // attention, 41 | } 42 | 43 | extension ArchiveFilterTypeExtension on ArchiveFilterType { 44 | String get description => 45 | ['默认排序', '播放多', '新发布', '弹幕多', '收藏多', '评论多', '最多喜欢'][index]; 46 | } 47 | -------------------------------------------------------------------------------- /lib/models/common/tab_type.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:PiliPalaX/pages/bangumi/index.dart'; 4 | import 'package:PiliPalaX/pages/hot/index.dart'; 5 | import 'package:PiliPalaX/pages/live/index.dart'; 6 | import 'package:PiliPalaX/pages/rcmd/index.dart'; 7 | 8 | enum TabType { live, rcmd, hot, bangumi } 9 | 10 | extension TabTypeDesc on TabType { 11 | String get description => ['直播', '推荐', '热门', '番剧'][index]; 12 | String get id => ['live', 'rcmd', 'hot', 'bangumi'][index]; 13 | } 14 | 15 | List tabsConfig = [ 16 | { 17 | 'icon': const Icon( 18 | Icons.live_tv_outlined, 19 | size: 15, 20 | ), 21 | 'label': '直播', 22 | 'type': TabType.live, 23 | 'ctr': Get.find, 24 | 'page': const LivePage(), 25 | }, 26 | { 27 | 'icon': const Icon( 28 | Icons.thumb_up_off_alt_outlined, 29 | size: 15, 30 | ), 31 | 'label': '推荐', 32 | 'type': TabType.rcmd, 33 | 'ctr': Get.find, 34 | 'page': const RcmdPage(), 35 | }, 36 | { 37 | 'icon': const Icon( 38 | Icons.whatshot_outlined, 39 | size: 15, 40 | ), 41 | 'label': '热门', 42 | 'type': TabType.hot, 43 | 'ctr': Get.find, 44 | 'page': const HotPage(), 45 | }, 46 | { 47 | 'icon': const Icon( 48 | Icons.play_circle_outlined, 49 | size: 15, 50 | ), 51 | 'label': '番剧', 52 | 'type': TabType.bangumi, 53 | 'ctr': Get.find, 54 | 'page': const BangumiPage(), 55 | }, 56 | ]; 57 | -------------------------------------------------------------------------------- /lib/models/common/theme_type.dart: -------------------------------------------------------------------------------- 1 | enum ThemeType { 2 | light, 3 | dark, 4 | system, 5 | } 6 | 7 | extension ThemeTypeDesc on ThemeType { 8 | String get description => ['浅色', '深色', '跟随系统'][index]; 9 | } 10 | 11 | extension ThemeTypeCode on ThemeType { 12 | int get code => [0, 1, 2][index]; 13 | } 14 | -------------------------------------------------------------------------------- /lib/models/fans/result.dart: -------------------------------------------------------------------------------- 1 | class FansDataModel { 2 | FansDataModel({ 3 | this.total, 4 | this.list, 5 | }); 6 | 7 | int? total; 8 | List? list; 9 | 10 | FansDataModel.fromJson(Map json) { 11 | total = json['total']; 12 | list = json['list'] 13 | .map((e) => FansItemModel.fromJson(e)) 14 | .toList(); 15 | } 16 | } 17 | 18 | class FansItemModel { 19 | FansItemModel({ 20 | this.mid, 21 | this.attribute, 22 | this.mtime, 23 | this.tag, 24 | this.special, 25 | this.uname, 26 | this.face, 27 | this.sign, 28 | this.officialVerify, 29 | }); 30 | 31 | int? mid; 32 | int? attribute; 33 | int? mtime; 34 | List? tag; 35 | int? special; 36 | String? uname; 37 | String? face; 38 | String? sign; 39 | Map? officialVerify; 40 | 41 | FansItemModel.fromJson(Map json) { 42 | mid = json['mid']; 43 | attribute = json['attribute']; 44 | mtime = json['mtime']; 45 | tag = json['tag']; 46 | special = json['special']; 47 | uname = json['uname']; 48 | face = json['face']; 49 | sign = json['sign'] == '' ? '还没有签名' : json['sign']; 50 | officialVerify = json['official_verify']; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/models/follow/result.dart: -------------------------------------------------------------------------------- 1 | class FollowDataModel { 2 | FollowDataModel({ 3 | this.total, 4 | this.list, 5 | }); 6 | 7 | int? total; 8 | List? list; 9 | 10 | FollowDataModel.fromJson(Map json) { 11 | total = json['total'] ?? 0; 12 | list = json['list'] 13 | .map((e) => FollowItemModel.fromJson(e)) 14 | .toList(); 15 | } 16 | } 17 | 18 | class FollowItemModel { 19 | FollowItemModel({ 20 | this.mid, 21 | this.attribute, 22 | // this.mtime, 23 | this.tag, 24 | this.special, 25 | this.uname, 26 | this.face, 27 | this.sign, 28 | this.officialVerify, 29 | }); 30 | 31 | int? mid; 32 | int? attribute; 33 | // int? mtime; 34 | List? tag; 35 | int? special; 36 | String? uname; 37 | String? face; 38 | String? sign; 39 | Map? officialVerify; 40 | 41 | FollowItemModel.fromJson(Map json) { 42 | mid = json['mid']; 43 | attribute = json['attribute']; 44 | // mtime = json['mtime']; 45 | tag = json['tag']; 46 | special = json['special']; 47 | uname = json['uname']; 48 | face = json['face']; 49 | sign = json['sign'] == '' ? '还没有签名' : json['sign']; 50 | officialVerify = json['official_verify']; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/models/github/latest.dart: -------------------------------------------------------------------------------- 1 | class LatestDataModel { 2 | LatestDataModel({ 3 | this.url, 4 | this.tagName, 5 | this.createdAt, 6 | this.assets, 7 | this.body, 8 | }); 9 | 10 | String? url; 11 | String? tagName; 12 | String? createdAt; 13 | List? assets; 14 | String? body; 15 | 16 | LatestDataModel.fromJson(Map json) { 17 | url = json['url']; 18 | tagName = json['tag_name']; 19 | createdAt = json['created_at']; 20 | assets = json['assets'] != null 21 | ? json['assets'].map((e) => AssetItem.fromJson(e)).toList() 22 | : []; 23 | body = json['body']; 24 | } 25 | } 26 | 27 | class AssetItem { 28 | AssetItem({ 29 | this.url, 30 | this.name, 31 | this.size, 32 | this.downloadCount, 33 | this.downloadUrl, 34 | }); 35 | 36 | String? url; 37 | String? name; 38 | int? size; 39 | int? downloadCount; 40 | String? downloadUrl; 41 | 42 | AssetItem.fromJson(Map json) { 43 | url = json['url']; 44 | name = json['name']; 45 | size = json['size']; 46 | downloadCount = json['download_count']; 47 | downloadUrl = json['browser_download_url']; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/models/login/index.dart: -------------------------------------------------------------------------------- 1 | class CaptchaDataModel { 2 | CaptchaDataModel({ 3 | this.type, 4 | this.token, 5 | this.geetest, 6 | this.tencent, 7 | this.validate, 8 | this.seccode, 9 | }); 10 | 11 | String? type; 12 | String? token; 13 | GeetestData? geetest; 14 | Tencent? tencent; 15 | String? validate; 16 | String? seccode; 17 | 18 | CaptchaDataModel.fromJson(Map json) { 19 | type = json["type"]; 20 | token = json["token"]; 21 | geetest = 22 | json["geetest"] != null ? GeetestData.fromJson(json["geetest"]) : null; 23 | tencent = 24 | json["tencent"] != null ? Tencent.fromJson(json["tencent"]) : null; 25 | } 26 | } 27 | 28 | class GeetestData { 29 | GeetestData({ 30 | this.challenge, 31 | this.gt, 32 | }); 33 | 34 | String? challenge; 35 | String? gt; 36 | 37 | GeetestData.fromJson(Map json) { 38 | challenge = json["challenge"]; 39 | gt = json["gt"]; 40 | } 41 | } 42 | 43 | class Tencent { 44 | Tencent({this.appid}); 45 | String? appid; 46 | Tencent.fromJson(Map json) { 47 | appid = json["appid"]; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/models/member/tags.dart: -------------------------------------------------------------------------------- 1 | class MemberTagItemModel { 2 | MemberTagItemModel({ 3 | this.count, 4 | this.name, 5 | this.tagid, 6 | this.tip, 7 | this.checked, 8 | }); 9 | 10 | int? count; 11 | String? name; 12 | int? tagid; 13 | String? tip; 14 | bool? checked; 15 | 16 | MemberTagItemModel.fromJson(Map json) { 17 | count = json['count']; 18 | name = json['name']; 19 | tagid = json['tagid']; 20 | tip = json['tip']; 21 | checked = false; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/models/model_owner.dart: -------------------------------------------------------------------------------- 1 | import 'package:hive/hive.dart'; 2 | 3 | part 'model_owner.g.dart'; 4 | 5 | @HiveType(typeId: 3) 6 | class Owner { 7 | Owner({ 8 | this.mid, 9 | this.name, 10 | this.face, 11 | }); 12 | @HiveField(0) 13 | int? mid; 14 | @HiveField(1) 15 | String? name; 16 | @HiveField(2) 17 | String? face; 18 | 19 | Owner.fromJson(Map json) { 20 | mid = json["mid"]; 21 | name = json["name"]; 22 | face = json['face']; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/models/model_owner.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'model_owner.dart'; 4 | 5 | // ************************************************************************** 6 | // TypeAdapterGenerator 7 | // ************************************************************************** 8 | 9 | class OwnerAdapter extends TypeAdapter { 10 | @override 11 | final int typeId = 3; 12 | 13 | @override 14 | Owner read(BinaryReader reader) { 15 | final numOfFields = reader.readByte(); 16 | final fields = { 17 | for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), 18 | }; 19 | return Owner( 20 | mid: fields[0] as int?, 21 | name: fields[1] as String?, 22 | face: fields[2] as String?, 23 | ); 24 | } 25 | 26 | @override 27 | void write(BinaryWriter writer, Owner obj) { 28 | writer 29 | ..writeByte(3) 30 | ..writeByte(0) 31 | ..write(obj.mid) 32 | ..writeByte(1) 33 | ..write(obj.name) 34 | ..writeByte(2) 35 | ..write(obj.face); 36 | } 37 | 38 | @override 39 | int get hashCode => typeId.hashCode; 40 | 41 | @override 42 | bool operator ==(Object other) => 43 | identical(this, other) || 44 | other is OwnerAdapter && 45 | runtimeType == other.runtimeType && 46 | typeId == other.typeId; 47 | } 48 | -------------------------------------------------------------------------------- /lib/models/msg/msgfeed_unread.dart: -------------------------------------------------------------------------------- 1 | class MsgFeedUnread { 2 | MsgFeedUnread({ 3 | this.at = 0, 4 | this.chat = 0, 5 | this.like = 0, 6 | this.reply = 0, 7 | this.sys_msg = 0, 8 | this.up = 0, 9 | }); 10 | 11 | int at = 0; 12 | int chat = 0; 13 | int like = 0; 14 | int reply = 0; 15 | int sys_msg = 0; 16 | int up = 0; 17 | 18 | MsgFeedUnread.fromJson(Map json) { 19 | at = json['at'] ?? 0; 20 | chat = json['chat'] ?? 0; 21 | like = json['like'] ?? 0; 22 | reply = json['reply'] ?? 0; 23 | sys_msg = json['sys_msg'] ?? 0; 24 | up = json['up'] ?? 0; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/models/search/hot.dart: -------------------------------------------------------------------------------- 1 | import 'package:hive/hive.dart'; 2 | 3 | part 'hot.g.dart'; 4 | 5 | @HiveType(typeId: 6) 6 | class HotSearchModel { 7 | HotSearchModel({ 8 | this.list, 9 | }); 10 | 11 | @HiveField(0) 12 | List? list; 13 | 14 | HotSearchModel.fromJson(Map json) { 15 | list = json['list'] 16 | .map((e) => HotSearchItem.fromJson(e)) 17 | .toList(); 18 | } 19 | } 20 | 21 | @HiveType(typeId: 7) 22 | class HotSearchItem { 23 | HotSearchItem({ 24 | this.keyword, 25 | this.showName, 26 | this.wordType, 27 | this.icon, 28 | }); 29 | 30 | @HiveField(0) 31 | String? keyword; 32 | @HiveField(1) 33 | String? showName; 34 | // 4/5热 11话题 8普通 7直播 35 | @HiveField(2) 36 | int? wordType; 37 | @HiveField(3) 38 | String? icon; 39 | 40 | HotSearchItem.fromJson(Map json) { 41 | keyword = json['keyword']; 42 | showName = json['show_name']; 43 | wordType = json['word_type']; 44 | icon = json['icon']; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/models/user/black.dart: -------------------------------------------------------------------------------- 1 | class BlackListDataModel { 2 | BlackListDataModel({ 3 | this.list, 4 | this.total, 5 | }); 6 | 7 | List? list; 8 | int? total; 9 | 10 | BlackListDataModel.fromJson(Map json) { 11 | list = json['list'] 12 | .map((e) => BlackListItem.fromJson(e)) 13 | .toList(); 14 | total = json['total']; 15 | } 16 | } 17 | 18 | class BlackListItem { 19 | BlackListItem({ 20 | this.face, 21 | this.mid, 22 | this.mtime, 23 | this.uname, 24 | }); 25 | 26 | String? face; 27 | int? mid; 28 | int? mtime; 29 | String? uname; 30 | 31 | BlackListItem.fromJson(Map json) { 32 | face = json['face']; 33 | mid = json['mid']; 34 | mtime = json['mtime']; 35 | uname = json['uname']; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/models/user/stat.dart: -------------------------------------------------------------------------------- 1 | import 'package:hive/hive.dart'; 2 | 3 | part 'stat.g.dart'; 4 | 5 | @HiveType(typeId: 1) 6 | class UserStat { 7 | UserStat({ 8 | this.following, 9 | this.follower, 10 | this.dynamicCount, 11 | }); 12 | 13 | @HiveField(0) 14 | int? following; 15 | @HiveField(1) 16 | int? follower; 17 | @HiveField(2) 18 | int? dynamicCount; 19 | 20 | UserStat.fromJson(Map json) { 21 | following = json['following']; 22 | follower = json['follower']; 23 | dynamicCount = json['dynamic_count']; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/models/user/stat.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'stat.dart'; 4 | 5 | // ************************************************************************** 6 | // TypeAdapterGenerator 7 | // ************************************************************************** 8 | 9 | class UserStatAdapter extends TypeAdapter { 10 | @override 11 | final int typeId = 1; 12 | 13 | @override 14 | UserStat read(BinaryReader reader) { 15 | final numOfFields = reader.readByte(); 16 | final fields = { 17 | for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), 18 | }; 19 | return UserStat( 20 | following: fields[0] as int?, 21 | follower: fields[1] as int?, 22 | dynamicCount: fields[2] as int?, 23 | ); 24 | } 25 | 26 | @override 27 | void write(BinaryWriter writer, UserStat obj) { 28 | writer 29 | ..writeByte(3) 30 | ..writeByte(0) 31 | ..write(obj.following) 32 | ..writeByte(1) 33 | ..write(obj.follower) 34 | ..writeByte(2) 35 | ..write(obj.dynamicCount); 36 | } 37 | 38 | @override 39 | int get hashCode => typeId.hashCode; 40 | 41 | @override 42 | bool operator ==(Object other) => 43 | identical(this, other) || 44 | other is UserStatAdapter && 45 | runtimeType == other.runtimeType && 46 | typeId == other.typeId; 47 | } 48 | -------------------------------------------------------------------------------- /lib/models/video/ai.dart: -------------------------------------------------------------------------------- 1 | class AiConclusionModel { 2 | AiConclusionModel({ 3 | this.code, 4 | this.modelResult, 5 | this.stid, 6 | this.status, 7 | this.likeNum, 8 | this.dislikeNum, 9 | }); 10 | 11 | int? code; 12 | ModelResult? modelResult; 13 | String? stid; 14 | int? status; 15 | int? likeNum; 16 | int? dislikeNum; 17 | 18 | AiConclusionModel.fromJson(Map json) { 19 | code = json['code']; 20 | modelResult = ModelResult.fromJson(json['model_result']); 21 | stid = json['stid']; 22 | status = json['status']; 23 | likeNum = json['like_num']; 24 | dislikeNum = json['dislike_num']; 25 | } 26 | } 27 | 28 | class ModelResult { 29 | ModelResult({ 30 | this.resultType, 31 | this.summary, 32 | this.outline, 33 | }); 34 | 35 | int? resultType; 36 | String? summary; 37 | List? outline; 38 | 39 | ModelResult.fromJson(Map json) { 40 | resultType = json['result_type']; 41 | summary = json['summary']; 42 | outline = json['result_type'] == 2 43 | ? json['outline'] 44 | .map((e) => OutlineItem.fromJson(e)) 45 | .toList() 46 | : []; 47 | } 48 | } 49 | 50 | class OutlineItem { 51 | OutlineItem({ 52 | this.title, 53 | this.partOutline, 54 | }); 55 | 56 | String? title; 57 | List? partOutline; 58 | 59 | OutlineItem.fromJson(Map json) { 60 | title = json['title']; 61 | partOutline = json['part_outline'] 62 | .map((e) => PartOutline.fromJson(e)) 63 | .toList(); 64 | } 65 | } 66 | 67 | class PartOutline { 68 | PartOutline({ 69 | this.timestamp, 70 | this.content, 71 | }); 72 | 73 | int? timestamp; 74 | String? content; 75 | 76 | PartOutline.fromJson(Map json) { 77 | timestamp = json['timestamp']; 78 | content = json['content']; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/models/video/reply/config.dart: -------------------------------------------------------------------------------- 1 | class ReplyConfig { 2 | ReplyConfig({ 3 | this.showtopic, 4 | this.showUpFlag, 5 | this.readOnly, 6 | }); 7 | 8 | int? showtopic; 9 | bool? showUpFlag; 10 | bool? readOnly; 11 | 12 | ReplyConfig.fromJson(Map json) { 13 | showtopic = json['showtopic']; 14 | showUpFlag = json['show_up_flag']; 15 | readOnly = json['read_only']; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/models/video/reply/content.dart: -------------------------------------------------------------------------------- 1 | class ReplyContent { 2 | ReplyContent({ 3 | this.message, 4 | this.atNameToMid, // @的用户的mid null 5 | this.members, // 被@的用户List 如果有的话 [] 6 | this.emote, // 表情包 如果有的话 null 7 | this.jumpUrl, // {} 8 | this.pictures, // {} 9 | this.vote, 10 | this.richText, 11 | this.isText, 12 | }); 13 | 14 | String? message; 15 | Map? atNameToMid; 16 | List? members; 17 | Map? emote; 18 | Map? jumpUrl; 19 | List? pictures; 20 | Map? vote; 21 | Map? richText; 22 | bool? isText; 23 | 24 | ReplyContent.fromJson(Map json) { 25 | message = json['message'] 26 | .replaceAll('>', '>') 27 | .replaceAll('"', '"') 28 | .replaceAll(''', "'"); 29 | atNameToMid = json['at_name_to_mid'] ?? {}; 30 | members = json['members'] != null 31 | ? json['members'] 32 | .map((e) => MemberItemModel.fromJson(e)) 33 | .toList() 34 | : []; 35 | emote = json['emote'] ?? {}; 36 | jumpUrl = json['jump_url'] ?? {}; 37 | pictures = json['pictures'] ?? []; 38 | vote = json['vote'] ?? {}; 39 | richText = json['rich_text'] ?? {}; 40 | // 不包含@ 笔记 图片的时候,文字可折叠 41 | isText = atNameToMid!.isEmpty && vote!.isEmpty && pictures!.isEmpty; 42 | } 43 | } 44 | 45 | class MemberItemModel { 46 | MemberItemModel({ 47 | required this.mid, 48 | required this.uname, 49 | }); 50 | 51 | late String mid; 52 | late String uname; 53 | 54 | MemberItemModel.fromJson(Map json) { 55 | mid = json['mid']; 56 | uname = json['uname']; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/models/video/reply/data.dart: -------------------------------------------------------------------------------- 1 | import 'package:PiliPalaX/models/video/reply/item.dart'; 2 | 3 | import 'config.dart'; 4 | import 'page.dart'; 5 | import 'upper.dart'; 6 | 7 | class ReplyData { 8 | ReplyData({ 9 | this.page, 10 | this.config, 11 | this.replies, 12 | this.topReplies, 13 | this.upper, 14 | }); 15 | 16 | ReplyPage? page; 17 | ReplyConfig? config; 18 | late List? replies; 19 | late List? topReplies; 20 | ReplyUpper? upper; 21 | 22 | ReplyData.fromJson(Map json) { 23 | page = ReplyPage.fromJson(json['page']); 24 | config = ReplyConfig.fromJson(json['config']); 25 | replies = json['replies'] != null 26 | ? json['replies'] 27 | .map( 28 | (item) => ReplyItemModel.fromJson(item, json['upper']['mid'])) 29 | .toList() 30 | : []; 31 | topReplies = json['top_replies'] != null 32 | ? json['top_replies'] 33 | .map((item) => ReplyItemModel.fromJson( 34 | item, json['upper']['mid'], 35 | isTopStatus: true)) 36 | .toList() 37 | : []; 38 | upper = ReplyUpper.fromJson(json['upper']); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/models/video/reply/member.dart: -------------------------------------------------------------------------------- 1 | class ReplyMember { 2 | ReplyMember({ 3 | this.mid, 4 | this.uname, 5 | this.sign, 6 | this.avatar, 7 | this.level, 8 | this.pendant, 9 | this.officialVerify, 10 | this.vip, 11 | this.fansDetail, 12 | }); 13 | 14 | String? mid; 15 | String? uname; 16 | String? sign; 17 | String? avatar; 18 | int? level; 19 | Pendant? pendant; 20 | Map? officialVerify; 21 | Map? vip; 22 | Map? fansDetail; 23 | UserSailing? userSailing; 24 | 25 | ReplyMember.fromJson(Map json) { 26 | mid = json['mid']; 27 | uname = json['uname']; 28 | sign = json['sign']; 29 | avatar = json['avatar']; 30 | level = json['level_info']['current_level']; 31 | pendant = Pendant.fromJson(json['pendant']); 32 | officialVerify = json['officia_verify']; 33 | vip = json['vip']; 34 | fansDetail = json['fans_detail']; 35 | userSailing = json['user_sailing'] != null 36 | ? UserSailing.fromJson(json['user_sailing']) 37 | : UserSailing(); 38 | } 39 | } 40 | 41 | class Pendant { 42 | Pendant({ 43 | this.pid, 44 | this.name, 45 | this.image, 46 | }); 47 | 48 | int? pid; 49 | String? name; 50 | String? image; 51 | 52 | Pendant.fromJson(Map json) { 53 | pid = json['pid']; 54 | name = json['name']; 55 | image = json['image']; 56 | } 57 | } 58 | 59 | class UserSailing { 60 | UserSailing({this.pendant, this.cardbg}); 61 | 62 | Map? pendant; 63 | Map? cardbg; 64 | 65 | UserSailing.fromJson(Map json) { 66 | pendant = json['pendant']; 67 | cardbg = json['cardbg']; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/models/video/reply/page.dart: -------------------------------------------------------------------------------- 1 | class ReplyPage { 2 | ReplyPage({ 3 | this.num, 4 | this.size, 5 | this.count, 6 | this.acount, 7 | }); 8 | 9 | int? num; 10 | int? size; 11 | int? count; 12 | int? acount; 13 | 14 | ReplyPage.fromJson(Map json) { 15 | num = json['num']; 16 | size = json['size']; 17 | count = json['count']; 18 | acount = json['acount']; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/models/video/reply/top_replies.dart: -------------------------------------------------------------------------------- 1 | class ReplyTop {} 2 | -------------------------------------------------------------------------------- /lib/models/video/reply/upper.dart: -------------------------------------------------------------------------------- 1 | import 'item.dart'; 2 | 3 | class ReplyUpper { 4 | ReplyUpper({ 5 | this.mid, 6 | this.top, 7 | }); 8 | 9 | int? mid; 10 | ReplyItemModel? top; 11 | 12 | ReplyUpper.fromJson(Map json) { 13 | mid = json['mid']; 14 | top = json['top'] != null 15 | ? ReplyItemModel.fromJson(json['top'], json['mid']) 16 | : null; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/pages/bangumi/index.dart: -------------------------------------------------------------------------------- 1 | library bangumi_panel; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/bangumi/introduction/index.dart: -------------------------------------------------------------------------------- 1 | library bangumi_intro; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/danmaku/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:PiliPalaX/http/danmaku.dart'; 2 | import 'package:PiliPalaX/models/danmaku/dm.pb.dart'; 3 | 4 | class PlDanmakuController { 5 | PlDanmakuController(this.cid); 6 | final int cid; 7 | Map> dmSegMap = {}; 8 | // 已请求的段落标记 9 | List requestedSeg = []; 10 | 11 | bool get initiated => requestedSeg.isNotEmpty; 12 | 13 | static int segmentLength = 60 * 6 * 1000; 14 | 15 | void initiate(int videoDuration, int progress) { 16 | if (requestedSeg.isEmpty) { 17 | int segCount = (videoDuration / segmentLength).ceil(); 18 | requestedSeg = List.generate(segCount, (index) => false); 19 | } 20 | queryDanmaku(calcSegment(progress)); 21 | } 22 | 23 | void dispose() { 24 | dmSegMap.clear(); 25 | requestedSeg.clear(); 26 | } 27 | 28 | int calcSegment(int progress) { 29 | return progress ~/ segmentLength; 30 | } 31 | 32 | void queryDanmaku(int segmentIndex) async { 33 | assert(requestedSeg[segmentIndex] == false); 34 | requestedSeg[segmentIndex] = true; 35 | final DmSegMobileReply result = await DanmakaHttp.queryDanmaku( 36 | cid: cid, segmentIndex: segmentIndex + 1); 37 | if (result.elems.isNotEmpty) { 38 | for (var element in result.elems) { 39 | int pos = element.progress ~/ 100; //每0.1秒存储一次 40 | if (dmSegMap[pos] == null) { 41 | dmSegMap[pos] = []; 42 | } 43 | dmSegMap[pos]!.add(element); 44 | } 45 | } 46 | } 47 | 48 | List? getCurrentDanmaku(int progress) { 49 | int segmentIndex = calcSegment(progress); 50 | if (!requestedSeg[segmentIndex]) { 51 | queryDanmaku(segmentIndex); 52 | } 53 | return dmSegMap[progress ~/ 100]; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/pages/danmaku/index.dart: -------------------------------------------------------------------------------- 1 | library pldanmaku; 2 | 3 | export './controller.dart'; 4 | export 'view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/dynamics/detail/index.dart: -------------------------------------------------------------------------------- 1 | library dynamic_detail; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/dynamics/index.dart: -------------------------------------------------------------------------------- 1 | library dynamics; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/fan/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:hive/hive.dart'; 4 | import 'package:PiliPalaX/http/fan.dart'; 5 | import 'package:PiliPalaX/models/fans/result.dart'; 6 | import 'package:PiliPalaX/utils/storage.dart'; 7 | 8 | class FansController extends GetxController { 9 | Box userInfoCache = GStrorage.userInfo; 10 | int pn = 1; 11 | int ps = 20; 12 | int total = 0; 13 | RxList fansList = [FansItemModel()].obs; 14 | late int mid; 15 | late String name; 16 | var userInfo; 17 | RxString loadingText = '加载中...'.obs; 18 | RxBool isOwner = false.obs; 19 | 20 | @override 21 | void onInit() { 22 | super.onInit(); 23 | userInfo = userInfoCache.get('userInfoCache'); 24 | mid = Get.parameters['mid'] != null 25 | ? int.parse(Get.parameters['mid']!) 26 | : userInfo.mid; 27 | isOwner.value = mid == userInfo.mid; 28 | name = Get.parameters['name'] ?? userInfo.uname; 29 | } 30 | 31 | Future queryFans(type) async { 32 | if (type == 'init') { 33 | pn = 1; 34 | loadingText.value == '加载中...'; 35 | } 36 | if (loadingText.value == '没有更多了') { 37 | return; 38 | } 39 | var res = await FanHttp.fans( 40 | vmid: mid, 41 | pn: pn, 42 | ps: ps, 43 | orderType: 'attention', 44 | ); 45 | if (res['status']) { 46 | if (type == 'init') { 47 | fansList.value = res['data'].list; 48 | total = res['data'].total; 49 | } else if (type == 'onLoad') { 50 | fansList.addAll(res['data'].list); 51 | } 52 | print(total); 53 | if ((pn == 1 && total < ps) || res['data'].list.isEmpty) { 54 | loadingText.value = '没有更多了'; 55 | } 56 | pn += 1; 57 | } else { 58 | SmartDialog.showToast(res['msg']); 59 | } 60 | return res; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/pages/fan/index.dart: -------------------------------------------------------------------------------- 1 | library fan; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/fan/widgets/fan_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:PiliPalaX/common/widgets/network_img_layer.dart'; 4 | import 'package:PiliPalaX/utils/utils.dart'; 5 | 6 | Widget fanItem({item}) { 7 | String heroTag = Utils.makeHeroTag(item!.mid); 8 | return ListTile( 9 | onTap: () => Get.toNamed('/member?mid=${item.mid}', 10 | arguments: {'face': item.face, 'heroTag': heroTag}), 11 | leading: Hero( 12 | tag: heroTag, 13 | child: NetworkImgLayer( 14 | width: 38, 15 | height: 38, 16 | type: 'avatar', 17 | src: item.face, 18 | ), 19 | ), 20 | title: Text(item.uname), 21 | subtitle: Text( 22 | item.sign, 23 | maxLines: 1, 24 | overflow: TextOverflow.ellipsis, 25 | ), 26 | dense: true, 27 | trailing: const SizedBox(width: 6), 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /lib/pages/fav/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:hive/hive.dart'; 5 | import 'package:PiliPalaX/http/user.dart'; 6 | import 'package:PiliPalaX/models/user/fav_folder.dart'; 7 | import 'package:PiliPalaX/models/user/info.dart'; 8 | import 'package:PiliPalaX/utils/storage.dart'; 9 | 10 | class FavController extends GetxController { 11 | final ScrollController scrollController = ScrollController(); 12 | Rx favFolderData = FavFolderData().obs; 13 | Box userInfoCache = GStrorage.userInfo; 14 | UserInfoData? userInfo; 15 | int currentPage = 1; 16 | int pageSize = 10; 17 | RxBool hasMore = true.obs; 18 | 19 | Future queryFavFolder({type = 'init'}) async { 20 | userInfo = userInfoCache.get('userInfoCache'); 21 | if (userInfo == null) { 22 | return {'status': false, 'msg': '账号未登录'}; 23 | } 24 | if (!hasMore.value) { 25 | return; 26 | } 27 | var res = await await UserHttp.userfavFolder( 28 | pn: currentPage, 29 | ps: pageSize, 30 | mid: userInfo!.mid!, 31 | ); 32 | if (res['status']) { 33 | if (type == 'init') { 34 | favFolderData.value = res['data']; 35 | } else { 36 | if (res['data'].list.isNotEmpty) { 37 | favFolderData.value.list!.addAll(res['data'].list); 38 | favFolderData.update((val) {}); 39 | } 40 | } 41 | hasMore.value = res['data'].hasMore; 42 | currentPage++; 43 | } else { 44 | SmartDialog.showToast(res['msg']); 45 | } 46 | return res; 47 | } 48 | 49 | Future onLoad() async { 50 | queryFavFolder(type: 'onload'); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/pages/fav/index.dart: -------------------------------------------------------------------------------- 1 | library fav; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/fav_detail/index.dart: -------------------------------------------------------------------------------- 1 | library favdetail; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/fav_search/index.dart: -------------------------------------------------------------------------------- 1 | library fav_search; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/follow/index.dart: -------------------------------------------------------------------------------- 1 | library following; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/follow_search/index.dart: -------------------------------------------------------------------------------- 1 | library follow_search; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/history/index.dart: -------------------------------------------------------------------------------- 1 | library history; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/history_search/index.dart: -------------------------------------------------------------------------------- 1 | library history_search; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/home/index.dart: -------------------------------------------------------------------------------- 1 | library home; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/hot/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:PiliPalaX/http/video.dart'; 4 | import 'package:PiliPalaX/models/model_hot_video_item.dart'; 5 | 6 | class HotController extends GetxController { 7 | final ScrollController scrollController = ScrollController(); 8 | final int _count = 20; 9 | int _currentPage = 1; 10 | RxList videoList = [HotVideoItemModel()].obs; 11 | bool isLoadingMore = false; 12 | bool flag = false; 13 | OverlayEntry? popupDialog; 14 | 15 | // 获取推荐 16 | Future queryHotFeed(type) async { 17 | var res = await VideoHttp.hotVideoList( 18 | pn: _currentPage, 19 | ps: _count, 20 | ); 21 | if (res['status']) { 22 | if (type == 'init') { 23 | videoList.value = res['data']; 24 | } else if (type == 'onRefresh') { 25 | videoList.insertAll(0, res['data']); 26 | } else if (type == 'onLoad') { 27 | videoList.addAll(res['data']); 28 | } 29 | _currentPage += 1; 30 | } 31 | isLoadingMore = false; 32 | return res; 33 | } 34 | 35 | // 下拉刷新 36 | Future onRefresh() async { 37 | queryHotFeed('onRefresh'); 38 | } 39 | 40 | // 上拉加载 41 | Future onLoad() async { 42 | queryHotFeed('onLoad'); 43 | } 44 | 45 | // 返回顶部并刷新 46 | void animateToTop() async { 47 | if (scrollController.offset >= 48 | MediaQuery.of(Get.context!).size.height * 5) { 49 | scrollController.jumpTo(0); 50 | } else { 51 | await scrollController.animateTo(0, 52 | duration: const Duration(milliseconds: 500), curve: Curves.easeInOut); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/pages/hot/index.dart: -------------------------------------------------------------------------------- 1 | library hot; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/html/index.dart: -------------------------------------------------------------------------------- 1 | library html_render; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/later/index.dart: -------------------------------------------------------------------------------- 1 | library later; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/live/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:hive/hive.dart'; 4 | import 'package:PiliPalaX/http/live.dart'; 5 | import 'package:PiliPalaX/models/live/item.dart'; 6 | import 'package:PiliPalaX/utils/storage.dart'; 7 | 8 | class LiveController extends GetxController { 9 | final ScrollController scrollController = ScrollController(); 10 | int count = 12; 11 | int _currentPage = 1; 12 | RxInt crossAxisCount = 2.obs; 13 | RxList liveList = [].obs; 14 | bool flag = false; 15 | OverlayEntry? popupDialog; 16 | Box setting = GStrorage.setting; 17 | 18 | @override 19 | void onInit() { 20 | super.onInit(); 21 | } 22 | 23 | // 获取推荐 24 | Future queryLiveList(type) async { 25 | // if (type == 'init') { 26 | // _currentPage = 1; 27 | // } 28 | var res = await LiveHttp.liveList( 29 | pn: _currentPage, 30 | ); 31 | if (res['status']) { 32 | if (type == 'init') { 33 | liveList.value = res['data']; 34 | } else if (type == 'onLoad') { 35 | liveList.addAll(res['data']); 36 | } 37 | _currentPage += 1; 38 | } 39 | return res; 40 | } 41 | 42 | // 下拉刷新 43 | Future onRefresh() async { 44 | queryLiveList('init'); 45 | } 46 | 47 | // 上拉加载 48 | Future onLoad() async { 49 | queryLiveList('onLoad'); 50 | } 51 | 52 | // 返回顶部并刷新 53 | void animateToTop() async { 54 | if (scrollController.offset >= 55 | MediaQuery.of(Get.context!).size.height * 5) { 56 | scrollController.jumpTo(0); 57 | } else { 58 | await scrollController.animateTo(0, 59 | duration: const Duration(milliseconds: 500), curve: Curves.easeInOut); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/pages/live/index.dart: -------------------------------------------------------------------------------- 1 | library live; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/live_room/index.dart: -------------------------------------------------------------------------------- 1 | library liveroom; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/login/index.dart: -------------------------------------------------------------------------------- 1 | library login; 2 | 3 | export './controller.dart'; 4 | export 'view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/main/index.dart: -------------------------------------------------------------------------------- 1 | library main; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/media/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:hive/hive.dart'; 5 | import 'package:PiliPalaX/http/user.dart'; 6 | import 'package:PiliPalaX/models/user/fav_folder.dart'; 7 | import 'package:PiliPalaX/utils/storage.dart'; 8 | 9 | class MediaController extends GetxController { 10 | Rx favFolderData = FavFolderData().obs; 11 | Box userInfoCache = GStrorage.userInfo; 12 | RxBool userLogin = false.obs; 13 | List list = [ 14 | { 15 | 'icon': Icons.file_download_outlined, 16 | 'title': '离线缓存', 17 | 'onTap': () { 18 | SmartDialog.showToast('功能开发中'); 19 | }, 20 | }, 21 | { 22 | 'icon': Icons.history, 23 | 'title': '观看记录', 24 | 'onTap': () => Get.toNamed('/history'), 25 | }, 26 | { 27 | 'icon': Icons.star_border, 28 | 'title': '我的收藏', 29 | 'onTap': () => Get.toNamed('/fav'), 30 | }, 31 | { 32 | 'icon': Icons.watch_later_outlined, 33 | 'title': '稍后再看', 34 | 'onTap': () => Get.toNamed('/later'), 35 | }, 36 | ]; 37 | var userInfo; 38 | int? mid; 39 | final ScrollController scrollController = ScrollController(); 40 | 41 | @override 42 | void onInit() { 43 | super.onInit(); 44 | userInfo = userInfoCache.get('userInfoCache'); 45 | userLogin.value = userInfo != null; 46 | } 47 | 48 | Future queryFavFolder() async { 49 | if (!userLogin.value) { 50 | return {'status': false, 'data': [], 'msg': '未登录'}; 51 | } 52 | var res = await await UserHttp.userfavFolder( 53 | pn: 1, 54 | ps: 5, 55 | mid: mid ?? GStrorage.userInfo.get('userInfoCache').mid, 56 | ); 57 | favFolderData.value = res['data']; 58 | return res; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/pages/media/index.dart: -------------------------------------------------------------------------------- 1 | library media; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/member/index.dart: -------------------------------------------------------------------------------- 1 | library member; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/member/widgets/conis.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:PiliPalaX/common/constants.dart'; 3 | import 'package:PiliPalaX/models/member/coin.dart'; 4 | import 'package:PiliPalaX/pages/member_coin/widgets/item.dart'; 5 | 6 | class MemberCoinsPanel extends StatelessWidget { 7 | final List? data; 8 | const MemberCoinsPanel({super.key, this.data}); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return LayoutBuilder( 13 | builder: (context, boxConstraints) { 14 | return GridView.builder( 15 | gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( 16 | crossAxisCount: 2, // Use a fixed count for GridView 17 | crossAxisSpacing: StyleString.safeSpace, 18 | mainAxisSpacing: StyleString.safeSpace, 19 | childAspectRatio: 0.94, 20 | ), 21 | physics: const NeverScrollableScrollPhysics(), 22 | shrinkWrap: true, 23 | itemCount: data!.length, 24 | itemBuilder: (context, i) { 25 | return MemberCoinsItem(coinItem: data![i]); 26 | }, 27 | ); 28 | }, 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/pages/member_archive/index.dart: -------------------------------------------------------------------------------- 1 | library member_archive; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/member_coin/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | 3 | class MemberCoinController extends GetxController {} 4 | -------------------------------------------------------------------------------- /lib/pages/member_coin/index.dart: -------------------------------------------------------------------------------- 1 | library member_coin; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/member_coin/view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class MemberCoinPage extends StatefulWidget { 4 | const MemberCoinPage({super.key}); 5 | 6 | @override 7 | State createState() => _MemberCoinPageState(); 8 | } 9 | 10 | class _MemberCoinPageState extends State { 11 | @override 12 | Widget build(BuildContext context) { 13 | return Scaffold(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/pages/member_dynamics/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:PiliPalaX/http/member.dart'; 4 | import 'package:PiliPalaX/models/dynamics/result.dart'; 5 | 6 | class MemberDynamicsController extends GetxController { 7 | final ScrollController scrollController = ScrollController(); 8 | late int mid; 9 | String offset = ''; 10 | int count = 0; 11 | bool hasMore = true; 12 | RxList dynamicsList = [].obs; 13 | 14 | @override 15 | void onInit() { 16 | super.onInit(); 17 | mid = int.parse(Get.parameters['mid']!); 18 | } 19 | 20 | Future getMemberDynamic(type) async { 21 | if (type == 'onRefresh') { 22 | offset = ''; 23 | dynamicsList.clear(); 24 | } 25 | if (offset == '-1') { 26 | return; 27 | } 28 | var res = await MemberHttp.memberDynamic( 29 | offset: offset, 30 | mid: mid, 31 | ); 32 | if (res['status']) { 33 | dynamicsList.addAll(res['data'].items); 34 | offset = res['data'].offset != '' ? res['data'].offset : '-1'; 35 | hasMore = res['data'].hasMore; 36 | } 37 | return res; 38 | } 39 | 40 | // 上拉加载 41 | Future onLoad() async { 42 | getMemberDynamic('onLoad'); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/pages/member_dynamics/index.dart: -------------------------------------------------------------------------------- 1 | library member_dynamics; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/member_like/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | 3 | class MemberLikeController extends GetxController {} 4 | -------------------------------------------------------------------------------- /lib/pages/member_like/index.dart: -------------------------------------------------------------------------------- 1 | library member_like; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/member_like/view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class MemberLikePage extends StatefulWidget { 4 | const MemberLikePage({super.key}); 5 | 6 | @override 7 | State createState() => _MemberLikePageState(); 8 | } 9 | 10 | class _MemberLikePageState extends State { 11 | @override 12 | Widget build(BuildContext context) { 13 | return Scaffold(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/pages/member_search/index.dart: -------------------------------------------------------------------------------- 1 | library member_search; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/member_seasons/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:PiliPalaX/http/member.dart'; 4 | import 'package:PiliPalaX/models/member/seasons.dart'; 5 | 6 | class MemberSeasonsController extends GetxController { 7 | final ScrollController scrollController = ScrollController(); 8 | late int mid; 9 | late int seasonId; 10 | int pn = 1; 11 | int ps = 30; 12 | int count = 0; 13 | RxList seasonsList = [].obs; 14 | late Map page; 15 | 16 | @override 17 | void onInit() { 18 | super.onInit(); 19 | mid = int.parse(Get.parameters['mid']!); 20 | seasonId = int.parse(Get.parameters['seasonId']!); 21 | } 22 | 23 | // 获取专栏详情 24 | Future getSeasonDetail(type) async { 25 | if (type == 'onRefresh') { 26 | pn = 1; 27 | } 28 | var res = await MemberHttp.getSeasonDetail( 29 | mid: mid, 30 | seasonId: seasonId, 31 | pn: pn, 32 | ps: ps, 33 | sortReverse: false, 34 | ); 35 | if (res['status']) { 36 | seasonsList.addAll(res['data'].archives); 37 | page = res['data'].page; 38 | pn += 1; 39 | } 40 | return res; 41 | } 42 | 43 | // 上拉加载 44 | Future onLoad() async { 45 | getSeasonDetail('onLoad'); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/pages/member_seasons/index.dart: -------------------------------------------------------------------------------- 1 | library member_seasons; 2 | 3 | export 'controller.dart'; 4 | export 'view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/mine/index.dart: -------------------------------------------------------------------------------- 1 | library mine; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/msg_feed_top/at_me/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:PiliPalaX/http/msg.dart'; 4 | import 'package:PiliPalaX/models/msg/msgfeed_at_me.dart'; 5 | 6 | class AtMeController extends GetxController { 7 | RxList msgFeedAtMeList = [].obs; 8 | bool isLoading = false; 9 | int cursor = -1; 10 | int cursorTime = -1; 11 | bool isEnd = false; 12 | 13 | Future queryMsgFeedAtMe() async { 14 | if (isLoading) return; 15 | isLoading = true; 16 | var res = await MsgHttp.msgFeedAtMe(cursor: cursor, cursorTime: cursorTime); 17 | isLoading = false; 18 | if (res['status']) { 19 | MsgFeedAtMe data = MsgFeedAtMe.fromJson(res['data']); 20 | isEnd = data.cursor?.isEnd ?? false; 21 | if (cursor == -1) { 22 | msgFeedAtMeList.assignAll(data.items!); 23 | } else { 24 | msgFeedAtMeList.addAll(data.items!); 25 | } 26 | cursor = data.cursor?.id ?? -1; 27 | cursorTime = data.cursor?.time ?? -1; 28 | } else { 29 | SmartDialog.showToast(res['msg']); 30 | } 31 | } 32 | 33 | Future onLoad() async { 34 | if (isEnd) return; 35 | queryMsgFeedAtMe(); 36 | } 37 | 38 | Future onRefresh() async { 39 | cursor = -1; 40 | cursorTime = -1; 41 | queryMsgFeedAtMe(); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /lib/pages/msg_feed_top/at_me/index.dart: -------------------------------------------------------------------------------- 1 | library whisper; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/msg_feed_top/like_me/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:PiliPalaX/http/msg.dart'; 4 | import '../../../models/msg/msgfeed_like_me.dart'; 5 | 6 | class LikeMeController extends GetxController { 7 | RxList msgFeedLikeMeLatestList = [].obs; 8 | RxList msgFeedLikeMeTotalList = [].obs; 9 | bool isLoading = false; 10 | int cursor = -1; 11 | int cursorTime = -1; 12 | bool isEnd = false; 13 | 14 | Future queryMsgFeedLikeMe() async { 15 | if (isLoading) return; 16 | isLoading = true; 17 | var res = await MsgHttp.msgFeedLikeMe(cursor: cursor, cursorTime: cursorTime); 18 | isLoading = false; 19 | if (res['status']) { 20 | MsgFeedLikeMe data = MsgFeedLikeMe.fromJson(res['data']); 21 | isEnd = data.total?.cursor?.isEnd ?? false; 22 | if (cursor == -1) { 23 | msgFeedLikeMeLatestList.assignAll(data.latest?.items??[]); 24 | msgFeedLikeMeTotalList.assignAll(data.total?.items??[]); 25 | } else { 26 | msgFeedLikeMeLatestList.addAll(data.latest?.items??[]); 27 | msgFeedLikeMeTotalList.addAll(data.total?.items??[]); 28 | } 29 | cursor = data.total?.cursor?.id ?? -1; 30 | cursorTime = data.total?.cursor?.time ?? -1; 31 | } else { 32 | SmartDialog.showToast(res['msg']); 33 | } 34 | } 35 | 36 | Future onLoad() async { 37 | if (isEnd) return; 38 | queryMsgFeedLikeMe(); 39 | } 40 | 41 | Future onRefresh() async { 42 | cursor = -1; 43 | cursorTime = -1; 44 | queryMsgFeedLikeMe(); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /lib/pages/msg_feed_top/like_me/index.dart: -------------------------------------------------------------------------------- 1 | library whisper; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/msg_feed_top/reply_me/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:PiliPalaX/http/msg.dart'; 4 | 5 | import '../../../models/msg/msgfeed_reply_me.dart'; 6 | 7 | class ReplyMeController extends GetxController { 8 | RxList msgFeedReplyMeList = [].obs; 9 | bool isLoading = false; 10 | int cursor = -1; 11 | int cursorTime = -1; 12 | bool isEnd = false; 13 | 14 | Future queryMsgFeedReplyMe() async { 15 | if (isLoading) return; 16 | isLoading = true; 17 | var res = await MsgHttp.msgFeedReplyMe(cursor: cursor, cursorTime: cursorTime); 18 | isLoading = false; 19 | if (res['status']) { 20 | MsgFeedReplyMe data = MsgFeedReplyMe.fromJson(res['data']); 21 | isEnd = data.cursor?.isEnd ?? false; 22 | if (cursor == -1) { 23 | msgFeedReplyMeList.assignAll(data.items!); 24 | } else { 25 | msgFeedReplyMeList.addAll(data.items!); 26 | } 27 | cursor = data.cursor?.id ?? -1; 28 | cursorTime = data.cursor?.time ?? -1; 29 | } else { 30 | SmartDialog.showToast(res['msg']); 31 | } 32 | } 33 | 34 | Future onLoad() async { 35 | if (isEnd) return; 36 | queryMsgFeedReplyMe(); 37 | } 38 | 39 | Future onRefresh() async { 40 | cursor = -1; 41 | cursorTime = -1; 42 | queryMsgFeedReplyMe(); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /lib/pages/msg_feed_top/reply_me/index.dart: -------------------------------------------------------------------------------- 1 | library whisper; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/preview/controller.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:device_info_plus/device_info_plus.dart'; 4 | import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; 5 | import 'package:get/get.dart'; 6 | import 'package:dio/dio.dart'; 7 | import 'package:path_provider/path_provider.dart'; 8 | import 'package:permission_handler/permission_handler.dart'; 9 | import 'package:share_plus/share_plus.dart'; 10 | 11 | class PreviewController extends GetxController { 12 | DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); 13 | RxInt initialPage = 0.obs; 14 | RxInt currentPage = 1.obs; 15 | RxList imgList = [].obs; 16 | bool storage = true; 17 | bool videos = true; 18 | bool photos = true; 19 | String currentImgUrl = ''; 20 | 21 | requestPermission() async { 22 | Map statuses = await [ 23 | Permission.storage, 24 | // Permission.photos 25 | ].request(); 26 | 27 | statuses[Permission.storage].toString(); 28 | // final photosInfo = statuses[Permission.photos].toString(); 29 | } 30 | 31 | // 图片分享 32 | void onShareImg() async { 33 | SmartDialog.showLoading(); 34 | var response = await Dio().get(imgList[initialPage.value], 35 | options: Options(responseType: ResponseType.bytes)); 36 | final temp = await getTemporaryDirectory(); 37 | SmartDialog.dismiss(); 38 | String imgName = 39 | "plpl_pic_${DateTime.now().toString().split('-').join()}.jpg"; 40 | var path = '${temp.path}/$imgName'; 41 | File(path).writeAsBytesSync(response.data); 42 | Share.shareXFiles([XFile(path)], subject: imgList[initialPage.value]); 43 | } 44 | 45 | void onChange(int index) { 46 | initialPage.value = index; 47 | currentPage.value = index + 1; 48 | currentImgUrl = imgList[index]; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/pages/preview/index.dart: -------------------------------------------------------------------------------- 1 | library preview; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/rcmd/index.dart: -------------------------------------------------------------------------------- 1 | library recm_panel; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/search/index.dart: -------------------------------------------------------------------------------- 1 | library search; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/search/widgets/search_text.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class SearchText extends StatelessWidget { 4 | final String? searchText; 5 | final Function? onSelect; 6 | final int? searchTextIdx; 7 | final Function? onLongSelect; 8 | const SearchText({ 9 | super.key, 10 | this.searchText, 11 | this.onSelect, 12 | this.searchTextIdx, 13 | this.onLongSelect, 14 | }); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return Material( 19 | color: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.5), 20 | borderRadius: BorderRadius.circular(6), 21 | child: Padding( 22 | padding: EdgeInsets.zero, 23 | child: InkWell( 24 | onTap: () { 25 | onSelect!(searchText); 26 | }, 27 | onLongPress: () { 28 | onLongSelect!(searchText); 29 | }, 30 | borderRadius: BorderRadius.circular(6), 31 | child: Padding( 32 | padding: 33 | const EdgeInsets.only(top: 5, bottom: 5, left: 11, right: 11), 34 | child: Text( 35 | searchText!, 36 | style: TextStyle( 37 | color: Theme.of(context).colorScheme.onSurfaceVariant), 38 | ), 39 | ), 40 | ), 41 | ), 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/pages/search_panel/index.dart: -------------------------------------------------------------------------------- 1 | library searchpanel; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/search_result/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | 3 | class SearchResultController extends GetxController { 4 | String? keyword; 5 | int tabIndex = 0; 6 | 7 | @override 8 | void onInit() { 9 | super.onInit(); 10 | if (Get.parameters.keys.isNotEmpty) { 11 | keyword = Get.parameters['keyword']; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/pages/search_result/index.dart: -------------------------------------------------------------------------------- 1 | library searchresult; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/setting/index.dart: -------------------------------------------------------------------------------- 1 | library setting; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/video/README.md: -------------------------------------------------------------------------------- 1 | 视频详情页预渲染 2 | + videoItem 3 | + title 4 | + stat 5 | + view 6 | + danmaku 7 | + pubdate 8 | + owner 9 | + face 10 | + name -------------------------------------------------------------------------------- /lib/pages/video/detail/index.dart: -------------------------------------------------------------------------------- 1 | library video_detail; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/video/detail/introduction/index.dart: -------------------------------------------------------------------------------- 1 | library video_intro_panel; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/video/detail/related/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:PiliPalaX/http/video.dart'; 4 | import '../../../../models/model_hot_video_item.dart'; 5 | 6 | class ReleatedController extends GetxController { 7 | // 视频aid 8 | String bvid = Get.parameters['bvid'] ?? ""; 9 | // 推荐视频列表 10 | RxList relatedVideoList = [].obs; 11 | 12 | OverlayEntry? popupDialog; 13 | 14 | Future queryRelatedVideo() async { 15 | return VideoHttp.relatedVideoList(bvid: bvid).then((value) { 16 | if (value['status']) { 17 | relatedVideoList.value = value['data']; 18 | } 19 | return value; 20 | }); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/pages/video/detail/related/index.dart: -------------------------------------------------------------------------------- 1 | library releated_video_panel; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/video/detail/reply/index.dart: -------------------------------------------------------------------------------- 1 | library video_reply_panel; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/video/detail/reply_new/index.dart: -------------------------------------------------------------------------------- 1 | library video_reply_new; 2 | 3 | export './view.dart'; -------------------------------------------------------------------------------- /lib/pages/video/detail/reply_reply/index.dart: -------------------------------------------------------------------------------- 1 | library video_reply_reply_panel; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/webview/index.dart: -------------------------------------------------------------------------------- 1 | library webview; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/whisper/index.dart: -------------------------------------------------------------------------------- 1 | library whisper; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/whisper_detail/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:PiliPalaX/http/msg.dart'; 4 | import 'package:PiliPalaX/models/msg/session.dart'; 5 | 6 | class WhisperDetailController extends GetxController { 7 | late int talkerId; 8 | late String name; 9 | late String face; 10 | late String mid; 11 | RxList messageList = [].obs; 12 | //表情转换图片规则 13 | List? eInfos; 14 | 15 | @override 16 | void onInit() { 17 | super.onInit(); 18 | talkerId = int.parse(Get.parameters['talkerId']!); 19 | name = Get.parameters['name']!; 20 | face = Get.parameters['face']!; 21 | mid = Get.parameters['mid']!; 22 | } 23 | 24 | Future querySessionMsg() async { 25 | var res = await MsgHttp.sessionMsg(talkerId: talkerId); 26 | if (res['status']) { 27 | messageList.value = res['data'].messages; 28 | if (messageList.isNotEmpty && res['data'].eInfos != null) { 29 | eInfos = res['data'].eInfos; 30 | } 31 | } 32 | return res; 33 | } 34 | 35 | Future ackSessionMsg() async { 36 | if (messageList.isEmpty){ 37 | return; 38 | } 39 | var res = await MsgHttp.ackSessionMsg( 40 | talkerId: talkerId, 41 | ackSeqno: messageList.last.msgSeqno, 42 | ); 43 | if (res['status']) { 44 | SmartDialog.showToast("已读成功"); 45 | } else { 46 | SmartDialog.showToast(res['msg']); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/pages/whisper_detail/index.dart: -------------------------------------------------------------------------------- 1 | library whisper_detail; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/plugin/pl_player/index.dart: -------------------------------------------------------------------------------- 1 | library pl_player; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | export './models/data_source.dart'; 6 | export './models/play_status.dart'; 7 | export './models/data_status.dart'; 8 | export './widgets/common_btn.dart'; 9 | export './models/play_speed.dart'; 10 | export './models/fullscreen_mode.dart'; 11 | export './models/bottom_progress_behavior.dart'; 12 | export './widgets/app_bar_ani.dart'; 13 | export './utils/fullscreen.dart'; 14 | export './utils.dart'; 15 | -------------------------------------------------------------------------------- /lib/plugin/pl_player/models/bottom_progress_behavior.dart: -------------------------------------------------------------------------------- 1 | // ignore: camel_case_types 2 | enum BtmProgresBehavior { 3 | alwaysShow, 4 | alwaysHide, 5 | onlyShowFullScreen, 6 | onlyHideFullScreen, 7 | } 8 | 9 | extension BtmProgresBehaviorDesc on BtmProgresBehavior { 10 | String get description => ['始终展示', '始终隐藏', '仅全屏时展示', '仅全屏时隐藏'][index]; 11 | } 12 | 13 | extension BtmProgresBehaviorCode on BtmProgresBehavior { 14 | static final List _codeList = [0, 1, 2, 3]; 15 | int get code => _codeList[index]; 16 | 17 | static BtmProgresBehavior? fromCode(int code) { 18 | final index = _codeList.indexOf(code); 19 | if (index != -1) { 20 | return BtmProgresBehavior.values[index]; 21 | } 22 | return null; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/plugin/pl_player/models/data_source.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | /// The way in which the video was originally loaded. 4 | /// 5 | /// This has nothing to do with the video's file type. It's just the place 6 | /// from which the video is fetched from. 7 | enum DataSourceType { 8 | /// The video was included in the app's asset files. 9 | asset, 10 | 11 | /// The video was downloaded from the internet. 12 | network, 13 | 14 | /// The video was loaded off of the local filesystem. 15 | file, 16 | 17 | /// The video is available via contentUri. Android only. 18 | contentUri, 19 | } 20 | 21 | class DataSource { 22 | File? file; 23 | String? videoSource; 24 | String? audioSource; 25 | String? subFiles; 26 | DataSourceType type; 27 | Map? httpHeaders; // for headers 28 | DataSource({ 29 | this.file, 30 | this.videoSource, 31 | this.audioSource, 32 | this.subFiles, 33 | required this.type, 34 | this.httpHeaders, 35 | }) : assert((type == DataSourceType.file && file != null) || 36 | videoSource != null); 37 | 38 | DataSource copyWith({ 39 | File? file, 40 | String? videoSource, 41 | String? audioSource, 42 | String? subFiles, 43 | DataSourceType? type, 44 | Map? httpHeaders, 45 | }) { 46 | return DataSource( 47 | file: file ?? this.file, 48 | videoSource: videoSource ?? this.videoSource, 49 | audioSource: audioSource ?? this.audioSource, 50 | subFiles: subFiles ?? this.subFiles, 51 | type: type ?? this.type, 52 | httpHeaders: httpHeaders ?? this.httpHeaders, 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/plugin/pl_player/models/data_status.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | 3 | enum DataStatus { none, loading, loaded, error } 4 | 5 | class PlPlayerDataStatus { 6 | Rx status = Rx(DataStatus.none); 7 | 8 | bool get none => status.value == DataStatus.none; 9 | bool get loading => status.value == DataStatus.loading; 10 | bool get loaded => status.value == DataStatus.loaded; 11 | bool get error => status.value == DataStatus.error; 12 | } 13 | -------------------------------------------------------------------------------- /lib/plugin/pl_player/models/duration.dart: -------------------------------------------------------------------------------- 1 | extension DurationExtension on Duration { 2 | /// Returns clamp of [Duration] between [min] and [max]. 3 | Duration clamp(Duration min, Duration max) { 4 | if (this < min) return min; 5 | if (this > max) return max; 6 | return this; 7 | } 8 | 9 | /// Returns a [String] representation of [Duration]. 10 | String label({Duration? reference}) { 11 | reference ??= this; 12 | if (reference > const Duration(days: 1)) { 13 | final days = inDays.toString().padLeft(3, '0'); 14 | final hours = (inHours - (inDays * 24)).toString().padLeft(2, '0'); 15 | final minutes = (inMinutes - (inHours * 60)).toString().padLeft(2, '0'); 16 | final seconds = (inSeconds - (inMinutes * 60)).toString().padLeft(2, '0'); 17 | return '$days:$hours:$minutes:$seconds'; 18 | } else if (reference > const Duration(hours: 1)) { 19 | final hours = inHours.toString().padLeft(2, '0'); 20 | final minutes = (inMinutes - (inHours * 60)).toString().padLeft(2, '0'); 21 | final seconds = (inSeconds - (inMinutes * 60)).toString().padLeft(2, '0'); 22 | return '$hours:$minutes:$seconds'; 23 | } else { 24 | final minutes = inMinutes.toString().padLeft(2, '0'); 25 | final seconds = (inSeconds - (inMinutes * 60)).toString().padLeft(2, '0'); 26 | return '$minutes:$seconds'; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/plugin/pl_player/models/fullscreen_mode.dart: -------------------------------------------------------------------------------- 1 | // 全屏模式 2 | enum FullScreenMode { 3 | // 根据内容自适应 4 | auto, 5 | // 始终竖屏 6 | vertical, 7 | // 始终横屏 8 | horizontal, 9 | // 屏幕长宽比<1.25或为竖屏视频时竖屏,否则横屏 10 | ratio, 11 | } 12 | 13 | extension FullScreenModeDesc on FullScreenMode { 14 | String get description => ['按视频方向(默认)', '强制竖屏', '强制横屏', '屏幕长宽比<1.25或为竖屏视频时竖屏,否则横屏'][index]; 15 | } 16 | 17 | extension FullScreenModeCode on FullScreenMode { 18 | static final List _codeList = [0, 1, 2, 3]; 19 | int get code => _codeList[index]; 20 | 21 | static FullScreenMode? fromCode(int code) { 22 | final index = _codeList.indexOf(code); 23 | if (index != -1) { 24 | return FullScreenMode.values[index]; 25 | } 26 | return null; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/plugin/pl_player/models/play_repeat.dart: -------------------------------------------------------------------------------- 1 | enum PlayRepeat { 2 | pause, 3 | listOrder, 4 | singleCycle, 5 | listCycle, 6 | } 7 | 8 | extension PlayRepeatExtension on PlayRepeat { 9 | static final List _descList = [ 10 | '播完暂停', 11 | '顺序播放', 12 | '单个循环', 13 | '列表循环', 14 | ]; 15 | String get description => _descList[index]; 16 | 17 | static final List _valueList = [ 18 | 1, 19 | 2, 20 | 3, 21 | 4, 22 | ]; 23 | double get value => _valueList[index]; 24 | double get defaultValue => _valueList[1]; 25 | } 26 | -------------------------------------------------------------------------------- /lib/plugin/pl_player/models/play_speed.dart: -------------------------------------------------------------------------------- 1 | enum PlaySpeed { 2 | pointTwoFive, 3 | pointFive, 4 | pointSevenFive, 5 | 6 | one, 7 | onePointTwoFive, 8 | onePointFive, 9 | onePointSevenFive, 10 | 11 | two, 12 | } 13 | 14 | extension PlaySpeedExtension on PlaySpeed { 15 | static final List _descList = [ 16 | '0.25', 17 | '0.5', 18 | '0.75', 19 | '正常', 20 | '1.25', 21 | '1.5', 22 | '1.75', 23 | '2.0', 24 | ]; 25 | String get description => _descList[index]; 26 | 27 | static final List _valueList = [ 28 | 0.25, 29 | 0.5, 30 | 0.75, 31 | 1.0, 32 | 1.25, 33 | 1.5, 34 | 1.75, 35 | 2.0, 36 | ]; 37 | double get value => _valueList[index]; 38 | double get defaultValue => _valueList[3]; 39 | } 40 | -------------------------------------------------------------------------------- /lib/plugin/pl_player/models/play_status.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | 3 | enum PlayerStatus { completed, playing, paused } 4 | 5 | class PlPlayerStatus { 6 | Rx status = Rx(PlayerStatus.paused); 7 | 8 | bool get playing { 9 | return status.value == PlayerStatus.playing; 10 | } 11 | 12 | bool get paused { 13 | return status.value == PlayerStatus.paused; 14 | } 15 | 16 | bool get completed { 17 | return status.value == PlayerStatus.completed; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/plugin/pl_player/utils.dart: -------------------------------------------------------------------------------- 1 | String printDuration(Duration? duration) { 2 | if (duration == null) return "--:--"; 3 | 4 | /*String twoDigits(int n) { 5 | if (n >= 10||n < 0) return "$n"; 6 | return "0$n"; 7 | }*/ 8 | String twoDigits(int n) => n.toString().padLeft(2, "0"); 9 | 10 | String twoDigitMinutes = twoDigits(duration.inMinutes).replaceAll("-", ""); 11 | String twoDigitSeconds = 12 | twoDigits(duration.inSeconds.remainder(60)).replaceAll("-", ""); 13 | //customDebugPrint(duration.inSeconds.remainder(60)); 14 | return "$twoDigitMinutes:$twoDigitSeconds"; 15 | } 16 | 17 | String printDurationWithHours(Duration? duration) { 18 | if (duration == null) return "--:--:--"; 19 | 20 | String twoDigits(int n) { 21 | if (n >= 10) return "$n"; 22 | return "0$n"; 23 | } 24 | 25 | String twoDigitHours = twoDigits(duration.inHours); 26 | String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60)); 27 | String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60)); 28 | return "$twoDigitHours:$twoDigitMinutes:$twoDigitSeconds"; 29 | } 30 | -------------------------------------------------------------------------------- /lib/plugin/pl_player/widgets/app_bar_ani.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class AppBarAni extends StatelessWidget implements PreferredSizeWidget { 4 | const AppBarAni({ 5 | required this.child, 6 | required this.controller, 7 | required this.visible, 8 | this.position, 9 | Key? key, 10 | }) : super(key: key); 11 | 12 | final PreferredSizeWidget child; 13 | final AnimationController controller; 14 | final bool visible; 15 | final String? position; 16 | 17 | @override 18 | Size get preferredSize => child.preferredSize; 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | visible ? controller.reverse() : controller.forward(); 23 | return SlideTransition( 24 | position: Tween( 25 | begin: Offset.zero, 26 | end: Offset(0, position! == 'top' ? -1 : 1), 27 | ).animate(CurvedAnimation( 28 | parent: controller, 29 | curve: Curves.linear, 30 | )), 31 | child: Container( 32 | decoration: BoxDecoration( 33 | gradient: position! == 'top' 34 | ? const LinearGradient( 35 | begin: Alignment.bottomCenter, 36 | end: Alignment.topCenter, 37 | colors: [ 38 | Colors.transparent, 39 | Colors.black54, 40 | ], 41 | tileMode: TileMode.mirror, 42 | ) 43 | : const LinearGradient( 44 | begin: Alignment.topCenter, 45 | end: Alignment.bottomCenter, 46 | colors: [ 47 | Colors.transparent, 48 | Colors.black54, 49 | ], 50 | tileMode: TileMode.mirror, 51 | ), 52 | ), 53 | child: child, 54 | ), 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/plugin/pl_player/widgets/common_btn.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ComBtn extends StatelessWidget { 4 | final Widget? icon; 5 | final Function? fuc; 6 | 7 | const ComBtn({ 8 | this.icon, 9 | this.fuc, 10 | super.key, 11 | }); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return SizedBox( 16 | width: 34, 17 | height: 34, 18 | child: IconButton( 19 | style: ButtonStyle( 20 | padding: MaterialStateProperty.all(EdgeInsets.zero), 21 | ), 22 | onPressed: () { 23 | fuc!(); 24 | }, 25 | icon: icon!, 26 | ), 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/scripts/build.sh: -------------------------------------------------------------------------------- 1 | flutter build apk --target-platform android-arm64 --split-per-abi -------------------------------------------------------------------------------- /lib/services/loggeer.dart: -------------------------------------------------------------------------------- 1 | // final _loggerFactory = 2 | 3 | import 'dart:io'; 4 | 5 | import 'package:logger/logger.dart'; 6 | import 'package:path_provider/path_provider.dart'; 7 | import 'package:path/path.dart' as p; 8 | 9 | final _loggerFactory = PiliLogger(); 10 | 11 | PiliLogger getLogger() { 12 | return _loggerFactory; 13 | } 14 | 15 | class PiliLogger extends Logger { 16 | PiliLogger() : super(); 17 | 18 | @override 19 | void log(Level level, dynamic message, 20 | {Object? error, StackTrace? stackTrace, DateTime? time}) async { 21 | if (level == Level.error || level == Level.fatal) { 22 | // 添加至文件末尾 23 | File logFile = await getLogsPath(); 24 | logFile.writeAsString( 25 | "**${DateTime.now()}** \n $message \n $stackTrace", 26 | mode: FileMode.writeOnlyAppend, 27 | ); 28 | } 29 | super.log(level, "$message", error: error, stackTrace: stackTrace); 30 | } 31 | } 32 | 33 | Future getLogsPath() async { 34 | String dir = (await getApplicationDocumentsDirectory()).path; 35 | final String filename = p.join(dir, ".pili_logs"); 36 | final File file = File(filename); 37 | if (!await file.exists()) { 38 | await file.create(recursive: true); 39 | } 40 | return file; 41 | } 42 | 43 | Future clearLogs() async { 44 | final File file = await getLogsPath(); 45 | try { 46 | await file.writeAsString(''); 47 | } catch (e) { 48 | print('Error clearing file: $e'); 49 | return false; 50 | } 51 | return true; 52 | } 53 | -------------------------------------------------------------------------------- /lib/services/service_locator.dart: -------------------------------------------------------------------------------- 1 | import 'audio_handler.dart'; 2 | import 'audio_session.dart'; 3 | 4 | late VideoPlayerServiceHandler videoPlayerServiceHandler; 5 | late AudioSessionHandler audioSessionHandler; 6 | 7 | Future setupServiceLocator() async { 8 | final audio = await initAudioService(); 9 | videoPlayerServiceHandler = audio; 10 | audioSessionHandler = AudioSessionHandler(); 11 | } 12 | -------------------------------------------------------------------------------- /lib/utils/cookie.dart: -------------------------------------------------------------------------------- 1 | import 'package:PiliPalaX/http/constants.dart'; 2 | import 'package:PiliPalaX/http/init.dart'; 3 | import 'package:webview_cookie_manager/webview_cookie_manager.dart'; 4 | 5 | class SetCookie { 6 | static onSet() async { 7 | var cookies = await WebviewCookieManager().getCookies(HttpString.baseUrl); 8 | await Request.cookieManager.cookieJar 9 | .saveFromResponse(Uri.parse(HttpString.baseUrl), cookies); 10 | var cookieString = 11 | cookies.map((cookie) => '${cookie.name}=${cookie.value}').join('; '); 12 | Request.dio.options.headers['cookie'] = cookieString; 13 | 14 | cookies = await WebviewCookieManager().getCookies(HttpString.apiBaseUrl); 15 | await Request.cookieManager.cookieJar 16 | .saveFromResponse(Uri.parse(HttpString.apiBaseUrl), cookies); 17 | 18 | cookies = await WebviewCookieManager().getCookies(HttpString.tUrl); 19 | await Request.cookieManager.cookieJar 20 | .saveFromResponse(Uri.parse(HttpString.tUrl), cookies); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/utils/danmaku.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:ns_danmaku/ns_danmaku.dart'; 3 | 4 | class DmUtils { 5 | static Color decimalToColor(int decimalColor) { 6 | // 16777215 表示白色 7 | int red = (decimalColor >> 16) & 0xFF; 8 | int green = (decimalColor >> 8) & 0xFF; 9 | int blue = decimalColor & 0xFF; 10 | 11 | return Color.fromARGB(255, red, green, blue); 12 | } 13 | 14 | static DanmakuItemType getPosition(int mode) { 15 | DanmakuItemType type = DanmakuItemType.scroll; 16 | if (mode >= 1 && mode <= 3) { 17 | type = DanmakuItemType.scroll; 18 | } else if (mode == 4) { 19 | type = DanmakuItemType.bottom; 20 | } else if (mode == 5) { 21 | type = DanmakuItemType.top; 22 | } 23 | return type; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/utils/data.dart: -------------------------------------------------------------------------------- 1 | import 'package:hive/hive.dart'; 2 | import 'package:PiliPalaX/http/user.dart'; 3 | 4 | import 'storage.dart'; 5 | 6 | class Data { 7 | static Future init() async { 8 | await historyStatus(); 9 | } 10 | 11 | static Future historyStatus() async { 12 | Box localCache = GStrorage.localCache; 13 | Box userInfoCache = GStrorage.userInfo; 14 | if (userInfoCache.get('userInfoCache') == null) { 15 | return; 16 | } 17 | var res = await UserHttp.historyStatus(); 18 | localCache.put(LocalCacheKey.historyPause, res.data['data']); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/utils/download.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:dio/dio.dart'; 4 | import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; 5 | import 'package:permission_handler/permission_handler.dart'; 6 | import 'package:saver_gallery/saver_gallery.dart'; 7 | 8 | class DownloadUtils { 9 | // 获取存储权限 10 | static requestStoragePer() async { 11 | Map statuses = await [ 12 | Permission.storage, 13 | Permission.photos, 14 | ].request(); 15 | statuses[Permission.storage].toString(); 16 | } 17 | 18 | static Future downloadImg(String imgUrl, 19 | {String imgType = 'cover'}) async { 20 | try { 21 | await requestStoragePer(); 22 | SmartDialog.showLoading(msg: '保存中'); 23 | var response = await Dio() 24 | .get(imgUrl, options: Options(responseType: ResponseType.bytes)); 25 | String picName = 26 | "plpl_${imgType}_${DateTime.now().toString().split('-').join()}"; 27 | final SaveResult result = await SaverGallery.saveImage( 28 | Uint8List.fromList(response.data), 29 | quality: 60, 30 | name: picName, 31 | // 保存到 PiliPala文件夹 32 | androidRelativePath: "Pictures/PiliPala", 33 | androidExistNotSave: false, 34 | ); 35 | SmartDialog.dismiss(); 36 | if (result.isSuccess) { 37 | await SmartDialog.showToast('「$picName」已保存 '); 38 | } 39 | return true; 40 | } catch (err) { 41 | SmartDialog.dismiss(); 42 | SmartDialog.showToast(err.toString()); 43 | return true; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/utils/em.dart: -------------------------------------------------------------------------------- 1 | class Em { 2 | static regCate(String origin) { 3 | String str = origin; 4 | RegExp exp = RegExp('<[^>]*>([^<]*)]*>'); 5 | Iterable matches = exp.allMatches(origin); 6 | for (Match match in matches) { 7 | str = match.group(1)!; 8 | } 9 | return str; 10 | } 11 | 12 | static regTitle(String origin) { 13 | RegExp exp = RegExp('<[^>]*>([^<]*)]*>'); 14 | List res = []; 15 | origin.splitMapJoin(exp, onMatch: (Match match) { 16 | String matchStr = match[0]!; 17 | Map map = {'type': 'em', 'text': regCate(matchStr)}; 18 | res.add(map); 19 | return regCate(matchStr); 20 | }, onNonMatch: (String str) { 21 | if (str != '') { 22 | str = str 23 | .replaceAll('<', '<') 24 | .replaceAll('>', '>') 25 | .replaceAll('"', '"') 26 | .replaceAll(''', "'") 27 | .replaceAll('"', '"') 28 | .replaceAll(''', "'") 29 | .replaceAll(' ', " ") 30 | .replaceAll('&', "&"); 31 | Map map = {'type': 'text', 'text': str}; 32 | res.add(map); 33 | } 34 | return str; 35 | }); 36 | return res; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/utils/event_bus.dart: -------------------------------------------------------------------------------- 1 | // 订阅者回调签名 2 | typedef void EventCallback(arg); 3 | 4 | class EventBus { 5 | // 私有构造函数 6 | EventBus._internal(); 7 | 8 | // 保存单例 9 | static final EventBus _singleton = EventBus._internal(); 10 | 11 | // 工厂构造函数 12 | factory EventBus() => _singleton; 13 | 14 | // 保存事件订阅者队列,key:事件名(id),value: 对应事件的订阅者队列 15 | final _emap = >{}; 16 | 17 | // 添加订阅者 18 | void on(eventName, EventCallback f) { 19 | _emap[eventName] ??= []; 20 | _emap[eventName]!.add(f); 21 | } 22 | 23 | // 移除订阅者 24 | void off(eventName, [EventCallback? f]) { 25 | var list = _emap[eventName]; 26 | if (eventName == null || list == null) return; 27 | if (f == null) { 28 | _emap[eventName] = []; 29 | } else { 30 | list.remove(f); 31 | } 32 | } 33 | 34 | // 触发事件,事件触发后该事件所有订阅者会被调用 35 | void emit(eventName, [arg]) { 36 | var list = _emap[eventName]; 37 | if (list == null) return; 38 | List tempList = List.from(list); 39 | for (var callback in tempList) { 40 | callback(arg); 41 | } 42 | } 43 | 44 | // 获取订阅者数量 45 | int getSubscriberCount(eventName) { 46 | var list = _emap[eventName]; 47 | return list?.length ?? 0; 48 | } 49 | } 50 | 51 | class EventName { 52 | static const String loginEvent = 'loginEvent'; 53 | } 54 | -------------------------------------------------------------------------------- /lib/utils/extension.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | extension ImageExtension on num { 4 | int cacheSize(BuildContext context) { 5 | return (this * MediaQuery.of(context).devicePixelRatio).round(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /lib/utils/feed_back.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart'; 2 | import 'package:hive/hive.dart'; 3 | import 'storage.dart'; 4 | 5 | Box setting = GStrorage.setting; 6 | void feedBack() { 7 | // 设置中是否开启 8 | final bool enable = 9 | setting.get(SettingBoxKey.feedBackEnable, defaultValue: false) as bool; 10 | if (enable) { 11 | HapticFeedback.lightImpact(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/utils/grid.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'storage.dart'; 3 | class Grid { 4 | static double maxRowWidth = GStrorage.setting.get(SettingBoxKey.maxRowWidth, defaultValue: 240.0) as double; 5 | 6 | static double calculateActualWidth(BuildContext context, double maxCrossAxisExtent, double crossAxisSpacing) { 7 | double screenWidth = MediaQuery.of(context).size.width; 8 | int columnCount = ((screenWidth - crossAxisSpacing) / (maxCrossAxisExtent + crossAxisSpacing)).ceil(); 9 | double columnWidth = (screenWidth - crossAxisSpacing) ~/ columnCount - crossAxisSpacing; 10 | return columnWidth; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/utils/proxy.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:system_proxy/system_proxy.dart'; 3 | 4 | class CustomProxy { 5 | init() async { 6 | Map? proxy = await SystemProxy.getProxySettings(); 7 | if (proxy != null) { 8 | HttpOverrides.global = 9 | ProxiedHttpOverrides(proxy['host']!, proxy['port']!); 10 | } 11 | } 12 | } 13 | 14 | class ProxiedHttpOverrides extends HttpOverrides { 15 | final String _port; 16 | final String _host; 17 | 18 | ProxiedHttpOverrides(this._host, this._port); 19 | 20 | @override 21 | HttpClient createHttpClient(SecurityContext? context) { 22 | return super.createHttpClient(context) 23 | // set proxy 24 | ..findProxy = (uri) { 25 | return "PROXY $_host:$_port;"; 26 | }; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/utils/recommend_filter.dart: -------------------------------------------------------------------------------- 1 | // import 'dart:math'; 2 | 3 | import 'storage.dart'; 4 | 5 | class RecommendFilter { 6 | // static late int filterUnfollowedRatio; 7 | static late int minDurationForRcmd; 8 | static late int minLikeRatioForRecommend; 9 | static late bool exemptFilterForFollowed; 10 | static late bool applyFilterToRelatedVideos; 11 | RecommendFilter() { 12 | update(); 13 | } 14 | 15 | static void update() { 16 | var setting = GStrorage.setting; 17 | // filterUnfollowedRatio = 18 | // setting.get(SettingBoxKey.filterUnfollowedRatio, defaultValue: 0); 19 | minDurationForRcmd = 20 | setting.get(SettingBoxKey.minDurationForRcmd, defaultValue: 0); 21 | minLikeRatioForRecommend = 22 | setting.get(SettingBoxKey.minLikeRatioForRecommend, defaultValue: 0); 23 | exemptFilterForFollowed = 24 | setting.get(SettingBoxKey.exemptFilterForFollowed, defaultValue: true); 25 | applyFilterToRelatedVideos = setting 26 | .get(SettingBoxKey.applyFilterToRelatedVideos, defaultValue: true); 27 | } 28 | 29 | static bool filter(dynamic videoItem, {bool relatedVideos = false}) { 30 | if (relatedVideos && !applyFilterToRelatedVideos) { 31 | return false; 32 | } 33 | //由于相关视频中没有已关注标签,只能视为非关注视频 34 | if (!relatedVideos && 35 | videoItem.isFollowed == 1 && 36 | exemptFilterForFollowed) { 37 | return false; 38 | } 39 | if (videoItem.duration > 0 && videoItem.duration < minDurationForRcmd) { 40 | return true; 41 | } 42 | if (videoItem.stat.view is int && 43 | videoItem.stat.view > -1 && 44 | videoItem.stat.like is int && 45 | videoItem.stat.like > -1 && 46 | videoItem.stat.like * 100 < 47 | minLikeRatioForRecommend * videoItem.stat.view) { 48 | return true; 49 | } 50 | return false; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/utils/url_utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:get/get.dart'; 3 | 4 | import '../http/search.dart'; 5 | import 'id_utils.dart'; 6 | import 'utils.dart'; 7 | 8 | class UrlUtils { 9 | // 302重定向路由截取 10 | static Future parseRedirectUrl(String url) async { 11 | late String redirectUrl; 12 | final dio = Dio(); 13 | dio.options.followRedirects = false; 14 | dio.options.validateStatus = (status) { 15 | return status == 200 || status == 301 || status == 302; 16 | }; 17 | final response = await dio.get(url); 18 | if (response.statusCode == 302) { 19 | redirectUrl = response.headers['location']?.first as String; 20 | if (redirectUrl.endsWith('/')) { 21 | redirectUrl = redirectUrl.substring(0, redirectUrl.length - 1); 22 | } 23 | } else { 24 | if (url.endsWith('/')) { 25 | url = url.substring(0, url.length - 1); 26 | } 27 | return url; 28 | } 29 | return redirectUrl; 30 | } 31 | 32 | // 匹配url路由跳转 33 | static matchUrlPush( 34 | String pathSegment, 35 | String title, 36 | String redirectUrl, 37 | ) async { 38 | final Map matchRes = IdUtils.matchAvorBv(input: pathSegment); 39 | if (matchRes.containsKey('BV')) { 40 | final String bv = matchRes['BV']; 41 | final int cid = await SearchHttp.ab2c(bvid: bv); 42 | final String heroTag = Utils.makeHeroTag(bv); 43 | await Get.toNamed( 44 | '/video?bvid=$bv&cid=$cid', 45 | arguments: { 46 | 'pic': '', 47 | 'heroTag': heroTag, 48 | }, 49 | ); 50 | } else { 51 | await Get.toNamed( 52 | '/webview', 53 | parameters: { 54 | 'url': redirectUrl, 55 | 'type': 'url', 56 | 'pageTitle': title, 57 | }, 58 | ); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/utils/video_utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:PiliPalaX/models/video/play/url.dart'; 2 | 3 | class VideoUtils { 4 | static String getCdnUrl(dynamic item) { 5 | var backupUrl = ""; 6 | var videoUrl = ""; 7 | 8 | /// 先获取backupUrl 一般是upgcxcode地址 播放更稳定 9 | if (item is VideoItem) { 10 | backupUrl = item.backupUrl ?? ""; 11 | videoUrl = backupUrl.contains("http") ? backupUrl : (item.baseUrl ?? ""); 12 | } else if (item is AudioItem) { 13 | backupUrl = item.backupUrl ?? ""; 14 | videoUrl = backupUrl.contains("http") ? backupUrl : (item.baseUrl ?? ""); 15 | } else { 16 | return ""; 17 | } 18 | 19 | /// issues #70 20 | if (videoUrl.contains(".mcdn.bilivideo") || 21 | videoUrl.contains("/upgcxcode/")) { 22 | //CDN列表 23 | var cdnList = { 24 | 'ali': 'upos-sz-mirrorali.bilivideo.com', 25 | 'cos': 'upos-sz-mirrorcos.bilivideo.com', 26 | 'hw': 'upos-sz-mirrorhw.bilivideo.com', 27 | }; 28 | //取一个CDN 29 | var cdn = cdnList['ali'] ?? ""; 30 | var reg = RegExp(r'(http|https)://(.*?)/upgcxcode/'); 31 | videoUrl = videoUrl.replaceAll(reg, "https://$cdn/upgcxcode/"); 32 | } 33 | 34 | return videoUrl; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /linux/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | int main(int argc, char** argv) { 4 | g_autoptr(MyApplication) app = my_application_new(); 5 | return g_application_run(G_APPLICATION(app), argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /linux/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Podfile: -------------------------------------------------------------------------------- 1 | platform :osx, '10.14' 2 | 3 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 4 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 5 | 6 | project 'Runner', { 7 | 'Debug' => :debug, 8 | 'Profile' => :release, 9 | 'Release' => :release, 10 | } 11 | 12 | def flutter_root 13 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) 14 | unless File.exist?(generated_xcode_build_settings_path) 15 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" 16 | end 17 | 18 | File.foreach(generated_xcode_build_settings_path) do |line| 19 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 20 | return matches[1].strip if matches 21 | end 22 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" 23 | end 24 | 25 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 26 | 27 | flutter_macos_podfile_setup 28 | 29 | target 'Runner' do 30 | use_frameworks! 31 | use_modular_headers! 32 | 33 | flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) 34 | end 35 | 36 | post_install do |installer| 37 | installer.pods_project.targets.each do |target| 38 | flutter_additional_macos_build_settings(target) 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @NSApplicationMain 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/macos/Runner/Assets.xcassets/AppIcon.appiconset/1024.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/macos/Runner/Assets.xcassets/AppIcon.appiconset/128.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/macos/Runner/Assets.xcassets/AppIcon.appiconset/16.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/macos/Runner/Assets.xcassets/AppIcon.appiconset/256.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/macos/Runner/Assets.xcassets/AppIcon.appiconset/32.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/macos/Runner/Assets.xcassets/AppIcon.appiconset/512.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/macos/Runner/Assets.xcassets/AppIcon.appiconset/64.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | {"images":[{"size":"128x128","expected-size":"128","filename":"128.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"256x256","expected-size":"256","filename":"256.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"128x128","expected-size":"256","filename":"256.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"256x256","expected-size":"512","filename":"512.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"32x32","expected-size":"32","filename":"32.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"512x512","expected-size":"512","filename":"512.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"16x16","expected-size":"16","filename":"16.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"16x16","expected-size":"32","filename":"32.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"32x32","expected-size":"64","filename":"64.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"512x512","expected-size":"1024","filename":"1024.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"}]} -------------------------------------------------------------------------------- /macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = pilipala 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.example.pilipala 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2023 com.example. All rights reserved. 15 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.server 10 | 11 | com.apple.security.network.client 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController.init() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.network.client 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility in the flutter_test package. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:PiliPalaX/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(const MyApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/web/favicon.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/web/icons/Icon-512.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pilipala", 3 | "short_name": "pilipala", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /windows/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(runner LANGUAGES CXX) 3 | 4 | # Define the application target. To change its name, change BINARY_NAME in the 5 | # top-level CMakeLists.txt, not the value here, or `flutter run` will no longer 6 | # work. 7 | # 8 | # Any new source files that you add to the application should be added here. 9 | add_executable(${BINARY_NAME} WIN32 10 | "flutter_window.cpp" 11 | "main.cpp" 12 | "utils.cpp" 13 | "win32_window.cpp" 14 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 15 | "Runner.rc" 16 | "runner.exe.manifest" 17 | ) 18 | 19 | # Apply the standard set of build settings. This can be removed for applications 20 | # that need different build settings. 21 | apply_standard_settings(${BINARY_NAME}) 22 | 23 | # Add preprocessor definitions for the build version. 24 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") 25 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") 26 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") 27 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") 28 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") 29 | 30 | # Disable Windows macros that collide with C++ standard library functions. 31 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 32 | 33 | # Add dependency libraries and include directories. Add any application-specific 34 | # dependencies here. 35 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 36 | target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") 37 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 38 | 39 | # Run the Flutter tool portions of the build. This must not be removed. 40 | add_dependencies(${BINARY_NAME} flutter_assemble) 41 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flutter_window.h" 6 | #include "utils.h" 7 | 8 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 9 | _In_ wchar_t *command_line, _In_ int show_command) { 10 | // Attach to console when present (e.g., 'flutter run') or create a 11 | // new console when running with a debugger. 12 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 13 | CreateAndAttachConsole(); 14 | } 15 | 16 | // Initialize COM, so that it is available for use in the library and/or 17 | // plugins. 18 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 19 | 20 | flutter::DartProject project(L"data"); 21 | 22 | std::vector command_line_arguments = 23 | GetCommandLineArguments(); 24 | 25 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 26 | 27 | FlutterWindow window(project); 28 | Win32Window::Point origin(10, 10); 29 | Win32Window::Size size(1280, 720); 30 | if (!window.Create(L"pilipala", origin, size)) { 31 | return EXIT_FAILURE; 32 | } 33 | window.SetQuitOnClose(true); 34 | 35 | ::MSG msg; 36 | while (::GetMessage(&msg, nullptr, 0, 0)) { 37 | ::TranslateMessage(&msg); 38 | ::DispatchMessage(&msg); 39 | } 40 | 41 | ::CoUninitialize(); 42 | return EXIT_SUCCESS; 43 | } 44 | -------------------------------------------------------------------------------- /windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huajiworld/pilipalaX/cded396cd52a0c2daf60c48916b28ac0274a1f88/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /windows/runner/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void CreateAndAttachConsole() { 11 | if (::AllocConsole()) { 12 | FILE *unused; 13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) { 14 | _dup2(_fileno(stdout), 1); 15 | } 16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) { 17 | _dup2(_fileno(stdout), 2); 18 | } 19 | std::ios::sync_with_stdio(); 20 | FlutterDesktopResyncOutputStreams(); 21 | } 22 | } 23 | 24 | std::vector GetCommandLineArguments() { 25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. 26 | int argc; 27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 28 | if (argv == nullptr) { 29 | return std::vector(); 30 | } 31 | 32 | std::vector command_line_arguments; 33 | 34 | // Skip the first argument as it's the binary name. 35 | for (int i = 1; i < argc; i++) { 36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i])); 37 | } 38 | 39 | ::LocalFree(argv); 40 | 41 | return command_line_arguments; 42 | } 43 | 44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) { 45 | if (utf16_string == nullptr) { 46 | return std::string(); 47 | } 48 | int target_length = ::WideCharToMultiByte( 49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 50 | -1, nullptr, 0, nullptr, nullptr); 51 | std::string utf8_string; 52 | if (target_length == 0 || target_length > utf8_string.max_size()) { 53 | return utf8_string; 54 | } 55 | utf8_string.resize(target_length); 56 | int converted_length = ::WideCharToMultiByte( 57 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 58 | -1, utf8_string.data(), 59 | target_length, nullptr, nullptr); 60 | if (converted_length == 0) { 61 | return std::string(); 62 | } 63 | return utf8_string; 64 | } 65 | -------------------------------------------------------------------------------- /windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | --------------------------------------------------------------------------------