├── .gitignore ├── .metadata ├── .vscode ├── extensions.json ├── launch.json └── settings.json ├── LICENSE ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── org │ │ │ │ └── augenblick │ │ │ │ └── artvier │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-v21 │ │ │ └── launch_background.xml │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-ldpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets ├── emoji │ ├── 101.png │ ├── 102.png │ ├── 103.png │ ├── 104.png │ ├── 105.png │ ├── 106.png │ ├── 107.png │ ├── 108.png │ ├── 201.png │ ├── 202.png │ ├── 203.png │ ├── 204.png │ ├── 205.png │ ├── 206.png │ ├── 207.png │ ├── 208.png │ ├── 209.png │ ├── 301.png │ ├── 302.png │ ├── 303.png │ ├── 304.png │ ├── 305.png │ ├── 306.png │ ├── 307.png │ ├── 308.png │ ├── 309.png │ ├── 310.png │ ├── 401.png │ ├── 402.png │ ├── 403.png │ ├── 404.png │ ├── 405.png │ ├── 406.png │ ├── 407.png │ ├── 408.png │ ├── 501.png │ ├── 502.png │ └── 503.png ├── icon │ ├── Sticker-4.svg │ ├── emoji.svg │ ├── emoji_fill.svg │ ├── sticker.svg │ └── sticker_fill.svg ├── image │ ├── default_avatar.png │ ├── network_error.png │ └── page_loading.gif └── sticker │ ├── 101_s.jpg │ ├── 102_s.jpg │ ├── 103_s.jpg │ ├── 104_s.jpg │ ├── 105_s.jpg │ ├── 106_s.jpg │ ├── 107_s.jpg │ ├── 108_s.jpg │ ├── 109_s.jpg │ ├── 110_s.jpg │ ├── 201_s.jpg │ ├── 202_s.jpg │ ├── 203_s.jpg │ ├── 204_s.jpg │ ├── 205_s.jpg │ ├── 206_s.jpg │ ├── 207_s.jpg │ ├── 208_s.jpg │ ├── 209_s.jpg │ ├── 210_s.jpg │ ├── 301_s.jpg │ ├── 302_s.jpg │ ├── 303_s.jpg │ ├── 304_s.jpg │ ├── 305_s.jpg │ ├── 306_s.jpg │ ├── 307_s.jpg │ ├── 308_s.jpg │ ├── 309_s.jpg │ ├── 310_s.jpg │ ├── 401_s.jpg │ ├── 402_s.jpg │ ├── 403_s.jpg │ ├── 404_s.jpg │ ├── 405_s.jpg │ ├── 406_s.jpg │ ├── 407_s.jpg │ ├── 408_s.jpg │ ├── 409_s.jpg │ └── 410_s.jpg ├── build.yaml ├── build_ipa.sh ├── devtools_options.yaml ├── intl.sh ├── ios ├── ._Podfile ├── ._Runner.xcodeproj ├── ._Runner.xcworkspace ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── icon-1024.png │ │ ├── icon-20-ipad.png │ │ ├── icon-20@2x-ipad.png │ │ ├── icon-20@2x.png │ │ ├── icon-20@3x.png │ │ ├── icon-29-ipad.png │ │ ├── icon-29.png │ │ ├── icon-29@2x-ipad.png │ │ ├── icon-29@2x.png │ │ ├── icon-29@3x.png │ │ ├── icon-40.png │ │ ├── icon-40@2x.png │ │ ├── icon-40@3x.png │ │ ├── icon-60@2x.png │ │ ├── icon-60@3x.png │ │ ├── icon-76.png │ │ ├── icon-76@2x.png │ │ └── icon-83.5@2x.png │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── LaunchImage.png │ │ ├── LaunchImage@2x.png │ │ ├── LaunchImage@3x.png │ │ └── README.md │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── Runner-Bridging-Header.h ├── l10n-arb ├── intl_messages.arb └── intl_zh_CN.arb ├── lib ├── api_app │ ├── api_illusts.dart │ ├── api_newest.dart │ ├── api_novels.dart │ ├── api_serach.dart │ ├── api_update.dart │ ├── api_user.dart │ └── oauth.dart ├── base │ ├── base_api.dart │ ├── base_page.dart │ ├── base_provider │ │ ├── base_notifier.dart │ │ ├── illust_list_notifier.dart │ │ ├── list_notifier.dart │ │ └── novel_list_notifier.dart │ └── base_storage.dart ├── business_component │ ├── advanced_collecting_bottom_sheet │ │ ├── advanced_collecting_bottom_sheet.dart │ │ ├── logic.dart │ │ ├── model │ │ │ ├── advanced_collecting_data.dart │ │ │ └── advanced_collecting_data.freezed.dart │ │ ├── provider │ │ │ ├── collecting_provider.dart │ │ │ └── states_provider.dart │ │ └── widget │ │ │ └── tag_list_item_widget.dart │ ├── button │ │ └── user_follow_button.dart │ ├── card │ │ └── author_card.dart │ ├── dialog │ │ └── token_login.dart │ ├── input │ │ └── search_box.dart │ ├── listview │ │ ├── comment_listview │ │ │ ├── comment_listview.dart │ │ │ ├── comment_listview_item.dart │ │ │ ├── comment_listview_provider.dart │ │ │ ├── comment_replies.dart │ │ │ ├── comment_replies_listview.dart │ │ │ └── logic.dart │ │ ├── illust_listview │ │ │ ├── illust_waterfall_gridview.dart │ │ │ ├── illust_waterfall_item.dart │ │ │ └── logic.dart │ │ ├── lazyload_logic_mixin.dart │ │ ├── manga_listview │ │ │ ├── logic.dart │ │ │ ├── manga_gridview.dart │ │ │ └── manga_gridview_item.dart │ │ ├── manga_series_listview │ │ │ ├── logic.dart │ │ │ ├── manga_series_list_item.dart │ │ │ └── manga_series_listview.dart │ │ ├── novel_listview │ │ │ ├── logic.dart │ │ │ ├── novel_list.dart │ │ │ └── novel_list_item.dart │ │ ├── novel_series_listview │ │ │ ├── logic.dart │ │ │ ├── novel_series_list_item.dart │ │ │ └── novel_series_listview.dart │ │ └── user_vertical_listview │ │ │ ├── logic.dart │ │ │ ├── user_vertical_listview.dart │ │ │ └── user_vertical_listview_item.dart │ ├── page_layout │ │ └── banner_appbar_page_layout.dart │ └── ugoira_image │ │ ├── model.dart │ │ ├── model.freezed.dart │ │ ├── provider.dart │ │ └── ugoira_image.dart ├── component │ ├── badge.dart │ ├── bottom_sheet │ │ ├── bottom_sheets.dart │ │ ├── close_bar.dart │ │ ├── select_item.dart │ │ └── slide_bar.dart │ ├── buttons │ │ ├── blur_button.dart │ │ └── label_button.dart │ ├── carousel │ │ └── blur_carousel.dart │ ├── content │ │ └── expansion_custom.dart │ ├── dialog_custom.dart │ ├── drag_view │ │ ├── drag_vertical_container.dart │ │ └── drag_vertical_panel.dart │ ├── filter │ │ ├── badge_with_remove_icon.dart │ │ ├── custom_tabbar.dart │ │ ├── stateful_flow_filter.dart │ │ └── stateless_flow_filter.dart │ ├── filter_dropdown │ │ ├── custom_dropdown.dart │ │ └── filter_dropdown_list.dart │ ├── flexible_space_bar │ │ ├── banner_flexible_space_bar.dart │ │ └── my_flexible_space_bar.dart │ ├── image │ │ ├── enhance_network_image.dart │ │ ├── gif_image.dart │ │ └── stack_avatar_list.dart │ ├── layout │ │ ├── layout_log_printer.dart │ │ ├── single_line_fitted_box.dart │ │ └── size_reporting_widget.dart │ ├── loading │ │ ├── lazyloading.dart │ │ └── request_loading.dart │ ├── perference │ │ ├── perference_container.dart │ │ ├── perference_group.dart │ │ ├── perference_item.dart │ │ ├── perference_single_choise_panel.dart │ │ └── preferences_navigator_item.dart │ ├── scroll_physics │ │ └── top_clamping_bouncing_scroll_physics.dart │ ├── scroll_view │ │ ├── extend_page_view.dart │ │ └── extend_tab_bar_view.dart │ ├── slider │ │ └── division_slider.dart │ ├── sliver_persistent_header │ │ ├── tab_bar_delegate.dart │ │ └── widget_delegate.dart │ ├── tab_view │ │ └── tab_indicator.dart │ ├── text │ │ └── collapsible_text.dart │ ├── trending_tags_grid.dart │ └── viewport │ │ └── delayed_build_until_viewport.dart ├── config │ ├── constants.dart │ ├── enums.dart │ ├── http_base_options.dart │ └── ranking_mode_constants.dart ├── global │ ├── download_task_queue.dart │ ├── logger.dart │ ├── model │ │ ├── account_profile │ │ │ ├── account_profile.dart │ │ │ └── account_profile.g.dart │ │ ├── collect_state_changed_arguments │ │ │ ├── collect_state_changed_arguments.dart │ │ │ └── collect_state_changed_arguments.freezed.dart │ │ ├── following_state_changed_arguments copy │ │ │ ├── following_state_changed_arguments.dart │ │ │ └── following_state_changed_arguments.freezed.dart │ │ ├── image_download_task_model │ │ │ ├── image_download_task_model.dart │ │ │ ├── image_download_task_model.freezed.dart │ │ │ └── image_download_task_model.g.dart │ │ ├── novel_viewer │ │ │ ├── novel_viewer_settings_model.dart │ │ │ └── novel_viewer_settings_model.freezed.dart │ │ ├── proxy_options │ │ │ ├── image_hosting_model.dart │ │ │ ├── image_hosting_model.freezed.dart │ │ │ ├── proxy_state_model.dart │ │ │ └── proxy_state_model.freezed.dart │ │ └── works_badge_argument │ │ │ ├── works_badge_argument.dart │ │ │ └── works_badge_argument.freezed.dart │ ├── provider │ │ ├── collection_state_provider.dart │ │ ├── current_account_provider.dart │ │ ├── current_user_detail.dart │ │ ├── current_works_type.dart │ │ ├── download_tasks_provider.dart │ │ ├── follow_state_provider.dart │ │ ├── language_provider.dart │ │ ├── network_provider.dart │ │ ├── requester_provider.dart │ │ ├── shared_preferences_provider.dart │ │ ├── themes_provider.dart │ │ └── version_and_update_provider.dart │ ├── themes.dart │ └── variable.dart ├── l10n │ ├── localization_intl.dart │ ├── messages_all.dart │ ├── messages_all_locales.dart │ ├── messages_messages.dart │ └── messages_zh_CN.dart ├── main.dart ├── model_response │ ├── common │ │ ├── collection_detail.dart │ │ ├── collection_detail.g.dart │ │ ├── predictive_search.dart │ │ ├── predictive_search.freezed.dart │ │ └── predictive_search.g.dart │ ├── illusts │ │ ├── common_illust.dart │ │ ├── common_illust.g.dart │ │ ├── common_illust_list.dart │ │ ├── common_illust_list.g.dart │ │ ├── illust_comment_response.dart │ │ ├── illust_comment_response.g.dart │ │ ├── illust_comments.dart │ │ ├── illust_comments.g.dart │ │ ├── illust_detail.dart │ │ ├── illust_detail.g.dart │ │ ├── illust_trending_tags.dart │ │ ├── illust_trending_tags.g.dart │ │ ├── illusts_search_result.dart │ │ ├── illusts_search_result.g.dart │ │ ├── pixivision │ │ │ ├── spotlight_articles.dart │ │ │ └── spotlight_articles.g.dart │ │ ├── recommended │ │ │ ├── illust_recommended.dart │ │ │ └── illust_recommended.g.dart │ │ ├── ugoira.dart │ │ └── ugoira.g.dart │ ├── manga │ │ ├── manga_series_detail.dart │ │ ├── manga_series_detail.freezed.dart │ │ ├── manga_series_detail.g.dart │ │ ├── manga_series_list.dart │ │ ├── manga_series_list.freezed.dart │ │ ├── manga_series_list.g.dart │ │ └── recommended │ │ │ ├── manga_recommended.dart │ │ │ └── manga_recommended.g.dart │ ├── novels │ │ ├── common_novel.dart │ │ ├── common_novel.g.dart │ │ ├── common_novel_list.dart │ │ ├── common_novel_list.g.dart │ │ ├── novel_detail.dart │ │ ├── novel_detail.g.dart │ │ ├── novel_detail_webview.dart │ │ ├── novel_detail_webview.freezed.dart │ │ ├── novel_detail_webview.g.dart │ │ ├── novel_series_detail.dart │ │ ├── novel_series_detail.freezed.dart │ │ ├── novel_series_detail.g.dart │ │ ├── novel_series_list.dart │ │ ├── novel_series_list.freezed.dart │ │ ├── novel_series_list.g.dart │ │ ├── novels_recommended.dart │ │ ├── novels_recommended.g.dart │ │ └── recommended │ │ │ ├── novels_recommended.dart │ │ │ └── novels_recommended.g.dart │ ├── preview_artworks_urls.dart │ ├── preview_artworks_urls.g.dart │ ├── update │ │ ├── git_release.dart │ │ ├── git_release.freezed.dart │ │ └── git_release.g.dart │ └── user │ │ ├── bookmark │ │ ├── bookmark_tag.dart │ │ ├── bookmark_tag.g.dart │ │ ├── bookmark_tag_list.dart │ │ └── bookmark_tag_list.g.dart │ │ ├── common_user.dart │ │ ├── common_user.g.dart │ │ ├── common_user_previews.dart │ │ ├── common_user_previews.g.dart │ │ ├── common_user_previews_list.dart │ │ ├── common_user_previews_list.g.dart │ │ ├── follow │ │ ├── folowing_detail.dart │ │ └── folowing_detail.g.dart │ │ ├── preload_user_least_info.dart │ │ ├── user_detail.dart │ │ ├── user_detail.g.dart │ │ ├── user_previews_list.dart │ │ └── user_previews_list.g.dart ├── pages │ ├── account │ │ └── account_manage │ │ │ ├── account_manage_page.dart │ │ │ ├── logic.dart │ │ │ └── provider │ │ │ └── account_manage_provider.dart │ ├── artwork │ │ ├── detail │ │ │ ├── arguments │ │ │ │ ├── illust_detail_page_args.dart │ │ │ │ └── illust_detail_page_args.freezed.dart │ │ │ ├── detail_page.dart │ │ │ ├── layout.dart │ │ │ ├── logic.dart │ │ │ ├── provider │ │ │ │ ├── illust_comment_provider.dart │ │ │ │ ├── illust_detail_provider.dart │ │ │ │ └── illust_related_provider.dart │ │ │ └── widgets │ │ │ │ ├── author_card.dart │ │ │ │ ├── comments_preview_content.dart │ │ │ │ ├── menu_bottom_sheet.dart │ │ │ │ ├── related_artworks_content.dart │ │ │ │ └── user_follow_button.dart │ │ ├── download_manage │ │ │ ├── download_manage_page.dart │ │ │ └── provider │ │ │ │ └── download_manage_provider.dart │ │ ├── history │ │ │ ├── history_page.dart │ │ │ ├── logic.dart │ │ │ ├── provider │ │ │ │ └── history_provider.dart │ │ │ └── widgets │ │ │ │ └── history_gridview.dart │ │ ├── pixivision │ │ │ ├── illust_pixivision_page.dart │ │ │ ├── logic.dart │ │ │ ├── model │ │ │ │ ├── pixivision_body_illust_item.dart │ │ │ │ ├── pixivision_body_illust_item.freezed.dart │ │ │ │ ├── pixivision_webview_page_arguments.dart │ │ │ │ └── pixivision_webview_page_arguments.freezed.dart │ │ │ ├── provider │ │ │ │ └── illust_pixivision_provider.dart │ │ │ └── widgets │ │ │ │ └── pixivision_illust_card.dart │ │ ├── series │ │ │ ├── manga_series_detail_page.dart │ │ │ ├── model │ │ │ │ ├── arguments.dart │ │ │ │ └── arguments.freezed.dart │ │ │ ├── provider.dart │ │ │ └── widget │ │ │ │ └── author_card.dart │ │ └── viewer │ │ │ ├── image_viewer_page.dart │ │ │ ├── logic.dart │ │ │ └── model │ │ │ ├── image_quality_url_model.dart │ │ │ ├── image_quality_url_model.freezed.dart │ │ │ ├── image_viewer_page_arguments.dart │ │ │ ├── image_viewer_page_arguments.freezed.dart │ │ │ ├── image_viewer_state.dart │ │ │ └── image_viewer_state.freezed.dart │ ├── comment │ │ ├── comments_page.dart │ │ ├── model │ │ │ ├── comment_bar_model.dart │ │ │ └── comment_bar_model.freezed.dart │ │ ├── provider │ │ │ ├── comment_bar_provider.dart │ │ │ └── comment_list_provider.dart │ │ └── widgets │ │ │ ├── comment_bar_bottom_sheet.dart │ │ │ └── comment_bar_preview.dart │ ├── framework │ │ ├── booting │ │ │ └── booting_page.dart │ │ └── not_found │ │ │ └── not_found_page.dart │ ├── login │ │ ├── login_by_web_page.dart │ │ ├── login_wizard_page.dart │ │ └── widgets │ │ │ └── login_settings_bottom_sheet.dart │ ├── main_navigation_tab_page │ │ ├── home │ │ │ ├── home_tabpage.dart │ │ │ ├── model │ │ │ │ ├── home_illust_model.dart │ │ │ │ └── home_illust_model.freezed.dart │ │ │ ├── provider │ │ │ │ ├── home_manga_provider.dart │ │ │ │ ├── home_novel_provider.dart │ │ │ │ └── home_provider.dart │ │ │ ├── views │ │ │ │ ├── home_illust_tabpage.dart │ │ │ │ ├── home_manga_tabpage.dart │ │ │ │ └── home_novel_tabpage.dart │ │ │ └── widgets │ │ │ │ └── pixivision_carousel.dart │ │ ├── main_navigation.dart │ │ ├── newest │ │ │ ├── newest_tabpage.dart │ │ │ ├── provider │ │ │ │ ├── everybody_newest_provider.dart │ │ │ │ ├── followed_newest_provider.dart │ │ │ │ ├── followed_series_provider.dart │ │ │ │ └── friends_newest_provider.dart │ │ │ ├── sub_tabpage │ │ │ │ ├── everybody_newest_tabpage.dart │ │ │ │ ├── followed_newest_tabpage.dart │ │ │ │ ├── followed_series_tabpage.dart │ │ │ │ └── friends_newest_tabpage.dart │ │ │ └── widgets │ │ │ │ └── recommend_users_widget.dart │ │ ├── profile │ │ │ ├── logic.dart │ │ │ ├── models.dart │ │ │ ├── profile_tabpage.dart │ │ │ ├── provider │ │ │ │ └── profile_provider.dart │ │ │ └── quick_settings │ │ │ │ ├── proxy │ │ │ │ ├── logic.dart │ │ │ │ ├── proxy_bottom_sheet.dart │ │ │ │ └── proxy_settings.dart │ │ │ │ └── theme │ │ │ │ ├── theme_bottom_sheet.dart │ │ │ │ └── widget │ │ │ │ ├── brightness_select_widget.dart │ │ │ │ └── brightness_settings_panel.dart │ │ └── search │ │ │ ├── provider │ │ │ ├── search_input_provider.dart │ │ │ └── trend_tags_provider.dart │ │ │ └── search_tabpage.dart │ ├── novel │ │ ├── detail │ │ │ ├── arguments │ │ │ │ ├── novel_detail_page_args.dart │ │ │ │ └── novel_detail_page_args.freezed.dart │ │ │ ├── detail_page.dart │ │ │ ├── layout.dart │ │ │ ├── logic.dart │ │ │ ├── provider │ │ │ │ └── novel_detail_provider.dart │ │ │ └── widgets │ │ │ │ ├── menu_bottom_sheet.dart │ │ │ │ └── novel_overlay_settings.dart │ │ └── series │ │ │ ├── model │ │ │ ├── arguments.dart │ │ │ └── arguments.freezed.dart │ │ │ ├── novel_series_detail_page.dart │ │ │ ├── provider.dart │ │ │ └── widget │ │ │ └── author_card.dart │ ├── ranking │ │ ├── logic.dart │ │ ├── provider │ │ │ └── ranking_provider.dart │ │ ├── ranking_page.dart │ │ └── ranking_tabpage.dart │ ├── search │ │ ├── expand_search │ │ │ ├── expand_search_page.dart │ │ │ ├── provider.dart │ │ │ └── widget │ │ │ │ ├── predictive_search_user_item.dart │ │ │ │ └── search_input.dart │ │ └── result │ │ │ ├── arguments │ │ │ ├── seach_filter_arguments.dart │ │ │ └── seach_filter_arguments.freezed.dart │ │ │ ├── logic.dart │ │ │ ├── provider │ │ │ ├── search_filters_provider.dart │ │ │ └── search_result_provider.dart │ │ │ └── search_result_page.dart │ ├── settings │ │ ├── about │ │ │ └── about_app_page.dart │ │ ├── all_settings │ │ │ └── all_settings_page.dart │ │ ├── check_update │ │ │ ├── check_update.dart │ │ │ └── provider │ │ │ │ └── check_update_provider.dart │ │ ├── develop │ │ │ ├── developer_page.dart │ │ │ └── widgets │ │ │ │ └── refresh_token_settings.dart │ │ ├── download │ │ │ └── download_setting.dart │ │ └── language │ │ │ └── language_setting.dart │ └── user │ │ ├── collection │ │ ├── logic.dart │ │ ├── model │ │ │ ├── collections_filter_model.dart │ │ │ └── collections_filter_model.freezed.dart │ │ ├── my_collection_page.dart │ │ ├── provider │ │ │ ├── artwork_collections_provider.dart │ │ │ ├── filter_provider.dart │ │ │ ├── novel_collections_provider.dart │ │ │ └── tags_provider.dart │ │ ├── tabpage │ │ │ ├── artworks_tabpage.dart │ │ │ └── novels_tabpage.dart │ │ └── widget │ │ │ ├── collections_filter_sheets.dart │ │ │ ├── tag_list_item.dart │ │ │ └── tag_listview.dart │ │ ├── detail │ │ ├── logic.dart │ │ ├── provider │ │ │ ├── user_collections_provider.dart │ │ │ ├── user_detail_provider.dart │ │ │ ├── user_follow_provider.dart │ │ │ └── user_works_provider.dart │ │ ├── tabpage │ │ │ ├── collections_tabpage.dart │ │ │ ├── more_information_tabpage.dart │ │ │ └── works_tabpage.dart │ │ ├── user_detail_page.dart │ │ └── widget │ │ │ ├── appbar.dart │ │ │ ├── menu_bottom_sheet.dart │ │ │ └── user_panel_widget.dart │ │ ├── following │ │ ├── provider │ │ │ └── user_following_provider.dart │ │ └── user_following_page.dart │ │ ├── recommend │ │ ├── provider │ │ │ └── recommend_users_provider.dart │ │ └── recommend_users_page.dart │ │ └── works │ │ └── my_works.dart ├── request │ ├── authorization_interceptor.dart │ ├── default_interceptor.dart │ ├── http_host_overrides.dart │ ├── http_requester.dart │ └── my_http_overrides.dart ├── routes.dart ├── storage │ ├── account_storage.dart │ ├── downloads │ │ ├── download_task_table.dart │ │ ├── downloads_db.dart │ │ └── downloads_db.g.dart │ ├── language_store.dart │ ├── network_store.dart │ ├── novel_viewer_store.dart │ ├── theme_storage.dart │ └── viewing_history │ │ ├── viewing_history_db.dart │ │ ├── viewing_history_db.g.dart │ │ └── viewing_history_table.dart └── util │ ├── download_util.dart │ ├── event_util.dart │ ├── save_image_util.dart │ └── string_util.dart ├── pubspec.lock ├── pubspec.yaml ├── test └── widget_test.dart └── web ├── favicon.png ├── icons ├── Icon-192.png └── Icon-512.png ├── index.html └── manifest.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | 36 | # Symbolication related 37 | app.*.symbols 38 | 39 | # Obfuscation related 40 | app.*.map.json 41 | 42 | # Android Studio will place build artifacts here 43 | /android/app/debug 44 | /android/app/profile 45 | /android/app/release 46 | 47 | # XCode will place build artifacts here 48 | /ios/build 49 | 50 | # KeyStore 51 | *.jks 52 | /android/key.properties 53 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: adc687823a831bbebe28bdccfac1a628ca621513 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [] 3 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Run", 6 | "program": "lib/main.dart", 7 | "request": "launch", 8 | "type": "dart" 9 | }, 10 | { 11 | "name": "Profile", 12 | "type": "dart", 13 | "request": "launch", 14 | "program": "lib/main.dart", 15 | "flutterMode": "profile" 16 | }, 17 | { 18 | "name": "Release", 19 | "type": "dart", 20 | "request": "launch", 21 | "program": "lib/main.dart", 22 | "flutterMode": "release" 23 | }, 24 | ] 25 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "terminal.explorerKind": "external" 3 | } -------------------------------------------------------------------------------- /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 | # constant_identifier_names: fasle 28 | 29 | # Additional information about this file can be found at 30 | # https://dart.dev/guides/language/analysis-options 31 | 32 | analyzer: 33 | errors: 34 | invalid_annotation_target: ignore -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/org/augenblick/artvier/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package org.augenblick.artvier 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-ldpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/android/app/src/main/res/mipmap-ldpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.8.0' 3 | repositories { 4 | // google() 5 | // jcenter() 6 | mavenCentral() 7 | maven { url 'https://maven.aliyun.com/repository/google' } 8 | maven { url 'https://maven.aliyun.com/repository/jcenter' } 9 | // maven { url 'http://maven.aliyun.com/nexus/content/groups/public' } 10 | } 11 | 12 | dependencies { 13 | classpath 'com.android.tools.build:gradle:7.1.3' 14 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 15 | } 16 | } 17 | 18 | allprojects { 19 | repositories { 20 | mavenCentral() 21 | google() 22 | jcenter() 23 | } 24 | } 25 | 26 | rootProject.buildDir = '../build' 27 | subprojects { 28 | project.buildDir = "${rootProject.buildDir}/${project.name}" 29 | } 30 | subprojects { 31 | project.evaluationDependsOn(':app') 32 | } 33 | 34 | tasks.register("clean", Delete) { 35 | delete rootProject.buildDir 36 | } 37 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-all.zip 7 | #distributionUrl=file\:/C:/Users/kermi/.gradle/wrapper/dists/gradle-6.7-all.zip 8 | # distributionUrl=file\:/D:/Download/gradle-6.7-all.zip 9 | -------------------------------------------------------------------------------- /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/emoji/101.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/101.png -------------------------------------------------------------------------------- /assets/emoji/102.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/102.png -------------------------------------------------------------------------------- /assets/emoji/103.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/103.png -------------------------------------------------------------------------------- /assets/emoji/104.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/104.png -------------------------------------------------------------------------------- /assets/emoji/105.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/105.png -------------------------------------------------------------------------------- /assets/emoji/106.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/106.png -------------------------------------------------------------------------------- /assets/emoji/107.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/107.png -------------------------------------------------------------------------------- /assets/emoji/108.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/108.png -------------------------------------------------------------------------------- /assets/emoji/201.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/201.png -------------------------------------------------------------------------------- /assets/emoji/202.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/202.png -------------------------------------------------------------------------------- /assets/emoji/203.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/203.png -------------------------------------------------------------------------------- /assets/emoji/204.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/204.png -------------------------------------------------------------------------------- /assets/emoji/205.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/205.png -------------------------------------------------------------------------------- /assets/emoji/206.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/206.png -------------------------------------------------------------------------------- /assets/emoji/207.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/207.png -------------------------------------------------------------------------------- /assets/emoji/208.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/208.png -------------------------------------------------------------------------------- /assets/emoji/209.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/209.png -------------------------------------------------------------------------------- /assets/emoji/301.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/301.png -------------------------------------------------------------------------------- /assets/emoji/302.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/302.png -------------------------------------------------------------------------------- /assets/emoji/303.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/303.png -------------------------------------------------------------------------------- /assets/emoji/304.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/304.png -------------------------------------------------------------------------------- /assets/emoji/305.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/305.png -------------------------------------------------------------------------------- /assets/emoji/306.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/306.png -------------------------------------------------------------------------------- /assets/emoji/307.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/307.png -------------------------------------------------------------------------------- /assets/emoji/308.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/308.png -------------------------------------------------------------------------------- /assets/emoji/309.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/309.png -------------------------------------------------------------------------------- /assets/emoji/310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/310.png -------------------------------------------------------------------------------- /assets/emoji/401.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/401.png -------------------------------------------------------------------------------- /assets/emoji/402.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/402.png -------------------------------------------------------------------------------- /assets/emoji/403.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/403.png -------------------------------------------------------------------------------- /assets/emoji/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/404.png -------------------------------------------------------------------------------- /assets/emoji/405.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/405.png -------------------------------------------------------------------------------- /assets/emoji/406.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/406.png -------------------------------------------------------------------------------- /assets/emoji/407.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/407.png -------------------------------------------------------------------------------- /assets/emoji/408.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/408.png -------------------------------------------------------------------------------- /assets/emoji/501.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/501.png -------------------------------------------------------------------------------- /assets/emoji/502.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/502.png -------------------------------------------------------------------------------- /assets/emoji/503.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/emoji/503.png -------------------------------------------------------------------------------- /assets/icon/Sticker-4.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icon/emoji.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icon/emoji_fill.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icon/sticker.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icon/sticker_fill.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/image/default_avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/image/default_avatar.png -------------------------------------------------------------------------------- /assets/image/network_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/image/network_error.png -------------------------------------------------------------------------------- /assets/image/page_loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/image/page_loading.gif -------------------------------------------------------------------------------- /assets/sticker/101_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/101_s.jpg -------------------------------------------------------------------------------- /assets/sticker/102_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/102_s.jpg -------------------------------------------------------------------------------- /assets/sticker/103_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/103_s.jpg -------------------------------------------------------------------------------- /assets/sticker/104_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/104_s.jpg -------------------------------------------------------------------------------- /assets/sticker/105_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/105_s.jpg -------------------------------------------------------------------------------- /assets/sticker/106_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/106_s.jpg -------------------------------------------------------------------------------- /assets/sticker/107_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/107_s.jpg -------------------------------------------------------------------------------- /assets/sticker/108_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/108_s.jpg -------------------------------------------------------------------------------- /assets/sticker/109_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/109_s.jpg -------------------------------------------------------------------------------- /assets/sticker/110_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/110_s.jpg -------------------------------------------------------------------------------- /assets/sticker/201_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/201_s.jpg -------------------------------------------------------------------------------- /assets/sticker/202_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/202_s.jpg -------------------------------------------------------------------------------- /assets/sticker/203_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/203_s.jpg -------------------------------------------------------------------------------- /assets/sticker/204_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/204_s.jpg -------------------------------------------------------------------------------- /assets/sticker/205_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/205_s.jpg -------------------------------------------------------------------------------- /assets/sticker/206_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/206_s.jpg -------------------------------------------------------------------------------- /assets/sticker/207_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/207_s.jpg -------------------------------------------------------------------------------- /assets/sticker/208_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/208_s.jpg -------------------------------------------------------------------------------- /assets/sticker/209_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/209_s.jpg -------------------------------------------------------------------------------- /assets/sticker/210_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/210_s.jpg -------------------------------------------------------------------------------- /assets/sticker/301_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/301_s.jpg -------------------------------------------------------------------------------- /assets/sticker/302_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/302_s.jpg -------------------------------------------------------------------------------- /assets/sticker/303_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/303_s.jpg -------------------------------------------------------------------------------- /assets/sticker/304_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/304_s.jpg -------------------------------------------------------------------------------- /assets/sticker/305_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/305_s.jpg -------------------------------------------------------------------------------- /assets/sticker/306_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/306_s.jpg -------------------------------------------------------------------------------- /assets/sticker/307_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/307_s.jpg -------------------------------------------------------------------------------- /assets/sticker/308_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/308_s.jpg -------------------------------------------------------------------------------- /assets/sticker/309_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/309_s.jpg -------------------------------------------------------------------------------- /assets/sticker/310_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/310_s.jpg -------------------------------------------------------------------------------- /assets/sticker/401_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/401_s.jpg -------------------------------------------------------------------------------- /assets/sticker/402_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/402_s.jpg -------------------------------------------------------------------------------- /assets/sticker/403_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/403_s.jpg -------------------------------------------------------------------------------- /assets/sticker/404_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/404_s.jpg -------------------------------------------------------------------------------- /assets/sticker/405_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/405_s.jpg -------------------------------------------------------------------------------- /assets/sticker/406_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/406_s.jpg -------------------------------------------------------------------------------- /assets/sticker/407_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/407_s.jpg -------------------------------------------------------------------------------- /assets/sticker/408_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/408_s.jpg -------------------------------------------------------------------------------- /assets/sticker/409_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/409_s.jpg -------------------------------------------------------------------------------- /assets/sticker/410_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/assets/sticker/410_s.jpg -------------------------------------------------------------------------------- /build.yaml: -------------------------------------------------------------------------------- 1 | targets: 2 | $default: 3 | builders: 4 | source_gen|combining_builder: 5 | options: 6 | ignore_for_file: 7 | - non_constant_identifier_names -------------------------------------------------------------------------------- /build_ipa.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Build ipa file 4 | flutter build ios 5 | 6 | # Paused, waiting to continue 7 | echo -n "Press any key to continue..." 8 | read -n 1 -s 9 | echo 10 | 11 | RootPath=$(pwd) 12 | Workpath="$RootPath/build/ios/iphoneos" 13 | Payload="$Workpath/Payload" 14 | Runner_app="$Workpath/Runner.app" 15 | Payload_app="$Workpath/Payload/Payload.app" 16 | Payload_ipa="$Workpath/Payload.ipa" 17 | 18 | # Clear 19 | if [ -d "$Payload" ]; then 20 | rm -r $Payload 21 | fi 22 | 23 | # Create 24 | if [ ! -d "$Payload" ]; then 25 | mkdir -p "$Payload" 26 | fi 27 | 28 | if [ -d "$Runner_app" ]; then 29 | cp -r $Runner_app $Payload_app 30 | 31 | else 32 | echo "$Runner_app not found!" 33 | fi 34 | 35 | cd $Workpath 36 | zip -r $Payload_ipa Payload 37 | cd $RootPath 38 | 39 | echo "------" 40 | echo "Build $Payload_ipa success!" -------------------------------------------------------------------------------- /devtools_options.yaml: -------------------------------------------------------------------------------- 1 | extensions: 2 | -------------------------------------------------------------------------------- /intl.sh: -------------------------------------------------------------------------------- 1 | dart run intl_translation:extract_to_arb --output-dir=l10n-arb lib/l10n/localization_intl.dart 2 | dart run intl_translation:generate_from_arb --output-dir=lib/l10n --no-use-deferred-loading lib/l10n/localization_intl.dart l10n-arb/intl_*.arb 3 | -------------------------------------------------------------------------------- /ios/._Podfile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/ios/._Podfile -------------------------------------------------------------------------------- /ios/._Runner.xcodeproj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/ios/._Runner.xcodeproj -------------------------------------------------------------------------------- /ios/._Runner.xcworkspace: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/ios/._Runner.xcworkspace -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 12.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | // import flutter_downloader 4 | 5 | @UIApplicationMain 6 | @objc class AppDelegate: FlutterAppDelegate { 7 | override func application( 8 | _ application: UIApplication, 9 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 10 | ) -> Bool { 11 | GeneratedPluginRegistrant.register(with: self) 12 | // FlutterDownloaderPlugin.setPluginRegistrantCallback(registerPlugins) 13 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 14 | } 15 | } 16 | 17 | // private func registerPlugins(registry: FlutterPluginRegistry) { 18 | // if (!registry.hasPlugin("FlutterDownloaderPlugin")) { 19 | // FlutterDownloaderPlugin.register(with: registry.registrar(forPlugin: "FlutterDownloaderPlugin")!) 20 | // } 21 | // } 22 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-1024.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20-ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20-ipad.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@2x-ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@2x-ipad.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29-ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29-ipad.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@2x-ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@2x-ipad.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-76.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /lib/api_app/api_update.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:artvier/config/constants.dart'; 4 | import 'package:artvier/model_response/update/git_release.dart'; 5 | import 'package:dio/dio.dart'; 6 | 7 | import 'package:artvier/base/base_api.dart'; 8 | 9 | class ApiUpdate extends ApiBase { 10 | ApiUpdate(super.requester); 11 | 12 | /// 最新版本 13 | Future lastRelease({CancelToken? cancelToken}) async { 14 | Response res = await requester.get( 15 | '${CONSTANTS.app_repo_api}/releases/latest', 16 | queryParameters: {}, 17 | options: Options(responseType: ResponseType.json), 18 | cancelToken: cancelToken, 19 | ); 20 | return GitRelease.fromJson(json.decode(res.data)); 21 | } 22 | 23 | /// 获取某个版本信息 24 | Future releaseDetails(String tag, {CancelToken? cancelToken}) async { 25 | Response res = await requester.get( 26 | '${CONSTANTS.app_repo_api}/releases/tags/$tag', 27 | queryParameters: {}, 28 | options: Options(responseType: ResponseType.json), 29 | cancelToken: cancelToken, 30 | ); 31 | return GitRelease.fromJson(json.decode(res.data)); 32 | } 33 | 34 | /// 历史版本 35 | Future> historyReleases({int page = 1, int perPage = 10, CancelToken? cancelToken}) async { 36 | Response res = await requester.get( 37 | '${CONSTANTS.app_repo_api}/releases', 38 | queryParameters: { 39 | "page": page, 40 | "per_page": perPage, 41 | }, 42 | options: Options(responseType: ResponseType.json), 43 | cancelToken: cancelToken, 44 | ); 45 | return gitReleaseFromJson(json.decode(res.data)); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/base/base_api.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:artvier/request/http_host_overrides.dart'; 4 | import 'package:dio/dio.dart'; 5 | import 'package:artvier/request/http_requester.dart'; 6 | 7 | class ApiBase { 8 | final HttpRequester requester; 9 | 10 | ApiBase(this.requester); 11 | 12 | /// 通用加载更多/获取下一页数据 13 | /// 取得的jsonMap数据还需要 T.fromJson()转换 14 | Future> nextUrlData(String nextUrl, {CancelToken? cancelToken}) async { 15 | Response res = await requester.get( 16 | HttpHostOverrides().appApiUrl(nextUrl), 17 | options: Options(responseType: ResponseType.json), 18 | cancelToken: cancelToken, 19 | ); 20 | return json.decode(res.data); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/base/base_provider/list_notifier.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 2 | 3 | /// The abstract actions of [AsyncNotifier] if the [State] is [List] 4 | abstract class AsyncListNotifier { 5 | /// Fetch data. 6 | Future fetch(); 7 | 8 | /// Reload future. 9 | /// 10 | /// Will set the state to Loading. 11 | Future reload(); 12 | 13 | /// For pull-to-refresh. 14 | /// 15 | /// Still keep AsyncValue as Data. 16 | Future refresh(); 17 | 18 | /// The action for lazyload more data. 19 | /// 20 | /// Return has more data. 21 | Future next(); 22 | } 23 | 24 | /// The mixin of [AsyncNotifier] if the [State] is [List] 25 | mixin ListAsyncNotifierMixin implements AsyncListNotifier { 26 | String? nextUrl; 27 | 28 | bool get hasMore => nextUrl != null; 29 | 30 | set state(AsyncValue newState); 31 | 32 | @override 33 | Future reload() async { 34 | state = const AsyncValue.loading(); 35 | state = await AsyncValue.guard(() async { 36 | return fetch(); 37 | }); 38 | } 39 | 40 | @override 41 | Future refresh() async { 42 | state = await AsyncValue.guard(() async { 43 | return fetch(); 44 | }); 45 | } 46 | } -------------------------------------------------------------------------------- /lib/base/base_storage.dart: -------------------------------------------------------------------------------- 1 | import 'package:shared_preferences/shared_preferences.dart'; 2 | 3 | class BaseStorage { 4 | final SharedPreferences sharedPreferences; 5 | 6 | SharedPreferences get prefs => sharedPreferences; 7 | 8 | BaseStorage(this.sharedPreferences); 9 | } 10 | -------------------------------------------------------------------------------- /lib/business_component/advanced_collecting_bottom_sheet/model/advanced_collecting_data.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:artvier/config/enums.dart'; 3 | import 'package:artvier/model_response/common/collection_detail.dart'; 4 | 5 | part 'advanced_collecting_data.freezed.dart'; 6 | 7 | @Freezed( 8 | copyWith: true, 9 | ) 10 | class AdvancedCollectingDataModel with _$AdvancedCollectingDataModel { 11 | factory AdvancedCollectingDataModel({ 12 | required List tags, 13 | required Restrict restrict, 14 | required CollectState collectState, 15 | }) = _AdvancedCollectingDataModel; 16 | } 17 | -------------------------------------------------------------------------------- /lib/business_component/input/search_box.dart: -------------------------------------------------------------------------------- 1 | import 'package:artvier/base/base_page.dart'; 2 | import 'package:artvier/routes.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 5 | 6 | /// 搜索的输入框 7 | class SearchBox extends BasePage { 8 | const SearchBox({ 9 | super.key, 10 | this.textColor, 11 | this.backgroundColor, 12 | }); 13 | 14 | final Color? textColor; 15 | final Color? backgroundColor; 16 | 17 | @override 18 | Widget build(BuildContext context, WidgetRef ref) { 19 | return DecoratedBox( 20 | decoration: BoxDecoration( 21 | color: backgroundColor ?? Colors.grey.withOpacity(0.15), 22 | borderRadius: const BorderRadius.all(Radius.circular(8.0)), 23 | ), 24 | child: Row( 25 | children: [ 26 | // 搜索图标 27 | Padding( 28 | padding: const EdgeInsets.only(left: 10.0, right: 4), 29 | child: Icon( 30 | Icons.search_outlined, 31 | size: 18, 32 | color: textColor ?? textTheme(context).bodyMedium?.color ?? Colors.black, 33 | ), 34 | ), 35 | // 文本区域 36 | Expanded( 37 | flex: 1, 38 | child: GestureDetector( 39 | onTap: () { 40 | // 展开搜索框 41 | Navigator.of(context).push(BasePage.createFadeRoute(RouteNames.expandSearch.name)); 42 | }, 43 | child: Text( 44 | "${l10n(context).search}...", 45 | style: textColor != null 46 | ? textTheme(context).bodyMedium?.copyWith(color: textColor) 47 | : textTheme(context).bodyMedium, 48 | ), 49 | ), 50 | ), 51 | ], 52 | ), 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/business_component/listview/lazyload_logic_mixin.dart: -------------------------------------------------------------------------------- 1 | import 'package:artvier/global/logger.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | import 'package:artvier/config/enums.dart'; 4 | 5 | mixin LazyloadLogic { 6 | Future Function() get onLazyload; 7 | 8 | /// 懒加载状态 9 | final lazyloadStateProvider = StateProvider.autoDispose((ref) { 10 | return LazyloadState.idle; 11 | }); 12 | 13 | /// 当懒加载组件进入界面视图范围内时触发 14 | Future handleViewLazyloadWidget(WidgetRef ref, Function onLazyload) async { 15 | if ([LazyloadState.loading, LazyloadState.noMore].contains(ref.read(lazyloadStateProvider))) { 16 | // 已经在加载中或者没有更多数据,不允许触发 17 | return; 18 | } 19 | try { 20 | bool hasMore = await onLazyload(); 21 | ref.read(lazyloadStateProvider.notifier).update((state) => hasMore ? LazyloadState.idle : LazyloadState.noMore); 22 | } catch (e) { 23 | logger.e(e.toString()); 24 | ref.read(lazyloadStateProvider.notifier).update((state) => LazyloadState.error); 25 | } 26 | } 27 | 28 | /// 加载失败的重试 29 | Future handleRetry(WidgetRef ref) async { 30 | ref.read(lazyloadStateProvider.notifier).update((state) => LazyloadState.loading); 31 | try { 32 | bool hasMore = await onLazyload(); 33 | ref.read(lazyloadStateProvider.notifier).update((state) => hasMore ? LazyloadState.idle : LazyloadState.noMore); 34 | } catch (e) { 35 | logger.e(e.toString()); 36 | ref.read(lazyloadStateProvider.notifier).update((state) => LazyloadState.error); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/business_component/listview/manga_series_listview/logic.dart: -------------------------------------------------------------------------------- 1 | import 'package:artvier/model_response/manga/manga_series_list.dart'; 2 | import 'package:artvier/pages/artwork/series/model/arguments.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 5 | import 'package:artvier/routes.dart'; 6 | 7 | mixin ManagaSeriesListViewLogic { 8 | WidgetRef get ref; 9 | 10 | void handleTapItem(MangaSeries mangaSeries) { 11 | Navigator.of(ref.context).pushNamed( 12 | RouteNames.mangaSeriesDetail.name, 13 | arguments: MangaSeriesDetailPagePageArguments( 14 | id: mangaSeries.id, 15 | title: mangaSeries.title, 16 | url: mangaSeries.url, 17 | user: mangaSeries.user, 18 | ), 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/business_component/listview/novel_listview/logic.dart: -------------------------------------------------------------------------------- 1 | import 'package:artvier/pages/novel/detail/arguments/novel_detail_page_args.dart'; 2 | import 'package:artvier/routes.dart'; 3 | import 'package:flutter/widgets.dart'; 4 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 5 | import 'package:artvier/model_response/novels/common_novel.dart'; 6 | 7 | mixin NovelListViewLogic { 8 | WidgetRef get ref; 9 | 10 | void handleTapItem(CommonNovel novel) { 11 | Navigator.of(ref.context).pushNamed( 12 | RouteNames.novelDetail.name, 13 | arguments: NovelDetailPageArguments(detail: novel, worksId: novel.id.toString()), 14 | ); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/business_component/listview/novel_series_listview/logic.dart: -------------------------------------------------------------------------------- 1 | import 'package:artvier/model_response/novels/novel_series_list.dart'; 2 | import 'package:artvier/pages/novel/series/model/arguments.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 5 | import 'package:artvier/routes.dart'; 6 | 7 | mixin ManagaSeriesListViewLogic { 8 | WidgetRef get ref; 9 | 10 | void handleTapItem(NovelSeries novelSeries) { 11 | Navigator.of(ref.context).pushNamed( 12 | RouteNames.novelSeriesDetail.name, 13 | arguments: NovelSeriesDetailPagePageArguments( 14 | id: novelSeries.id, 15 | title: novelSeries.title, 16 | url: novelSeries.url, 17 | user: novelSeries.user, 18 | ), 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/business_component/listview/user_vertical_listview/logic.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | import 'package:artvier/config/enums.dart'; 4 | import 'package:artvier/global/model/following_state_changed_arguments%20copy/following_state_changed_arguments.dart'; 5 | import 'package:artvier/global/provider/follow_state_provider.dart'; 6 | import 'package:artvier/model_response/user/common_user_previews.dart'; 7 | import 'package:artvier/model_response/user/preload_user_least_info.dart'; 8 | import 'package:artvier/routes.dart'; 9 | 10 | mixin UserVerticalListViewLogic { 11 | WidgetRef get ref; 12 | 13 | void handleTapItem(CommonUserPreviews user) { 14 | Navigator.of(ref.context).pushNamed( 15 | RouteNames.userDetail.name, 16 | arguments: PreloadUserLeastInfo(user.user.id.toString(), user.user.name, user.user.profileImageUrls.medium), 17 | ); 18 | } 19 | } 20 | 21 | mixin UserVerticalListViewItemLogic { 22 | late UserFollowState followState; 23 | 24 | late String userId; 25 | 26 | WidgetRef get ref; 27 | 28 | late final followStateProvider = StateNotifierProvider.autoDispose((ref) { 29 | // 监听全局美术作品收藏状态通知器的变化 30 | ref.listen(globalFollowingStateChangedProvider, (previous, next) { 31 | if (next != null && next.userId == userId) { 32 | ref.notifier.setFollowState(next.state); 33 | } 34 | }); 35 | return FollowNotifier(followState, ref: ref, userId: userId); 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /lib/business_component/ugoira_image/model.dart: -------------------------------------------------------------------------------- 1 | import 'package:artvier/component/image/gif_image.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | 4 | part 'model.freezed.dart'; 5 | 6 | /// 动图状态 7 | @Freezed( 8 | copyWith: true, 9 | ) 10 | class UgoiraImageState with _$UgoiraImageState { 11 | const factory UgoiraImageState({ 12 | required String illustId, 13 | required List? images, 14 | /** 0 - 1 */ 15 | required double progress, 16 | required UgoiraImageLoadingState loadingState, 17 | }) = _UgoiraImageState; 18 | } 19 | 20 | enum UgoiraImageLoadingState { 21 | /// 暂停中 22 | paused, 23 | 24 | /// 下载中 25 | downloading, 26 | 27 | /// 解压中 28 | unziping, 29 | 30 | /// 合成中 31 | compositing, 32 | 33 | /// 失败 34 | failed, 35 | 36 | /// 成功 37 | success, 38 | } 39 | -------------------------------------------------------------------------------- /lib/component/badge.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class MyBadge extends StatelessWidget { 4 | final Widget child; 5 | final Color? color; 6 | final Border? border; 7 | final GestureTapCallback? onTap; 8 | final EdgeInsets? padding; 9 | final BorderRadius? borderRadius; 10 | 11 | const MyBadge({ 12 | super.key, 13 | required this.child, 14 | this.color, 15 | this.onTap, 16 | this.padding = const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0), 17 | this.border, 18 | this.borderRadius = const BorderRadius.all(Radius.circular(20)), 19 | }); 20 | 21 | @override 22 | Widget build(BuildContext context) { 23 | return LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) { 24 | if (onTap == null) { 25 | return Container( 26 | padding: padding, 27 | decoration: BoxDecoration( 28 | color: color ?? Theme.of(context).colorScheme.primary, 29 | border: border, 30 | borderRadius: borderRadius, 31 | ), 32 | clipBehavior: Clip.antiAlias, 33 | child: child, 34 | ); 35 | } 36 | return Material( 37 | color: color ?? Theme.of(context).colorScheme.primary, 38 | borderRadius: borderRadius, 39 | child: InkWell( 40 | onTap: onTap, 41 | borderRadius: borderRadius, 42 | splashFactory: InkSparkle.splashFactory, 43 | child: Container( 44 | padding: padding, 45 | decoration: BoxDecoration( 46 | border: border, 47 | ), 48 | child: child, 49 | ), 50 | ), 51 | ); 52 | }); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/component/bottom_sheet/close_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// 4 | /// 底部弹窗的包含关闭按钮的顶栏 5 | /// 6 | class BottomSheetCloseBar extends StatelessWidget { 7 | final Widget? title; 8 | 9 | final void Function()? onTapClose; 10 | 11 | final EdgeInsetsGeometry padding; 12 | 13 | const BottomSheetCloseBar({ 14 | super.key, 15 | this.title, 16 | this.onTapClose, 17 | this.padding = const EdgeInsets.all(12.0), 18 | }); 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return Padding( 23 | padding: padding, 24 | child: Row( 25 | children: [ 26 | Expanded( 27 | child: title ?? const Align(), 28 | ), 29 | BottomSheetCloseButton(onTap: onTapClose), 30 | ], 31 | ), 32 | ); 33 | } 34 | } 35 | 36 | /// 底部弹窗的关闭按钮 37 | class BottomSheetCloseButton extends StatelessWidget { 38 | final void Function()? onTap; 39 | 40 | const BottomSheetCloseButton({ 41 | super.key, 42 | this.onTap, 43 | }); 44 | 45 | @override 46 | Widget build(BuildContext context) { 47 | return GestureDetector( 48 | onTap: onTap, 49 | child: DecoratedBox( 50 | decoration: BoxDecoration( 51 | color: Theme.of(context).colorScheme.onSurface.withOpacity(0.1), 52 | borderRadius: const BorderRadius.all(Radius.circular(50)), 53 | ), 54 | child: const Padding( 55 | padding: EdgeInsets.all(4.0), 56 | child: Icon(Icons.close_rounded, size: 16), 57 | ), 58 | ), 59 | ); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/component/bottom_sheet/select_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class BottomSheetSelectItem extends StatelessWidget { 5 | final Widget text; 6 | final void Function() onTap; 7 | final EdgeInsetsGeometry? padding; 8 | final AlignmentGeometry alignment; 9 | 10 | const BottomSheetSelectItem({ 11 | super.key, 12 | this.padding, 13 | this.alignment = Alignment.center, 14 | required this.text, 15 | required this.onTap, 16 | }); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return Container( 21 | width: double.infinity, 22 | color: Theme.of(context).scaffoldBackgroundColor, 23 | padding: EdgeInsets.zero, 24 | child: CupertinoButton( 25 | pressedOpacity: 0.5, 26 | padding: padding ?? EdgeInsets.zero, 27 | color: Theme.of(context).cardColor, 28 | alignment: alignment, 29 | borderRadius: BorderRadius.zero, 30 | onPressed: onTap, 31 | child: DefaultTextStyle( 32 | style: TextStyle(color: Theme.of(context).textTheme.bodyLarge!.color), 33 | child: text, 34 | ), 35 | ), 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/component/bottom_sheet/slide_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// 4 | /// Dialog的顶部小横条 5 | /// 6 | 7 | class BottomSheetSlideBar extends StatelessWidget { 8 | final EdgeInsetsGeometry? padding; 9 | final AlignmentGeometry alignment; 10 | final double width; 11 | final double height; 12 | final Color? color; 13 | final BorderRadiusGeometry borderRadius; 14 | 15 | const BottomSheetSlideBar({ 16 | super.key, 17 | this.width = 32.0, 18 | this.height = 4.0, 19 | this.padding = const EdgeInsets.symmetric(vertical: 8.0), 20 | this.color, 21 | this.alignment = Alignment.center, 22 | this.borderRadius = const BorderRadius.all(Radius.circular(4)), 23 | }); 24 | 25 | @override 26 | Widget build(BuildContext context) { 27 | return Container( 28 | alignment: Alignment.center, 29 | padding: padding, 30 | child: Container( 31 | width: width, 32 | height: height, 33 | decoration: BoxDecoration( 34 | color: color ?? Theme.of(context).colorScheme.outline, 35 | borderRadius: borderRadius, 36 | ), 37 | ), 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/component/drag_view/drag_vertical_panel.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:artvier/component/drag_view/drag_vertical_container.dart'; 3 | 4 | class DragVerticalPanel extends DragVerticalContainer { 5 | const DragVerticalPanel({ 6 | super.key, 7 | super.controller, 8 | super.width = double.infinity, 9 | this.bodyFollowDragFactor = 1.8, 10 | required this.body, 11 | required super.child, 12 | required super.height, 13 | required super.defaultPosition, 14 | required super.dragStageOffset, 15 | required super.maximumPosition, 16 | }) : assert(defaultPosition <= maximumPosition); 17 | 18 | /// Behind the panel widget 19 | final Widget body; 20 | 21 | /// [body] 的拖拽阻力因子,当值为1时,[body] 完全跟随 [panel] 拖动;当>1且越大时,越不跟随拖动 22 | final double bodyFollowDragFactor; 23 | 24 | @override 25 | Widget parentBuilder(BuildContext context, double positionY, DragController dragController, 26 | GestureRecognizerFactoryWithHandlers recognizer) { 27 | return Stack( 28 | children: [ 29 | Transform.translate( 30 | offset: Offset(0.0, -(maximumPosition - positionY) / bodyFollowDragFactor), 31 | child: body, 32 | ), 33 | dragContentBuilder(context, positionY, dragController, recognizer), 34 | ], 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/component/filter/custom_tabbar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | typedef CustomTabbarBuilder = Widget Function(Animation? animation); 4 | 5 | class CustomScrollableTabbar extends StatefulWidget { 6 | const CustomScrollableTabbar({ 7 | super.key, 8 | required this.tabController, 9 | required this.builder, 10 | }); 11 | 12 | final TabController tabController; 13 | 14 | final CustomTabbarBuilder builder; 15 | 16 | @override 17 | State createState() => _CustomScrollableTabbarState(); 18 | } 19 | 20 | class _CustomScrollableTabbarState extends State { 21 | TabController get tabController => widget.tabController; 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | return ListenableBuilder( 26 | listenable: tabController.animation ?? tabController, 27 | builder: (context, child) { 28 | return widget.builder(tabController.animation); 29 | }); 30 | } 31 | } 32 | 33 | // class SliverCustomScrollableTabbar extends CustomScrollableTabbar {} 34 | -------------------------------------------------------------------------------- /lib/component/image/stack_avatar_list.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | class StackAvatarList extends StatelessWidget { 4 | const StackAvatarList({ 5 | super.key, 6 | required this.itemWidth, 7 | required this.offset, 8 | required this.itemBuilder, 9 | this.scrollDirection = Axis.horizontal, 10 | this.reverse = false, 11 | this.itemCount, 12 | }) : assert(offset >= 0); 13 | 14 | final double itemWidth; 15 | 16 | /// 偏移量,类似负数的 margin 17 | final double offset; 18 | final NullableIndexedWidgetBuilder itemBuilder; 19 | final bool reverse; 20 | final int? itemCount; 21 | final Axis scrollDirection; 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | return ListView.builder( 26 | padding: EdgeInsets.symmetric(horizontal: offset / 2), 27 | scrollDirection: scrollDirection, 28 | reverse: reverse, 29 | itemBuilder: (context, index) { 30 | return SizedBox( 31 | width: itemWidth - offset, 32 | child: FittedBox( 33 | fit: BoxFit.none, 34 | child: SizedBox( 35 | width: itemWidth, 36 | height: itemWidth, 37 | child: itemBuilder(context, index), 38 | ), 39 | ), 40 | ); 41 | }, 42 | itemCount: itemCount, 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/component/layout/layout_log_printer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | 4 | class LayoutLogPrint extends StatelessWidget { 5 | /// This widget is quoted from https://book.flutterchina.club/chapter4/layoutbuilder.html#_4-8-1-layoutbuilder 6 | const LayoutLogPrint({ 7 | super.key, 8 | this.tag, 9 | required this.child, 10 | }); 11 | 12 | final Widget child; 13 | final T? tag; //指定日志tag 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return LayoutBuilder(builder: (_, constraints) { 18 | // assert在编译release版本时会被去除 19 | assert(() { 20 | if (kDebugMode) { 21 | print('${tag ?? key ?? child}: $constraints'); 22 | } 23 | return true; 24 | }()); 25 | return child; 26 | }); 27 | } 28 | } 29 | 30 | class SliverLayoutLogPrint extends StatelessWidget { 31 | /// This widget is quoted from https://book.flutterchina.club/chapter4/layoutbuilder.html#_4-8-1-layoutbuilder 32 | const SliverLayoutLogPrint({ 33 | super.key, 34 | this.tag, 35 | required this.child, 36 | }); 37 | 38 | final Widget child; 39 | final T? tag; //指定日志tag 40 | 41 | @override 42 | Widget build(BuildContext context) { 43 | return SliverLayoutBuilder(builder: (_, constraints) { 44 | // assert在编译release版本时会被去除 45 | assert(() { 46 | if (kDebugMode) { 47 | print('${tag ?? key ?? child}: $constraints'); 48 | } 49 | return true; 50 | }()); 51 | return child; 52 | }); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/component/layout/single_line_fitted_box.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | class SingleLineFittedBox extends StatelessWidget { 4 | /// This widget is quoted from https://book.flutterchina.club/chapter5/fittedbox.html#_5-6-1-fittedbox 5 | const SingleLineFittedBox({super.key, this.child}); 6 | final Widget? child; 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return LayoutBuilder( 11 | builder: (context, constraints) => FittedBox( 12 | child: ConstrainedBox( 13 | constraints: constraints.copyWith( 14 | minWidth: constraints.maxWidth, 15 | maxWidth: double.infinity, 16 | // minHeight: constraints.maxHeight, 17 | // maxHeight: double.infinity, 18 | ), 19 | child: child, 20 | ), 21 | ), 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/component/layout/size_reporting_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class SizeReportingWidget extends StatefulWidget { 4 | final Widget child; 5 | 6 | final ValueChanged onSizeChange; 7 | 8 | const SizeReportingWidget({ 9 | super.key, 10 | required this.child, 11 | required this.onSizeChange, 12 | }); 13 | 14 | @override 15 | State createState() => _SizeReportingWidget(); 16 | } 17 | 18 | class _SizeReportingWidget extends State with AutomaticKeepAliveClientMixin { 19 | Size? _oldSize; 20 | 21 | @override 22 | Widget build(BuildContext context) { 23 | super.build(context); 24 | WidgetsBinding.instance.addPostFrameCallback((timeStamp) => _notifySizeChange()); 25 | return NotificationListener( 26 | onNotification: ((notification) { 27 | WidgetsBinding.instance.addPostFrameCallback((timeStamp) => _notifySizeChange()); 28 | return true; 29 | }), 30 | child: SizeChangedLayoutNotifier(child: widget.child), 31 | ); 32 | } 33 | 34 | _notifySizeChange() { 35 | if (!mounted) { 36 | return; 37 | } 38 | final size = context.size; 39 | if (size != null && _oldSize != size) { 40 | _oldSize = size; 41 | widget.onSizeChange(size); 42 | } 43 | } 44 | 45 | @override 46 | bool get wantKeepAlive => true; 47 | } 48 | -------------------------------------------------------------------------------- /lib/component/loading/lazyloading.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:artvier/l10n/localization_intl.dart'; 3 | 4 | class LazyloadingWidget extends StatelessWidget { 5 | const LazyloadingWidget({super.key}); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return Container( 10 | alignment: Alignment.center, 11 | child: const CircularProgressIndicator(strokeWidth: 1.0), 12 | ); 13 | } 14 | } 15 | 16 | class LazyloadingFailedWidget extends StatelessWidget { 17 | /// 点击重试的事件 18 | final VoidCallback onRetry; 19 | 20 | const LazyloadingFailedWidget({ 21 | super.key, 22 | required this.onRetry, 23 | }); 24 | 25 | @override 26 | Widget build(BuildContext context) { 27 | return Wrap( 28 | direction: Axis.vertical, 29 | children: [ 30 | Text(LocalizationIntl.of(context).requestFailed), 31 | TextButton(onPressed: onRetry, child: Text(LocalizationIntl.of(context).retryOnFailure)), 32 | ], 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/component/loading/request_loading.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:artvier/l10n/localization_intl.dart'; 3 | 4 | /// 网络请求加载中显示的组件 5 | class RequestLoading extends StatelessWidget { 6 | const RequestLoading({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return Center( 11 | child: Wrap( 12 | direction: Axis.vertical, 13 | crossAxisAlignment: WrapCrossAlignment.center, 14 | children: [ 15 | Image.asset("assets/image/page_loading.gif", height: 100), 16 | const Text("Loading..."), 17 | ], 18 | ), 19 | ); 20 | } 21 | } 22 | 23 | /// 网络请求加载失败时显示的组件 24 | class RequestLoadingFailed extends StatelessWidget { 25 | /// 点击重试的事件 26 | final VoidCallback onRetry; 27 | 28 | const RequestLoadingFailed({ 29 | super.key, 30 | required this.onRetry, 31 | }); 32 | 33 | @override 34 | Widget build(BuildContext context) { 35 | return Center( 36 | child: Wrap( 37 | direction: Axis.vertical, 38 | crossAxisAlignment: WrapCrossAlignment.center, 39 | children: [ 40 | Padding( 41 | padding: const EdgeInsets.symmetric(vertical: 8.0), 42 | child: Opacity(opacity: 0.75, child: Image.asset("assets/image/network_error.png", height: 100)), 43 | ), 44 | Text(LocalizationIntl.of(context).requestFailed), 45 | TextButton(onPressed: onRetry, child: Text(LocalizationIntl.of(context).retryOnFailure)), 46 | ], 47 | ), 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/component/perference/perference_container.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | /// 偏好设置的容器,可搭配 [PerferenceGroup], [PerferenceItem] 使用 4 | class PerferenceContainer extends StatelessWidget { 5 | const PerferenceContainer({ 6 | super.key, 7 | required this.groups, 8 | }); 9 | 10 | /// [PerferenceGroup] List 11 | final List groups; 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Padding( 16 | padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), 17 | child: Column( 18 | children: groups, 19 | ), 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/component/perference/preferences_navigator_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class PreferencesNavigatorItem extends StatelessWidget { 4 | final Widget text; 5 | final Widget? icon; 6 | final Function()? onTap; 7 | final EdgeInsetsGeometry? padding; 8 | 9 | const PreferencesNavigatorItem({super.key, required this.text, this.icon, this.onTap, this.padding}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return InkWell( 14 | onTap: onTap, 15 | child: Container( 16 | padding: padding, 17 | child: Row( 18 | crossAxisAlignment: CrossAxisAlignment.center, 19 | children: [ 20 | icon ?? Container(), 21 | Expanded( 22 | flex: 1, 23 | child: text, 24 | ), 25 | Icon( 26 | Icons.arrow_forward_ios_rounded, 27 | color: Theme.of(context).colorScheme.outline, 28 | size: 12, 29 | ), 30 | ], 31 | ), 32 | ), 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/component/scroll_physics/top_clamping_bouncing_scroll_physics.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | 3 | class TopClampingBouncingScrollPhysics extends BouncingScrollPhysics { 4 | const TopClampingBouncingScrollPhysics({super.parent}); 5 | 6 | @override 7 | TopClampingBouncingScrollPhysics applyTo(ScrollPhysics? ancestor) { 8 | return TopClampingBouncingScrollPhysics(parent: buildParent(ancestor)); 9 | } 10 | 11 | @override 12 | double applyBoundaryConditions(ScrollMetrics position, double value) { 13 | if (value < position.pixels && position.pixels <= position.minScrollExtent) { 14 | // Underscroll. 15 | return value - position.pixels; 16 | } 17 | if (value < position.minScrollExtent && position.minScrollExtent < position.pixels) { 18 | // Hit top edge. 19 | return value - position.minScrollExtent; 20 | } 21 | if (position.pixels < position.maxScrollExtent && position.maxScrollExtent < value) { 22 | // Hit bottom edge. 23 | return value - position.maxScrollExtent; 24 | } 25 | return super.applyBoundaryConditions(position, value); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/component/sliver_persistent_header/tab_bar_delegate.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * TabBar实现吸附顶端效果所需的Delegate */ 3 | import 'package:flutter/material.dart'; 4 | 5 | const double kTabBarHeight = 46; 6 | 7 | class SliverTabBarPersistentHeaderDelegate extends SliverPersistentHeaderDelegate { 8 | final TabBar child; 9 | 10 | EdgeInsets? padding; 11 | 12 | Color? backgroundColor; 13 | 14 | double? maxHeight; 15 | 16 | double? minHeight; 17 | 18 | SliverTabBarPersistentHeaderDelegate({ 19 | required this.child, 20 | this.padding, 21 | this.backgroundColor, 22 | this.maxHeight, 23 | this.minHeight, 24 | }); 25 | 26 | @override 27 | Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) { 28 | return Container( 29 | color: backgroundColor ?? BottomAppBarTheme.of(context).color, 30 | padding: padding, 31 | child: child, 32 | ); 33 | } 34 | 35 | @override 36 | double get maxExtent => maxHeight ?? child.preferredSize.height; 37 | 38 | @override 39 | double get minExtent => minHeight ?? child.preferredSize.height; 40 | 41 | @override 42 | bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) { 43 | return true; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/component/sliver_persistent_header/widget_delegate.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * 吸附顶端效果所需的Delegate */ 3 | 4 | import 'package:flutter/material.dart'; 5 | 6 | /// 参考 [FlexibleSpaceBar] 中的 [CollapseMode] 7 | enum CollapseMode { 8 | parallax, 9 | 10 | pin, 11 | 12 | none, 13 | } 14 | 15 | class SliverWidgetPersistentHeaderDelegate extends SliverPersistentHeaderDelegate { 16 | final Widget child; 17 | 18 | double maxHeight; 19 | 20 | double minHeight; 21 | 22 | CollapseMode collapseMode; 23 | 24 | SliverWidgetPersistentHeaderDelegate({ 25 | required this.child, 26 | required this.maxHeight, 27 | required this.minHeight, 28 | this.collapseMode = CollapseMode.parallax, 29 | }); 30 | 31 | @override 32 | Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) { 33 | return child; 34 | } 35 | 36 | @override 37 | double get maxExtent => maxHeight; 38 | 39 | @override 40 | double get minExtent => minHeight; 41 | 42 | @override 43 | bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) { 44 | return true; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/config/enums.dart: -------------------------------------------------------------------------------- 1 | /// 页面所处的状态 2 | enum PageState { 3 | /// 首次加载中 4 | loading, 5 | 6 | /// 刷新中(非首次加载) 7 | refreshing, 8 | 9 | /// 完成(且非空) 10 | complete, 11 | 12 | /// 完成但空内容 13 | empty, 14 | 15 | /// 加载出错 16 | error, 17 | } 18 | 19 | /// 懒加载的状态 20 | enum LazyloadState { 21 | /// 闲置;此时并未滚动到底,但还有数据未加载 22 | idle, 23 | 24 | /// 正在加载数据中;此时应该拦截重复触发的加载请求 25 | loading, 26 | 27 | /// 没有更多了 28 | noMore, 29 | 30 | /// 懒加载出错 31 | error, 32 | } 33 | 34 | /// 收藏状态 35 | enum CollectState { 36 | /// 请求收藏中 37 | collecting, 38 | 39 | /// 请求取消收藏 40 | uncollecting, 41 | 42 | /// 已收藏 43 | collected, 44 | 45 | /// 未收藏 46 | notCollect, 47 | } 48 | 49 | /// 用户关注状态 50 | enum UserFollowState { 51 | /// 请求关注中 52 | requestingFollow, 53 | 54 | /// 请求取消关注 55 | requestingUnfollow, 56 | 57 | /// 已关注 58 | followed, 59 | 60 | /// 未关注 61 | notFollow, 62 | } 63 | 64 | /// 作品类型 65 | enum WorksType { 66 | illust, 67 | manga, 68 | mangaSeries, 69 | novel, 70 | } 71 | 72 | /// 隐私(可见性)限制 73 | enum Restrict { 74 | /// 公开的 75 | public, 76 | 77 | /// 非公开的 78 | private, 79 | } 80 | 81 | /// 隐私(可见性)限制,完整版 82 | enum RestrictAll { 83 | /// 全部 84 | all, 85 | 86 | /// 公开的 87 | public, 88 | 89 | /// 非公开的 90 | private, 91 | } 92 | 93 | /// 下载(保存)状态 94 | enum DownloadState { 95 | /// 下载中 96 | downloading, 97 | 98 | /// 暂停 99 | paused, 100 | 101 | /// 下载失败 102 | failed, 103 | 104 | /// 等待中 105 | waiting, 106 | 107 | /// 下载成功 108 | success, 109 | } 110 | 111 | /// 下载资源类型 112 | enum DownloadType { 113 | illust, 114 | manga, 115 | } 116 | 117 | /// 搜索类型 118 | enum SearchType { 119 | artwork, 120 | novel, 121 | user, 122 | } 123 | -------------------------------------------------------------------------------- /lib/config/http_base_options.dart: -------------------------------------------------------------------------------- 1 | /// Http请求的默认参数 2 | class HttpBaseOptions { 3 | static get baseUrl => 'https://$appApiHost'; 4 | 5 | // Get ip by https://dns.sb/doh 6 | static const String appApiHost = "app-api.pixiv.net"; 7 | static const String appApiIp = "210.140.139.152"; 8 | 9 | static const String oauthHost = "oauth.secure.pixiv.net"; 10 | static const String oauthIp = "210.140.139.152"; 11 | 12 | static const String pximgHost = "i.pximg.net"; 13 | static const String pximg2Host = "s.pximg.net"; 14 | static const String pximgIp = "210.140.139.134"; 15 | static const String pximg2Ip = "210.140.139.134"; 16 | static const String pixivisionHost = "www.pixivision.net"; 17 | static const String pixivisionIp = "210.140.139.155"; 18 | 19 | // 网络请求的各个时间 20 | static const Duration connectTimeout = Duration(seconds: 12); 21 | static const Duration receiveTimeout = Duration(seconds: 12); 22 | static const Duration sendTimeout = Duration(seconds: 12); 23 | } 24 | -------------------------------------------------------------------------------- /lib/global/logger.dart: -------------------------------------------------------------------------------- 1 | import 'package:logger/logger.dart'; 2 | 3 | var logger = Logger(); -------------------------------------------------------------------------------- /lib/global/model/collect_state_changed_arguments/collect_state_changed_arguments.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:artvier/config/enums.dart'; 3 | 4 | part 'collect_state_changed_arguments.freezed.dart'; 5 | 6 | /// 作品收藏状态发生变化时传递的参数 7 | /// 8 | /// 插画、漫画、小说均通用 9 | /// 10 | /// Suitable for illusts, manga, novels. 11 | @Freezed( 12 | copyWith: true, 13 | ) 14 | class CollectStateChangedArguments with _$CollectStateChangedArguments { 15 | const factory CollectStateChangedArguments({ 16 | required String worksId, 17 | required CollectState state, 18 | }) = _CollectStateChangedArguments; 19 | } 20 | -------------------------------------------------------------------------------- /lib/global/model/following_state_changed_arguments copy/following_state_changed_arguments.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:artvier/config/enums.dart'; 3 | 4 | part 'following_state_changed_arguments.freezed.dart'; 5 | 6 | /// 用户关注状态发生变化时传递的参数 7 | @Freezed( 8 | copyWith: true, 9 | ) 10 | class FollowingStateChangedArguments with _$FollowingStateChangedArguments { 11 | const factory FollowingStateChangedArguments({ 12 | required String userId, 13 | required UserFollowState state, 14 | }) = _FollowingStateChangedArguments; 15 | } 16 | -------------------------------------------------------------------------------- /lib/global/model/image_download_task_model/image_download_task_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'image_download_task_model.freezed.dart'; 4 | part 'image_download_task_model.g.dart'; 5 | 6 | /// 美术作品下载进程的数据模型 7 | /// 8 | /// 注:一件(插画漫画)作品有多张图片 9 | @Freezed( 10 | copyWith: true, 11 | fromJson: true, 12 | toJson: true, 13 | ) 14 | class ImageDownloadTaskModel with _$ImageDownloadTaskModel { 15 | const factory ImageDownloadTaskModel({ 16 | /// 所属作品的ID 17 | required String worksId, 18 | 19 | /// 图片在作品中的索引,eg: p0, p1 p2 p3 20 | required int pIndex, 21 | 22 | /// 参阅[DownloadStorage.downloadStateMap] 23 | required int downloadState, 24 | 25 | /// 已下载进度(字节) 26 | double? receivedBytes, 27 | 28 | /// 图片总大小(字节) 29 | double? totalBytes, 30 | 31 | required String url, 32 | required String title, 33 | }) = _ImageDownloadTaskModel; 34 | 35 | factory ImageDownloadTaskModel.fromJson(Map json) => _$ImageDownloadTaskModelFromJson(json); 36 | } 37 | -------------------------------------------------------------------------------- /lib/global/model/image_download_task_model/image_download_task_model.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | // ignore_for_file: non_constant_identifier_names 4 | 5 | part of 'image_download_task_model.dart'; 6 | 7 | // ************************************************************************** 8 | // JsonSerializableGenerator 9 | // ************************************************************************** 10 | 11 | _$ImageDownloadTaskModelImpl _$$ImageDownloadTaskModelImplFromJson( 12 | Map json) => 13 | _$ImageDownloadTaskModelImpl( 14 | worksId: json['worksId'] as String, 15 | pIndex: (json['pIndex'] as num).toInt(), 16 | downloadState: (json['downloadState'] as num).toInt(), 17 | receivedBytes: (json['receivedBytes'] as num?)?.toDouble(), 18 | totalBytes: (json['totalBytes'] as num?)?.toDouble(), 19 | url: json['url'] as String, 20 | title: json['title'] as String, 21 | ); 22 | 23 | Map _$$ImageDownloadTaskModelImplToJson( 24 | _$ImageDownloadTaskModelImpl instance) => 25 | { 26 | 'worksId': instance.worksId, 27 | 'pIndex': instance.pIndex, 28 | 'downloadState': instance.downloadState, 29 | 'receivedBytes': instance.receivedBytes, 30 | 'totalBytes': instance.totalBytes, 31 | 'url': instance.url, 32 | 'title': instance.title, 33 | }; 34 | -------------------------------------------------------------------------------- /lib/global/model/novel_viewer/novel_viewer_settings_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'novel_viewer_settings_model.freezed.dart'; 4 | 5 | /// 图片源(图床) 6 | @Freezed( 7 | copyWith: true, 8 | ) 9 | class NovelViewerSettingsModel with _$NovelViewerSettingsModel { 10 | const factory NovelViewerSettingsModel({ 11 | required double textSize, 12 | }) = _NovelViewerSettingsModel; 13 | } 14 | -------------------------------------------------------------------------------- /lib/global/model/proxy_options/image_hosting_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'image_hosting_model.freezed.dart'; 4 | 5 | /// 图片源(图床) 6 | @Freezed( 7 | copyWith: true, 8 | ) 9 | class ImageHostingModel with _$ImageHostingModel { 10 | const factory ImageHostingModel({ 11 | /// 图片源 12 | required String host, 13 | 14 | /// 是否开启自定义图片源 15 | required bool isEnabled, 16 | }) = _ImageHostingModel; 17 | } 18 | -------------------------------------------------------------------------------- /lib/global/model/proxy_options/proxy_state_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'proxy_state_model.freezed.dart'; 4 | 5 | /// 代理的相关配置模型 6 | @Freezed( 7 | copyWith: true, 8 | ) 9 | class ProxyStateModel with _$ProxyStateModel { 10 | const factory ProxyStateModel({ 11 | /// 主机 12 | required String host, 13 | 14 | /// 端口 15 | required String port, 16 | 17 | /// 是否开启代理 18 | required bool isProxyEnabled, 19 | }) = _ProxyStateModel; 20 | } 21 | -------------------------------------------------------------------------------- /lib/global/model/works_badge_argument/works_badge_argument.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | 4 | part 'works_badge_argument.freezed.dart'; 5 | 6 | /// 作品角标的参数 7 | @Freezed( 8 | copyWith: true, 9 | ) 10 | class WorksBadgeArgument with _$WorksBadgeArgument { 11 | const factory WorksBadgeArgument({ 12 | required String text, 13 | Color? textColor, 14 | Color? backgroundColor, 15 | }) = _WorksBadgeArgument; 16 | } 17 | -------------------------------------------------------------------------------- /lib/global/provider/current_user_detail.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 4 | import 'package:artvier/api_app/api_user.dart'; 5 | import 'package:artvier/base/base_provider/base_notifier.dart'; 6 | import 'package:artvier/global/provider/current_account_provider.dart'; 7 | import 'package:artvier/model_response/user/user_detail.dart'; 8 | 9 | /// 当前用户的详细信息 10 | final globalCurrentUserDetailProvider = StateNotifierProvider.autoDispose((ref) { 11 | String? userId = ref.watch(globalCurrentAccountProvider)?.user.id; 12 | return CurrentUserDetailNotifier(null, ref: ref, userId: userId)..fetch(); 13 | }); 14 | 15 | /// 当前用户的详细信息 16 | class CurrentUserDetailNotifier extends BaseStateNotifier { 17 | CurrentUserDetailNotifier(super.state, {required super.ref, required this.userId}); 18 | 19 | final String? userId; 20 | 21 | Future fetch() async { 22 | if (userId == null) return; 23 | var detail = await ApiUser(requester).userDetail(userId!); 24 | state = detail; 25 | } 26 | 27 | /// 失败后的重试 28 | Future retry() async => await fetch(); 29 | } 30 | -------------------------------------------------------------------------------- /lib/global/provider/current_works_type.dart: -------------------------------------------------------------------------------- 1 | import 'package:artvier/config/enums.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | 4 | final globalCurrentWorksTypeProvider = StateProvider((ref) { 5 | return WorksType.illust; 6 | }); 7 | -------------------------------------------------------------------------------- /lib/global/provider/download_tasks_provider.dart: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lib/global/provider/language_provider.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | import 'package:artvier/base/base_provider/base_notifier.dart'; 3 | import 'package:artvier/global/variable.dart'; 4 | import 'package:artvier/request/http_host_overrides.dart'; 5 | import 'package:artvier/storage/language_store.dart'; 6 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 7 | 8 | final globalLanguageProvider = StateNotifierProvider((ref) { 9 | final languageStore = LanguageStorage(globalSharedPreferences); 10 | final autoLanguage = languageStore.getAutoLanguage(); 11 | final locale = autoLanguage ? null : languageStore.getLanguageLocale(); 12 | return LanguageNotivier(locale, ref: ref); 13 | }); 14 | 15 | /// Current Lanuange. 16 | class LanguageNotivier extends BaseStateNotifier { 17 | LanguageNotivier(super.state, {required super.ref}); 18 | 19 | /// Set null to enable auto language. 20 | Future setLocale(Locale? locale) async { 21 | final languageStore = LanguageStorage(globalSharedPreferences); 22 | if (locale == null) { 23 | final result = await languageStore.setAutoLanguage(true); 24 | update(null); 25 | HttpHostOverrides().reload(); 26 | return result; 27 | } else { 28 | assert(locale.countryCode != null); 29 | final result = await languageStore.setLanguage(locale.languageCode, locale.countryCode!); 30 | final result2 = await languageStore.setAutoLanguage(false); 31 | if (result) { 32 | update(locale); 33 | HttpHostOverrides().reload(); 34 | } 35 | return result && result2; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/global/provider/shared_preferences_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 2 | import 'package:shared_preferences/shared_preferences.dart'; 3 | 4 | /// 本地持久化存储,用于读取和保存数据到本地。 5 | /// 6 | /// Shared preferences, used to read and save data locally. 7 | final globalSharedPreferencesProvider = Provider( 8 | (ref) => throw UnimplementedError(), 9 | ); 10 | 11 | // late final SharedPreferences globalSharedPreferences; 12 | -------------------------------------------------------------------------------- /lib/global/provider/themes_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | import 'package:artvier/base/base_provider/base_notifier.dart'; 4 | import 'package:artvier/global/provider/shared_preferences_provider.dart'; 5 | import 'package:artvier/storage/theme_storage.dart'; 6 | 7 | /// 全局主题模式,浅色、深色、跟随系统 8 | final globalThemeModeProvider = StateNotifierProvider((ref) { 9 | // 本地缓存的主题模式(不存在则默认跟随系统) 10 | var localThemeMode = ThemeStorage(ref.watch(globalSharedPreferencesProvider)).themeMode(); 11 | 12 | return ThemeModeNotifier(localThemeMode, ref: ref); 13 | }); 14 | 15 | class ThemeModeNotifier extends BaseStateNotifier { 16 | ThemeModeNotifier(super.state, {required super.ref}); 17 | 18 | /// 切换主题模式 19 | /// - 返回是否成功 20 | Future switchThemeMode(ThemeMode themeMode) async { 21 | if (await ThemeStorage(prefs).setThemeMode(themeMode)) { 22 | state = themeMode; 23 | return true; 24 | } else { 25 | return false; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/global/provider/version_and_update_provider.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:artvier/api_app/api_update.dart'; 4 | import 'package:artvier/base/base_provider/base_notifier.dart'; 5 | import 'package:artvier/global/provider/requester_provider.dart'; 6 | import 'package:artvier/model_response/update/git_release.dart'; 7 | import 'package:dio/dio.dart'; 8 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 9 | import 'package:package_info_plus/package_info_plus.dart'; 10 | 11 | final packageInfoProvider = FutureProvider.autoDispose((ref) async { 12 | return PackageInfo.fromPlatform(); 13 | }); 14 | 15 | final globalLastVersionProvider = AsyncNotifierProvider(() { 16 | return LastVersionNotifier(); 17 | }); 18 | 19 | class LastVersionNotifier extends BaseAsyncNotifier { 20 | CancelToken cancelToken = CancelToken(); 21 | 22 | @override 23 | FutureOr build() { 24 | ref.onDispose(() { 25 | if (!cancelToken.isCancelled) cancelToken.cancel(); 26 | }); 27 | return fetch(); 28 | } 29 | 30 | @override 31 | Future fetch() { 32 | return ApiUpdate(ref.read(originHttpRequesterProvider)).lastRelease(cancelToken: cancelToken); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/global/variable.dart: -------------------------------------------------------------------------------- 1 | import 'package:shared_preferences/shared_preferences.dart'; 2 | 3 | // 全局变量集中地 4 | 5 | late final SharedPreferences globalSharedPreferences; // TODO: 全部 SharedPreferences 转到该对象上 6 | 7 | /// 基本请求头 8 | late final Map globalBaseHttpHeaders; 9 | -------------------------------------------------------------------------------- /lib/l10n/messages_all.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that looks up messages for specific locales by 3 | // delegating to the appropriate library. 4 | // @dart=2.12 5 | export 'messages_all_locales.dart' 6 | show initializeMessages; 7 | 8 | -------------------------------------------------------------------------------- /lib/model_response/common/collection_detail.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'collection_detail.g.dart'; 4 | 5 | @JsonSerializable(explicitToJson: true) 6 | class TheCollectionDetail{ 7 | 8 | @JsonKey(name: 'bookmark_detail') 9 | WorksCollectDetail? detail; 10 | 11 | TheCollectionDetail(); 12 | 13 | static TheCollectionDetail fromJson(Map srcJson) => _$TheCollectionDetailFromJson(srcJson); 14 | 15 | Map toJson() => _$TheCollectionDetailToJson(this); 16 | 17 | } 18 | 19 | 20 | @JsonSerializable(explicitToJson: true) 21 | class WorksCollectDetail{ 22 | 23 | @JsonKey(name: 'is_bookmarked') 24 | bool? isBookmarked; 25 | 26 | @JsonKey(name: 'tags') 27 | List? tags; 28 | 29 | @JsonKey(name: 'restrict') 30 | String? restrict; 31 | 32 | WorksCollectDetail({this.isBookmarked,this.restrict,this.tags}); 33 | 34 | static WorksCollectDetail fromJson(Map srcJson) => _$WorksCollectDetailFromJson(srcJson); 35 | 36 | Map toJson() => _$WorksCollectDetailToJson(this); 37 | 38 | } 39 | 40 | 41 | @JsonSerializable(explicitToJson: true) 42 | class WorksCollectTag{ 43 | 44 | @JsonKey(name: 'name') 45 | String? name; 46 | 47 | @JsonKey(name: 'is_registered') 48 | bool? isRegistered; 49 | 50 | WorksCollectTag({this.name, this.isRegistered}); 51 | 52 | static WorksCollectTag fromJson(Map srcJson) => _$WorksCollectTagFromJson(srcJson); 53 | 54 | Map toJson() => _$WorksCollectTagToJson(this); 55 | 56 | } -------------------------------------------------------------------------------- /lib/model_response/common/predictive_search.dart: -------------------------------------------------------------------------------- 1 | // To parse this JSON data, do 2 | // 3 | // final predictiveSearchWorks = predictiveSearchWorksFromJson(jsonString); 4 | 5 | import 'package:freezed_annotation/freezed_annotation.dart'; 6 | import 'dart:convert'; 7 | 8 | part 'predictive_search.freezed.dart'; 9 | part 'predictive_search.g.dart'; 10 | 11 | PredictiveSearchWorks predictiveSearchWorksFromJson(String str) => PredictiveSearchWorks.fromJson(json.decode(str)); 12 | 13 | String predictiveSearchWorksToJson(PredictiveSearchWorks data) => json.encode(data.toJson()); 14 | 15 | @freezed 16 | class PredictiveSearchWorks with _$PredictiveSearchWorks { 17 | const factory PredictiveSearchWorks({ 18 | @JsonKey(name: "tags") 19 | required List tags, 20 | }) = _PredictiveSearchWorks; 21 | 22 | factory PredictiveSearchWorks.fromJson(Map json) => _$PredictiveSearchWorksFromJson(json); 23 | } 24 | 25 | @freezed 26 | class PredictiveSearchWorksTag with _$PredictiveSearchWorksTag { 27 | const factory PredictiveSearchWorksTag({ 28 | @JsonKey(name: "name") 29 | required String name, 30 | @JsonKey(name: "translated_name") 31 | String? translatedName, 32 | }) = _PredictiveSearchWorksTag; 33 | 34 | factory PredictiveSearchWorksTag.fromJson(Map json) => _$PredictiveSearchWorksTagFromJson(json); 35 | } 36 | -------------------------------------------------------------------------------- /lib/model_response/common/predictive_search.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | // ignore_for_file: non_constant_identifier_names 4 | 5 | part of 'predictive_search.dart'; 6 | 7 | // ************************************************************************** 8 | // JsonSerializableGenerator 9 | // ************************************************************************** 10 | 11 | _$PredictiveSearchWorksImpl _$$PredictiveSearchWorksImplFromJson( 12 | Map json) => 13 | _$PredictiveSearchWorksImpl( 14 | tags: (json['tags'] as List) 15 | .map((e) => 16 | PredictiveSearchWorksTag.fromJson(e as Map)) 17 | .toList(), 18 | ); 19 | 20 | Map _$$PredictiveSearchWorksImplToJson( 21 | _$PredictiveSearchWorksImpl instance) => 22 | { 23 | 'tags': instance.tags, 24 | }; 25 | 26 | _$PredictiveSearchWorksTagImpl _$$PredictiveSearchWorksTagImplFromJson( 27 | Map json) => 28 | _$PredictiveSearchWorksTagImpl( 29 | name: json['name'] as String, 30 | translatedName: json['translated_name'] as String?, 31 | ); 32 | 33 | Map _$$PredictiveSearchWorksTagImplToJson( 34 | _$PredictiveSearchWorksTagImpl instance) => 35 | { 36 | 'name': instance.name, 37 | 'translated_name': instance.translatedName, 38 | }; 39 | -------------------------------------------------------------------------------- /lib/model_response/illusts/common_illust_list.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | import 'package:artvier/model_response/illusts/common_illust.dart'; 3 | 4 | part 'common_illust_list.g.dart'; 5 | 6 | 7 | @JsonSerializable() 8 | /// - 与漫画通用 9 | class CommonIllustList extends Object { 10 | 11 | @JsonKey(name: 'illusts') 12 | List illusts; 13 | 14 | @JsonKey(name: 'next_url') 15 | String? nextUrl; 16 | 17 | CommonIllustList(this.illusts, this.nextUrl); 18 | 19 | factory CommonIllustList.fromJson(Map srcJson) => _$CommonIllustListFromJson(srcJson); 20 | 21 | Map toJson() => _$CommonIllustListToJson(this); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /lib/model_response/illusts/common_illust_list.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | // ignore_for_file: non_constant_identifier_names 4 | 5 | part of 'common_illust_list.dart'; 6 | 7 | // ************************************************************************** 8 | // JsonSerializableGenerator 9 | // ************************************************************************** 10 | 11 | CommonIllustList _$CommonIllustListFromJson(Map json) => 12 | CommonIllustList( 13 | (json['illusts'] as List) 14 | .map((e) => CommonIllust.fromJson(e as Map)) 15 | .toList(), 16 | json['next_url'] as String?, 17 | ); 18 | 19 | Map _$CommonIllustListToJson(CommonIllustList instance) => 20 | { 21 | 'illusts': instance.illusts, 22 | 'next_url': instance.nextUrl, 23 | }; 24 | -------------------------------------------------------------------------------- /lib/model_response/illusts/illust_comment_response.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: camel_case_types 2 | 3 | import 'package:artvier/model_response/illusts/illust_comments.dart'; 4 | import 'package:json_annotation/json_annotation.dart'; 5 | 6 | part 'illust_comment_response.g.dart'; 7 | 8 | @JsonSerializable() 9 | class IllustCommentResponse extends Object { 10 | @JsonKey(name: 'comment') 11 | Comments comment; 12 | 13 | IllustCommentResponse(this.comment); 14 | 15 | factory IllustCommentResponse.fromJson(Map srcJson) => _$IllustCommentResponseFromJson(srcJson); 16 | 17 | Map toJson() => _$IllustCommentResponseToJson(this); 18 | } 19 | -------------------------------------------------------------------------------- /lib/model_response/illusts/illust_comment_response.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | // ignore_for_file: non_constant_identifier_names 4 | 5 | part of 'illust_comment_response.dart'; 6 | 7 | // ************************************************************************** 8 | // JsonSerializableGenerator 9 | // ************************************************************************** 10 | 11 | IllustCommentResponse _$IllustCommentResponseFromJson( 12 | Map json) => 13 | IllustCommentResponse( 14 | Comments.fromJson(json['comment'] as Map), 15 | ); 16 | 17 | Map _$IllustCommentResponseToJson( 18 | IllustCommentResponse instance) => 19 | { 20 | 'comment': instance.comment, 21 | }; 22 | -------------------------------------------------------------------------------- /lib/model_response/illusts/illust_detail.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | import 'package:artvier/model_response/illusts/common_illust.dart'; 3 | 4 | part 'illust_detail.g.dart'; 5 | 6 | @JsonSerializable() 7 | 8 | /// - 与漫画通用 9 | class IllustDetail extends Object { 10 | @JsonKey(name: 'illust') 11 | CommonIllust illust; 12 | 13 | IllustDetail(this.illust); 14 | 15 | factory IllustDetail.fromJson(Map srcJson) => _$IllustDetailFromJson(srcJson); 16 | 17 | Map toJson() => _$IllustDetailToJson(this); 18 | } 19 | -------------------------------------------------------------------------------- /lib/model_response/illusts/illust_detail.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | // ignore_for_file: non_constant_identifier_names 4 | 5 | part of 'illust_detail.dart'; 6 | 7 | // ************************************************************************** 8 | // JsonSerializableGenerator 9 | // ************************************************************************** 10 | 11 | IllustDetail _$IllustDetailFromJson(Map json) => IllustDetail( 12 | CommonIllust.fromJson(json['illust'] as Map), 13 | ); 14 | 15 | Map _$IllustDetailToJson(IllustDetail instance) => 16 | { 17 | 'illust': instance.illust, 18 | }; 19 | -------------------------------------------------------------------------------- /lib/model_response/illusts/illust_trending_tags.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | import 'common_illust.dart'; 4 | 5 | part 'illust_trending_tags.g.dart'; 6 | 7 | 8 | @JsonSerializable() 9 | class IllustTrendingTags extends Object { 10 | 11 | @JsonKey(name: 'trend_tags') 12 | List trendTags; 13 | 14 | IllustTrendingTags(this.trendTags,); 15 | 16 | factory IllustTrendingTags.fromJson(Map srcJson) => _$IllustTrendingTagsFromJson(srcJson); 17 | 18 | Map toJson() => _$IllustTrendingTagsToJson(this); 19 | 20 | } 21 | 22 | @JsonSerializable() 23 | class TrendTags extends Object { 24 | 25 | @JsonKey(name: 'tag') 26 | String tag; 27 | 28 | @JsonKey(name: 'translated_name') 29 | String? translatedName; 30 | 31 | @JsonKey(name: 'illust') 32 | CommonIllust illust; 33 | 34 | TrendTags(this.tag,this.translatedName,this.illust,); 35 | 36 | factory TrendTags.fromJson(Map srcJson) => _$TrendTagsFromJson(srcJson); 37 | 38 | Map toJson() => _$TrendTagsToJson(this); 39 | 40 | } -------------------------------------------------------------------------------- /lib/model_response/illusts/illust_trending_tags.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | // ignore_for_file: non_constant_identifier_names 4 | 5 | part of 'illust_trending_tags.dart'; 6 | 7 | // ************************************************************************** 8 | // JsonSerializableGenerator 9 | // ************************************************************************** 10 | 11 | IllustTrendingTags _$IllustTrendingTagsFromJson(Map json) => 12 | IllustTrendingTags( 13 | (json['trend_tags'] as List) 14 | .map((e) => TrendTags.fromJson(e as Map)) 15 | .toList(), 16 | ); 17 | 18 | Map _$IllustTrendingTagsToJson(IllustTrendingTags instance) => 19 | { 20 | 'trend_tags': instance.trendTags, 21 | }; 22 | 23 | TrendTags _$TrendTagsFromJson(Map json) => TrendTags( 24 | json['tag'] as String, 25 | json['translated_name'] as String?, 26 | CommonIllust.fromJson(json['illust'] as Map), 27 | ); 28 | 29 | Map _$TrendTagsToJson(TrendTags instance) => { 30 | 'tag': instance.tag, 31 | 'translated_name': instance.translatedName, 32 | 'illust': instance.illust, 33 | }; 34 | -------------------------------------------------------------------------------- /lib/model_response/illusts/illusts_search_result.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | import 'package:artvier/model_response/illusts/common_illust.dart'; 3 | 4 | part 'illusts_search_result.g.dart'; 5 | 6 | 7 | @JsonSerializable() 8 | class IllustsSearchResult extends Object { 9 | 10 | @JsonKey(name: 'illusts') 11 | List illusts; 12 | 13 | @JsonKey(name: 'next_url') 14 | String? nextUrl; 15 | 16 | @JsonKey(name: 'search_span_limit') 17 | int searchSpanLimit; 18 | 19 | IllustsSearchResult(this.illusts,this.nextUrl,this.searchSpanLimit,); 20 | 21 | factory IllustsSearchResult.fromJson(Map srcJson) => _$IllustsSearchResultFromJson(srcJson); 22 | 23 | Map toJson() => _$IllustsSearchResultToJson(this); 24 | 25 | } -------------------------------------------------------------------------------- /lib/model_response/illusts/illusts_search_result.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | // ignore_for_file: non_constant_identifier_names 4 | 5 | part of 'illusts_search_result.dart'; 6 | 7 | // ************************************************************************** 8 | // JsonSerializableGenerator 9 | // ************************************************************************** 10 | 11 | IllustsSearchResult _$IllustsSearchResultFromJson(Map json) => 12 | IllustsSearchResult( 13 | (json['illusts'] as List) 14 | .map((e) => CommonIllust.fromJson(e as Map)) 15 | .toList(), 16 | json['next_url'] as String?, 17 | (json['search_span_limit'] as num).toInt(), 18 | ); 19 | 20 | Map _$IllustsSearchResultToJson( 21 | IllustsSearchResult instance) => 22 | { 23 | 'illusts': instance.illusts, 24 | 'next_url': instance.nextUrl, 25 | 'search_span_limit': instance.searchSpanLimit, 26 | }; 27 | -------------------------------------------------------------------------------- /lib/model_response/illusts/pixivision/spotlight_articles.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'spotlight_articles.g.dart'; 4 | 5 | @JsonSerializable() 6 | class SpotlightArticles extends Object { 7 | @JsonKey(name: 'spotlight_articles') 8 | List spotlightArticles; 9 | 10 | @JsonKey(name: 'next_url') 11 | String nextUrl; 12 | 13 | SpotlightArticles( 14 | this.spotlightArticles, 15 | this.nextUrl, 16 | ); 17 | 18 | factory SpotlightArticles.fromJson(Map srcJson) => _$SpotlightArticlesFromJson(srcJson); 19 | 20 | Map toJson() => _$SpotlightArticlesToJson(this); 21 | } 22 | 23 | @JsonSerializable() 24 | class SpotlightArticle extends Object { 25 | @JsonKey(name: 'id') 26 | int id; 27 | 28 | @JsonKey(name: 'title') 29 | String title; 30 | 31 | @JsonKey(name: 'pure_title') 32 | String pureTitle; 33 | 34 | @JsonKey(name: 'thumbnail') 35 | String thumbnail; 36 | 37 | @JsonKey(name: 'article_url') 38 | String articleUrl; 39 | 40 | @JsonKey(name: 'publish_date') 41 | String publishDate; 42 | 43 | @JsonKey(name: 'category') 44 | String category; 45 | 46 | @JsonKey(name: 'subcategory_label') 47 | String subcategoryLabel; 48 | 49 | SpotlightArticle( 50 | this.id, 51 | this.title, 52 | this.pureTitle, 53 | this.thumbnail, 54 | this.articleUrl, 55 | this.publishDate, 56 | this.category, 57 | this.subcategoryLabel, 58 | ); 59 | 60 | factory SpotlightArticle.fromJson(Map srcJson) => _$SpotlightArticleFromJson(srcJson); 61 | 62 | Map toJson() => _$SpotlightArticleToJson(this); 63 | } 64 | -------------------------------------------------------------------------------- /lib/model_response/illusts/recommended/illust_recommended.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: camel_case_types 2 | 3 | import 'package:json_annotation/json_annotation.dart'; 4 | import 'package:artvier/model_response/illusts/common_illust.dart'; 5 | 6 | part 'illust_recommended.g.dart'; 7 | 8 | 9 | @JsonSerializable() 10 | class IllustRecommended extends Object { 11 | 12 | @JsonKey(name: 'illusts') 13 | List illusts; 14 | 15 | @JsonKey(name: 'ranking_illusts') 16 | List rankingIllusts; 17 | 18 | @JsonKey(name: 'contest_exists') 19 | bool contestExists; 20 | 21 | @JsonKey(name: 'privacy_policy') 22 | Privacy_policy privacyPolicy; 23 | 24 | @JsonKey(name: 'next_url') 25 | String? nextUrl; 26 | 27 | IllustRecommended(this.illusts,this.rankingIllusts,this.contestExists,this.privacyPolicy,this.nextUrl,); 28 | 29 | factory IllustRecommended.fromJson(Map srcJson) => _$IllustRecommendedFromJson(srcJson); 30 | 31 | Map toJson() => _$IllustRecommendedToJson(this); 32 | 33 | } 34 | 35 | @JsonSerializable() 36 | class Privacy_policy extends Object { 37 | 38 | @JsonKey(name: 'version') 39 | String? version; 40 | 41 | @JsonKey(name: 'message') 42 | String? message; 43 | 44 | @JsonKey(name: 'url') 45 | String? url; 46 | 47 | Privacy_policy(this.version,this.message,this.url,); 48 | 49 | factory Privacy_policy.fromJson(Map srcJson) => _$Privacy_policyFromJson(srcJson); 50 | 51 | Map toJson() => _$Privacy_policyToJson(this); 52 | 53 | } 54 | 55 | 56 | -------------------------------------------------------------------------------- /lib/model_response/illusts/recommended/illust_recommended.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | // ignore_for_file: non_constant_identifier_names 4 | 5 | part of 'illust_recommended.dart'; 6 | 7 | // ************************************************************************** 8 | // JsonSerializableGenerator 9 | // ************************************************************************** 10 | 11 | IllustRecommended _$IllustRecommendedFromJson(Map json) => 12 | IllustRecommended( 13 | (json['illusts'] as List) 14 | .map((e) => CommonIllust.fromJson(e as Map)) 15 | .toList(), 16 | (json['ranking_illusts'] as List) 17 | .map((e) => CommonIllust.fromJson(e as Map)) 18 | .toList(), 19 | json['contest_exists'] as bool, 20 | Privacy_policy.fromJson(json['privacy_policy'] as Map), 21 | json['next_url'] as String?, 22 | ); 23 | 24 | Map _$IllustRecommendedToJson(IllustRecommended instance) => 25 | { 26 | 'illusts': instance.illusts, 27 | 'ranking_illusts': instance.rankingIllusts, 28 | 'contest_exists': instance.contestExists, 29 | 'privacy_policy': instance.privacyPolicy, 30 | 'next_url': instance.nextUrl, 31 | }; 32 | 33 | Privacy_policy _$Privacy_policyFromJson(Map json) => 34 | Privacy_policy( 35 | json['version'] as String?, 36 | json['message'] as String?, 37 | json['url'] as String?, 38 | ); 39 | 40 | Map _$Privacy_policyToJson(Privacy_policy instance) => 41 | { 42 | 'version': instance.version, 43 | 'message': instance.message, 44 | 'url': instance.url, 45 | }; 46 | -------------------------------------------------------------------------------- /lib/model_response/illusts/ugoira.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'ugoira.g.dart'; 4 | 5 | @JsonSerializable(explicitToJson: true) 6 | class UgoiraMetadataResult { 7 | @JsonKey(name: 'ugoira_metadata') 8 | UgoiraMetadata ugoiraMetadata; 9 | 10 | UgoiraMetadataResult(this.ugoiraMetadata); 11 | 12 | factory UgoiraMetadataResult.fromJson(Map json) => _$UgoiraMetadataResultFromJson(json); 13 | 14 | Map toJson() => _$UgoiraMetadataResultToJson(this); 15 | } 16 | 17 | @JsonSerializable(explicitToJson: true) 18 | class UgoiraMetadata { 19 | @JsonKey(name: 'zip_urls') 20 | ZipUrls zipUrls; 21 | List frames; 22 | 23 | UgoiraMetadata(this.zipUrls, this.frames); 24 | 25 | factory UgoiraMetadata.fromJson(Map json) => _$UgoiraMetadataFromJson(json); 26 | 27 | Map toJson() => _$UgoiraMetadataToJson(this); 28 | } 29 | 30 | @JsonSerializable(explicitToJson: true) 31 | class ZipUrls { 32 | String medium; 33 | 34 | ZipUrls(this.medium); 35 | 36 | factory ZipUrls.fromJson(Map json) => _$ZipUrlsFromJson(json); 37 | 38 | Map toJson() => _$ZipUrlsToJson(this); 39 | } 40 | 41 | @JsonSerializable(explicitToJson: true) 42 | class Frame { 43 | String file; 44 | int delay; 45 | 46 | Frame(this.file, this.delay); 47 | 48 | factory Frame.fromJson(Map json) => _$FrameFromJson(json); 49 | 50 | Map toJson() => _$FrameToJson(this); 51 | } 52 | -------------------------------------------------------------------------------- /lib/model_response/manga/recommended/manga_recommended.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: camel_case_types 2 | 3 | import 'package:json_annotation/json_annotation.dart'; 4 | import 'package:artvier/model_response/illusts/common_illust.dart'; 5 | 6 | part 'manga_recommended.g.dart'; 7 | 8 | @JsonSerializable() 9 | class MangaRecommended extends Object { 10 | @JsonKey(name: 'illusts') 11 | List illusts; 12 | 13 | @JsonKey(name: 'ranking_illusts') 14 | List rankingIllusts; 15 | 16 | @JsonKey(name: 'privacy_policy') 17 | Privacy_policy privacyPolicy; 18 | 19 | @JsonKey(name: 'next_url') 20 | String? nextUrl; 21 | 22 | MangaRecommended( 23 | this.illusts, 24 | this.rankingIllusts, 25 | this.privacyPolicy, 26 | this.nextUrl, 27 | ); 28 | 29 | factory MangaRecommended.fromJson(Map srcJson) => _$MangaRecommendedFromJson(srcJson); 30 | 31 | Map toJson() => _$MangaRecommendedToJson(this); 32 | } 33 | 34 | @JsonSerializable() 35 | class Privacy_policy extends Object { 36 | @JsonKey(name: 'version') 37 | String? version; 38 | 39 | @JsonKey(name: 'message') 40 | String? message; 41 | 42 | @JsonKey(name: 'url') 43 | String? url; 44 | 45 | Privacy_policy( 46 | this.version, 47 | this.message, 48 | this.url, 49 | ); 50 | 51 | factory Privacy_policy.fromJson(Map srcJson) => _$Privacy_policyFromJson(srcJson); 52 | 53 | Map toJson() => _$Privacy_policyToJson(this); 54 | } 55 | -------------------------------------------------------------------------------- /lib/model_response/manga/recommended/manga_recommended.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | // ignore_for_file: non_constant_identifier_names 4 | 5 | part of 'manga_recommended.dart'; 6 | 7 | // ************************************************************************** 8 | // JsonSerializableGenerator 9 | // ************************************************************************** 10 | 11 | MangaRecommended _$MangaRecommendedFromJson(Map json) => 12 | MangaRecommended( 13 | (json['illusts'] as List) 14 | .map((e) => CommonIllust.fromJson(e as Map)) 15 | .toList(), 16 | (json['ranking_illusts'] as List) 17 | .map((e) => CommonIllust.fromJson(e as Map)) 18 | .toList(), 19 | Privacy_policy.fromJson(json['privacy_policy'] as Map), 20 | json['next_url'] as String?, 21 | ); 22 | 23 | Map _$MangaRecommendedToJson(MangaRecommended instance) => 24 | { 25 | 'illusts': instance.illusts, 26 | 'ranking_illusts': instance.rankingIllusts, 27 | 'privacy_policy': instance.privacyPolicy, 28 | 'next_url': instance.nextUrl, 29 | }; 30 | 31 | Privacy_policy _$Privacy_policyFromJson(Map json) => 32 | Privacy_policy( 33 | json['version'] as String?, 34 | json['message'] as String?, 35 | json['url'] as String?, 36 | ); 37 | 38 | Map _$Privacy_policyToJson(Privacy_policy instance) => 39 | { 40 | 'version': instance.version, 41 | 'message': instance.message, 42 | 'url': instance.url, 43 | }; 44 | -------------------------------------------------------------------------------- /lib/model_response/novels/common_novel_list.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | import 'package:artvier/model_response/novels/common_novel.dart'; 3 | 4 | part 'common_novel_list.g.dart'; 5 | 6 | 7 | @JsonSerializable() 8 | class CommonNovelList extends Object { 9 | 10 | @JsonKey(name: 'novels') 11 | List novels; 12 | 13 | @JsonKey(name: 'next_url') 14 | String? nextUrl; 15 | 16 | CommonNovelList(this.novels, this.nextUrl); 17 | 18 | factory CommonNovelList.fromJson(Map srcJson) => _$CommonNovelListFromJson(srcJson); 19 | 20 | Map toJson() => _$CommonNovelListToJson(this); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /lib/model_response/novels/common_novel_list.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | // ignore_for_file: non_constant_identifier_names 4 | 5 | part of 'common_novel_list.dart'; 6 | 7 | // ************************************************************************** 8 | // JsonSerializableGenerator 9 | // ************************************************************************** 10 | 11 | CommonNovelList _$CommonNovelListFromJson(Map json) => 12 | CommonNovelList( 13 | (json['novels'] as List) 14 | .map((e) => CommonNovel.fromJson(e as Map)) 15 | .toList(), 16 | json['next_url'] as String?, 17 | ); 18 | 19 | Map _$CommonNovelListToJson(CommonNovelList instance) => 20 | { 21 | 'novels': instance.novels, 22 | 'next_url': instance.nextUrl, 23 | }; 24 | -------------------------------------------------------------------------------- /lib/model_response/novels/novel_detail.dart: -------------------------------------------------------------------------------- 1 | import 'package:artvier/model_response/novels/common_novel.dart'; 2 | import 'package:json_annotation/json_annotation.dart'; 3 | 4 | part 'novel_detail.g.dart'; 5 | 6 | @JsonSerializable() 7 | class NovelDetail extends Object { 8 | @JsonKey(name: 'novel') 9 | CommonNovel novel; 10 | 11 | NovelDetail(this.novel); 12 | 13 | factory NovelDetail.fromJson(Map srcJson) => _$NovelDetailFromJson(srcJson); 14 | 15 | Map toJson() => _$NovelDetailToJson(this); 16 | } 17 | -------------------------------------------------------------------------------- /lib/model_response/novels/novel_detail.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | // ignore_for_file: non_constant_identifier_names 4 | 5 | part of 'novel_detail.dart'; 6 | 7 | // ************************************************************************** 8 | // JsonSerializableGenerator 9 | // ************************************************************************** 10 | 11 | NovelDetail _$NovelDetailFromJson(Map json) => NovelDetail( 12 | CommonNovel.fromJson(json['novel'] as Map), 13 | ); 14 | 15 | Map _$NovelDetailToJson(NovelDetail instance) => 16 | { 17 | 'novel': instance.novel, 18 | }; 19 | -------------------------------------------------------------------------------- /lib/model_response/novels/novels_recommended.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: camel_case_types 2 | 3 | import 'package:json_annotation/json_annotation.dart'; 4 | import 'package:artvier/model_response/novels/common_novel.dart'; 5 | 6 | part 'novels_recommended.g.dart'; 7 | 8 | 9 | @JsonSerializable() 10 | class Novels_recommended extends Object { 11 | 12 | @JsonKey(name: 'novels') 13 | List novels; 14 | 15 | @JsonKey(name: 'ranking_novels') 16 | List rankingNovels; 17 | 18 | @JsonKey(name: 'privacy_policy') 19 | Privacy_policy privacyPolicy; 20 | 21 | @JsonKey(name: 'next_url') 22 | String? nextUrl; 23 | 24 | Novels_recommended(this.novels,this.rankingNovels,this.privacyPolicy,this.nextUrl,); 25 | 26 | factory Novels_recommended.fromJson(Map srcJson) => _$Novels_recommendedFromJson(srcJson); 27 | 28 | Map toJson() => _$Novels_recommendedToJson(this); 29 | 30 | } 31 | 32 | @JsonSerializable() 33 | class Privacy_policy extends Object { 34 | 35 | @JsonKey(name: 'version') 36 | String version; 37 | 38 | @JsonKey(name: 'message') 39 | String message; 40 | 41 | @JsonKey(name: 'url') 42 | String url; 43 | 44 | Privacy_policy(this.version,this.message,this.url,); 45 | 46 | factory Privacy_policy.fromJson(Map srcJson) => _$Privacy_policyFromJson(srcJson); 47 | 48 | Map toJson() => _$Privacy_policyToJson(this); 49 | 50 | } -------------------------------------------------------------------------------- /lib/model_response/novels/novels_recommended.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | // ignore_for_file: non_constant_identifier_names 4 | 5 | part of 'novels_recommended.dart'; 6 | 7 | // ************************************************************************** 8 | // JsonSerializableGenerator 9 | // ************************************************************************** 10 | 11 | Novels_recommended _$Novels_recommendedFromJson(Map json) => 12 | Novels_recommended( 13 | (json['novels'] as List) 14 | .map((e) => CommonNovel.fromJson(e as Map)) 15 | .toList(), 16 | (json['ranking_novels'] as List) 17 | .map((e) => CommonNovel.fromJson(e as Map)) 18 | .toList(), 19 | Privacy_policy.fromJson(json['privacy_policy'] as Map), 20 | json['next_url'] as String?, 21 | ); 22 | 23 | Map _$Novels_recommendedToJson(Novels_recommended instance) => 24 | { 25 | 'novels': instance.novels, 26 | 'ranking_novels': instance.rankingNovels, 27 | 'privacy_policy': instance.privacyPolicy, 28 | 'next_url': instance.nextUrl, 29 | }; 30 | 31 | Privacy_policy _$Privacy_policyFromJson(Map json) => 32 | Privacy_policy( 33 | json['version'] as String, 34 | json['message'] as String, 35 | json['url'] as String, 36 | ); 37 | 38 | Map _$Privacy_policyToJson(Privacy_policy instance) => 39 | { 40 | 'version': instance.version, 41 | 'message': instance.message, 42 | 'url': instance.url, 43 | }; 44 | -------------------------------------------------------------------------------- /lib/model_response/novels/recommended/novels_recommended.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: camel_case_types 2 | 3 | import 'package:artvier/model_response/novels/common_novel.dart'; 4 | import 'package:json_annotation/json_annotation.dart'; 5 | 6 | part 'novels_recommended.g.dart'; 7 | 8 | @JsonSerializable() 9 | class NovelsRecommended extends Object { 10 | @JsonKey(name: 'novels') 11 | List novels; 12 | 13 | @JsonKey(name: 'ranking_novels') 14 | List rankingNovels; 15 | 16 | @JsonKey(name: 'privacy_policy') 17 | Privacy_policy privacyPolicy; 18 | 19 | @JsonKey(name: 'next_url') 20 | String? nextUrl; 21 | 22 | NovelsRecommended( 23 | this.novels, 24 | this.rankingNovels, 25 | this.privacyPolicy, 26 | this.nextUrl, 27 | ); 28 | 29 | factory NovelsRecommended.fromJson(Map srcJson) => _$NovelsRecommendedFromJson(srcJson); 30 | 31 | Map toJson() => _$NovelsRecommendedToJson(this); 32 | } 33 | 34 | @JsonSerializable() 35 | class Privacy_policy extends Object { 36 | @JsonKey(name: 'version') 37 | String? version; 38 | 39 | @JsonKey(name: 'message') 40 | String? message; 41 | 42 | @JsonKey(name: 'url') 43 | String? url; 44 | 45 | Privacy_policy( 46 | this.version, 47 | this.message, 48 | this.url, 49 | ); 50 | 51 | factory Privacy_policy.fromJson(Map srcJson) => _$Privacy_policyFromJson(srcJson); 52 | 53 | Map toJson() => _$Privacy_policyToJson(this); 54 | } 55 | -------------------------------------------------------------------------------- /lib/model_response/novels/recommended/novels_recommended.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | // ignore_for_file: non_constant_identifier_names 4 | 5 | part of 'novels_recommended.dart'; 6 | 7 | // ************************************************************************** 8 | // JsonSerializableGenerator 9 | // ************************************************************************** 10 | 11 | NovelsRecommended _$NovelsRecommendedFromJson(Map json) => 12 | NovelsRecommended( 13 | (json['novels'] as List) 14 | .map((e) => CommonNovel.fromJson(e as Map)) 15 | .toList(), 16 | (json['ranking_novels'] as List) 17 | .map((e) => CommonNovel.fromJson(e as Map)) 18 | .toList(), 19 | Privacy_policy.fromJson(json['privacy_policy'] as Map), 20 | json['next_url'] as String?, 21 | ); 22 | 23 | Map _$NovelsRecommendedToJson(NovelsRecommended instance) => 24 | { 25 | 'novels': instance.novels, 26 | 'ranking_novels': instance.rankingNovels, 27 | 'privacy_policy': instance.privacyPolicy, 28 | 'next_url': instance.nextUrl, 29 | }; 30 | 31 | Privacy_policy _$Privacy_policyFromJson(Map json) => 32 | Privacy_policy( 33 | json['version'] as String?, 34 | json['message'] as String?, 35 | json['url'] as String?, 36 | ); 37 | 38 | Map _$Privacy_policyToJson(Privacy_policy instance) => 39 | { 40 | 'version': instance.version, 41 | 'message': instance.message, 42 | 'url': instance.url, 43 | }; 44 | -------------------------------------------------------------------------------- /lib/model_response/preview_artworks_urls.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * 全屏预览图片时,所用的图片信息(包含链接和页码) 3 | * */ 4 | import 'package:json_annotation/json_annotation.dart'; 5 | 6 | part 'preview_artworks_urls.g.dart'; 7 | 8 | @JsonSerializable() 9 | class PreviewArtworksUrls extends Object { 10 | 11 | @JsonKey(name: 'square_medium') 12 | String squareMedium; 13 | 14 | @JsonKey(name: 'medium') 15 | String medium; 16 | 17 | @JsonKey(name: 'large') 18 | String large; 19 | 20 | @JsonKey(name: 'original') 21 | String? original; 22 | 23 | PreviewArtworksUrls(this.squareMedium,this.medium,this.large,this.original); 24 | 25 | PreviewArtworksUrls.create({required this.squareMedium,required this.medium,required this.large,required this.original}); 26 | 27 | factory PreviewArtworksUrls.fromJson(Map srcJson) => _$PreviewArtworksUrlsFromJson(srcJson); 28 | 29 | Map toJson() => _$PreviewArtworksUrlsToJson(this); 30 | 31 | } 32 | -------------------------------------------------------------------------------- /lib/model_response/preview_artworks_urls.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | // ignore_for_file: non_constant_identifier_names 4 | 5 | part of 'preview_artworks_urls.dart'; 6 | 7 | // ************************************************************************** 8 | // JsonSerializableGenerator 9 | // ************************************************************************** 10 | 11 | PreviewArtworksUrls _$PreviewArtworksUrlsFromJson(Map json) => 12 | PreviewArtworksUrls( 13 | json['square_medium'] as String, 14 | json['medium'] as String, 15 | json['large'] as String, 16 | json['original'] as String?, 17 | ); 18 | 19 | Map _$PreviewArtworksUrlsToJson( 20 | PreviewArtworksUrls instance) => 21 | { 22 | 'square_medium': instance.squareMedium, 23 | 'medium': instance.medium, 24 | 'large': instance.large, 25 | 'original': instance.original, 26 | }; 27 | -------------------------------------------------------------------------------- /lib/model_response/user/bookmark/bookmark_tag.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'bookmark_tag.g.dart'; 4 | 5 | @JsonSerializable(explicitToJson: true) 6 | class BookmarkTag { 7 | @JsonKey(name: 'name') 8 | String? name; 9 | 10 | @JsonKey(name: 'count') 11 | int? count; 12 | 13 | BookmarkTag({this.name, this.count}); 14 | 15 | static BookmarkTag fromJson(Map srcJson) => _$BookmarkTagFromJson(srcJson); 16 | 17 | Map toJson() => _$BookmarkTagToJson(this); 18 | } 19 | -------------------------------------------------------------------------------- /lib/model_response/user/bookmark/bookmark_tag.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | // ignore_for_file: non_constant_identifier_names 4 | 5 | part of 'bookmark_tag.dart'; 6 | 7 | // ************************************************************************** 8 | // JsonSerializableGenerator 9 | // ************************************************************************** 10 | 11 | BookmarkTag _$BookmarkTagFromJson(Map json) => BookmarkTag( 12 | name: json['name'] as String?, 13 | count: (json['count'] as num?)?.toInt(), 14 | ); 15 | 16 | Map _$BookmarkTagToJson(BookmarkTag instance) => 17 | { 18 | 'name': instance.name, 19 | 'count': instance.count, 20 | }; 21 | -------------------------------------------------------------------------------- /lib/model_response/user/bookmark/bookmark_tag_list.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | import 'package:artvier/model_response/user/bookmark/bookmark_tag.dart'; 3 | 4 | part 'bookmark_tag_list.g.dart'; 5 | 6 | @JsonSerializable(explicitToJson: true) 7 | class BookmarkTagList{ 8 | 9 | @JsonKey(name: 'bookmark_tags') 10 | List? bookmarkTags; 11 | 12 | @JsonKey(name: 'next_url') 13 | String? nextUrl; 14 | 15 | BookmarkTagList(this.bookmarkTags, this.nextUrl); 16 | 17 | static BookmarkTagList fromJson(Map srcJson) => _$BookmarkTagListFromJson(srcJson); 18 | 19 | Map toJson() => _$BookmarkTagListToJson(this); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /lib/model_response/user/bookmark/bookmark_tag_list.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | // ignore_for_file: non_constant_identifier_names 4 | 5 | part of 'bookmark_tag_list.dart'; 6 | 7 | // ************************************************************************** 8 | // JsonSerializableGenerator 9 | // ************************************************************************** 10 | 11 | BookmarkTagList _$BookmarkTagListFromJson(Map json) => 12 | BookmarkTagList( 13 | (json['bookmark_tags'] as List?) 14 | ?.map((e) => BookmarkTag.fromJson(e as Map)) 15 | .toList(), 16 | json['next_url'] as String?, 17 | ); 18 | 19 | Map _$BookmarkTagListToJson(BookmarkTagList instance) => 20 | { 21 | 'bookmark_tags': instance.bookmarkTags?.map((e) => e.toJson()).toList(), 22 | 'next_url': instance.nextUrl, 23 | }; 24 | -------------------------------------------------------------------------------- /lib/model_response/user/common_user.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: camel_case_types 2 | 3 | import 'package:json_annotation/json_annotation.dart'; 4 | 5 | part 'common_user.g.dart'; 6 | 7 | 8 | @JsonSerializable() 9 | class CommonUser extends Object { 10 | 11 | @JsonKey(name: 'id') 12 | int id; 13 | 14 | @JsonKey(name: 'name') 15 | String name; 16 | 17 | @JsonKey(name: 'account') 18 | String account; 19 | 20 | @JsonKey(name: 'profile_image_urls') 21 | Profile_image_urls profileImageUrls; 22 | 23 | @JsonKey(name: 'comment') 24 | /// 只在用户详情中存在该字段 25 | String? comment; 26 | 27 | /// 已删除或不公开的图片会不存在此字段 28 | @JsonKey(name: 'is_followed') 29 | bool? isFollowed; 30 | 31 | CommonUser(this.id,this.name,this.account,this.profileImageUrls,this.comment,this.isFollowed,); 32 | 33 | factory CommonUser.fromJson(Map srcJson) => _$CommonUserFromJson(srcJson); 34 | 35 | Map toJson() => _$CommonUserToJson(this); 36 | 37 | } 38 | 39 | @JsonSerializable() 40 | class Profile_image_urls extends Object { 41 | 42 | @JsonKey(name: 'medium') 43 | String medium; 44 | 45 | Profile_image_urls(this.medium,); 46 | 47 | factory Profile_image_urls.fromJson(Map srcJson) => _$Profile_image_urlsFromJson(srcJson); 48 | 49 | Map toJson() => _$Profile_image_urlsToJson(this); 50 | 51 | } 52 | -------------------------------------------------------------------------------- /lib/model_response/user/common_user.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | // ignore_for_file: non_constant_identifier_names 4 | 5 | part of 'common_user.dart'; 6 | 7 | // ************************************************************************** 8 | // JsonSerializableGenerator 9 | // ************************************************************************** 10 | 11 | CommonUser _$CommonUserFromJson(Map json) => CommonUser( 12 | (json['id'] as num).toInt(), 13 | json['name'] as String, 14 | json['account'] as String, 15 | Profile_image_urls.fromJson( 16 | json['profile_image_urls'] as Map), 17 | json['comment'] as String?, 18 | json['is_followed'] as bool?, 19 | ); 20 | 21 | Map _$CommonUserToJson(CommonUser instance) => 22 | { 23 | 'id': instance.id, 24 | 'name': instance.name, 25 | 'account': instance.account, 26 | 'profile_image_urls': instance.profileImageUrls, 27 | 'comment': instance.comment, 28 | 'is_followed': instance.isFollowed, 29 | }; 30 | 31 | Profile_image_urls _$Profile_image_urlsFromJson(Map json) => 32 | Profile_image_urls( 33 | json['medium'] as String, 34 | ); 35 | 36 | Map _$Profile_image_urlsToJson(Profile_image_urls instance) => 37 | { 38 | 'medium': instance.medium, 39 | }; 40 | -------------------------------------------------------------------------------- /lib/model_response/user/common_user_previews.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | import 'package:artvier/model_response/illusts/common_illust.dart'; 3 | import 'package:artvier/model_response/novels/common_novel.dart'; 4 | import 'package:artvier/model_response/user/common_user.dart'; 5 | 6 | part 'common_user_previews.g.dart'; 7 | 8 | @JsonSerializable() 9 | class CommonUserPreviews extends Object { 10 | 11 | @JsonKey(name: 'user') 12 | CommonUser user; 13 | 14 | @JsonKey(name: 'illusts') 15 | List illusts; 16 | 17 | @JsonKey(name: 'novels') 18 | List novels; 19 | 20 | @JsonKey(name: 'is_muted') 21 | bool isMuted; 22 | 23 | CommonUserPreviews(this.user,this.illusts,this.novels,this.isMuted,); 24 | 25 | factory CommonUserPreviews.fromJson(Map srcJson) => _$CommonUserPreviewsFromJson(srcJson); 26 | 27 | Map toJson() => _$CommonUserPreviewsToJson(this); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /lib/model_response/user/common_user_previews.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | // ignore_for_file: non_constant_identifier_names 4 | 5 | part of 'common_user_previews.dart'; 6 | 7 | // ************************************************************************** 8 | // JsonSerializableGenerator 9 | // ************************************************************************** 10 | 11 | CommonUserPreviews _$CommonUserPreviewsFromJson(Map json) => 12 | CommonUserPreviews( 13 | CommonUser.fromJson(json['user'] as Map), 14 | (json['illusts'] as List) 15 | .map((e) => CommonIllust.fromJson(e as Map)) 16 | .toList(), 17 | (json['novels'] as List) 18 | .map((e) => CommonNovel.fromJson(e as Map)) 19 | .toList(), 20 | json['is_muted'] as bool, 21 | ); 22 | 23 | Map _$CommonUserPreviewsToJson(CommonUserPreviews instance) => 24 | { 25 | 'user': instance.user, 26 | 'illusts': instance.illusts, 27 | 'novels': instance.novels, 28 | 'is_muted': instance.isMuted, 29 | }; 30 | -------------------------------------------------------------------------------- /lib/model_response/user/common_user_previews_list.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | import 'package:artvier/model_response/user/common_user_previews.dart'; 3 | 4 | part 'common_user_previews_list.g.dart'; 5 | 6 | 7 | @JsonSerializable() 8 | /// 用户列表(含有预览作品) 9 | class CommonUserPreviewsList extends Object { 10 | 11 | @JsonKey(name: 'user_previews') 12 | List users; 13 | 14 | @JsonKey(name: 'next_url') 15 | String? nextUrl; 16 | 17 | CommonUserPreviewsList(this.users, this.nextUrl); 18 | 19 | factory CommonUserPreviewsList.fromJson(Map srcJson) => _$CommonUserPreviewsListFromJson(srcJson); 20 | 21 | Map toJson() => _$CommonUserPreviewsListToJson(this); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /lib/model_response/user/common_user_previews_list.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | // ignore_for_file: non_constant_identifier_names 4 | 5 | part of 'common_user_previews_list.dart'; 6 | 7 | // ************************************************************************** 8 | // JsonSerializableGenerator 9 | // ************************************************************************** 10 | 11 | CommonUserPreviewsList _$CommonUserPreviewsListFromJson( 12 | Map json) => 13 | CommonUserPreviewsList( 14 | (json['user_previews'] as List) 15 | .map((e) => CommonUserPreviews.fromJson(e as Map)) 16 | .toList(), 17 | json['next_url'] as String?, 18 | ); 19 | 20 | Map _$CommonUserPreviewsListToJson( 21 | CommonUserPreviewsList instance) => 22 | { 23 | 'user_previews': instance.users, 24 | 'next_url': instance.nextUrl, 25 | }; 26 | -------------------------------------------------------------------------------- /lib/model_response/user/follow/folowing_detail.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'folowing_detail.g.dart'; 4 | 5 | @JsonSerializable(explicitToJson: true) 6 | class TheFollowingDetail{ 7 | 8 | @JsonKey(name: 'follow_detail') 9 | UserFollowingDetail? detail; 10 | 11 | TheFollowingDetail(); 12 | 13 | static TheFollowingDetail fromJson(Map srcJson) => _$TheFollowingDetailFromJson(srcJson); 14 | 15 | Map toJson() => _$TheFollowingDetailToJson(this); 16 | 17 | } 18 | 19 | 20 | @JsonSerializable(explicitToJson: true) 21 | class UserFollowingDetail{ 22 | 23 | @JsonKey(name: 'is_followed') 24 | bool? isFollowed; 25 | 26 | @JsonKey(name: 'restrict') 27 | String? restrict; 28 | 29 | UserFollowingDetail({this.isFollowed,this.restrict}); 30 | 31 | static UserFollowingDetail fromJson(Map srcJson) => _$UserFollowingDetailFromJson(srcJson); 32 | 33 | Map toJson() => _$UserFollowingDetailToJson(this); 34 | 35 | } -------------------------------------------------------------------------------- /lib/model_response/user/follow/folowing_detail.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | // ignore_for_file: non_constant_identifier_names 4 | 5 | part of 'folowing_detail.dart'; 6 | 7 | // ************************************************************************** 8 | // JsonSerializableGenerator 9 | // ************************************************************************** 10 | 11 | TheFollowingDetail _$TheFollowingDetailFromJson(Map json) => 12 | TheFollowingDetail() 13 | ..detail = json['follow_detail'] == null 14 | ? null 15 | : UserFollowingDetail.fromJson( 16 | json['follow_detail'] as Map); 17 | 18 | Map _$TheFollowingDetailToJson(TheFollowingDetail instance) => 19 | { 20 | 'follow_detail': instance.detail?.toJson(), 21 | }; 22 | 23 | UserFollowingDetail _$UserFollowingDetailFromJson(Map json) => 24 | UserFollowingDetail( 25 | isFollowed: json['is_followed'] as bool?, 26 | restrict: json['restrict'] as String?, 27 | ); 28 | 29 | Map _$UserFollowingDetailToJson( 30 | UserFollowingDetail instance) => 31 | { 32 | 'is_followed': instance.isFollowed, 33 | 'restrict': instance.restrict, 34 | }; 35 | -------------------------------------------------------------------------------- /lib/model_response/user/preload_user_least_info.dart: -------------------------------------------------------------------------------- 1 | /// 2 | /// 用户的最小信息,用于跳转新页面的预加载信息 3 | class PreloadUserLeastInfo extends Object { 4 | String id; 5 | 6 | String name; 7 | 8 | String avatar; 9 | 10 | PreloadUserLeastInfo(this.id,this.name,this.avatar,); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /lib/model_response/user/user_previews_list.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | import 'package:artvier/model_response/user/common_user_previews.dart'; 3 | 4 | part 'user_previews_list.g.dart'; 5 | 6 | 7 | @JsonSerializable() 8 | class UserPreviewsList extends Object { 9 | 10 | @JsonKey(name: 'user_previews') 11 | List userPreviews; 12 | 13 | @JsonKey(name: 'next_url') 14 | String? nextUrl; 15 | 16 | UserPreviewsList(this.userPreviews,this.nextUrl,); 17 | 18 | factory UserPreviewsList.fromJson(Map srcJson) => _$UserPreviewsListFromJson(srcJson); 19 | 20 | Map toJson() => _$UserPreviewsListToJson(this); 21 | 22 | } 23 | 24 | -------------------------------------------------------------------------------- /lib/model_response/user/user_previews_list.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | // ignore_for_file: non_constant_identifier_names 4 | 5 | part of 'user_previews_list.dart'; 6 | 7 | // ************************************************************************** 8 | // JsonSerializableGenerator 9 | // ************************************************************************** 10 | 11 | UserPreviewsList _$UserPreviewsListFromJson(Map json) => 12 | UserPreviewsList( 13 | (json['user_previews'] as List) 14 | .map((e) => CommonUserPreviews.fromJson(e as Map)) 15 | .toList(), 16 | json['next_url'] as String?, 17 | ); 18 | 19 | Map _$UserPreviewsListToJson(UserPreviewsList instance) => 20 | { 21 | 'user_previews': instance.userPreviews, 22 | 'next_url': instance.nextUrl, 23 | }; 24 | -------------------------------------------------------------------------------- /lib/pages/artwork/detail/arguments/illust_detail_page_args.dart: -------------------------------------------------------------------------------- 1 | import 'package:artvier/model_response/illusts/common_illust.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | 4 | part 'illust_detail_page_args.freezed.dart'; 5 | 6 | /// 插画详情页面的传递参数 7 | @Freezed( 8 | copyWith: true 9 | ) 10 | class IllustDetailPageArguments with _$IllustDetailPageArguments { 11 | const factory IllustDetailPageArguments({ 12 | required String illustId, 13 | String? title, 14 | CommonIllust? detail, 15 | }) = _IllustDetailPageArguments; 16 | } 17 | -------------------------------------------------------------------------------- /lib/pages/artwork/detail/provider/illust_comment_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:artvier/global/provider/requester_provider.dart'; 2 | import 'package:dio/dio.dart'; 3 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 4 | import 'package:artvier/api_app/api_illusts.dart'; 5 | import 'package:artvier/model_response/illusts/illust_comments.dart'; 6 | 7 | /// 请求获取的预览版评论列表 8 | final commentsPreviewProvider = FutureProvider.autoDispose.family, String>((ref, illustId) async { 9 | final cancelToken = CancelToken(); 10 | ref.onDispose(() => cancelToken.cancel()); 11 | final response = 12 | await ApiIllusts(ref.read(httpRequesterProvider)).getIllustComments(illustId, cancelToken: cancelToken); 13 | return response.comments; 14 | }); 15 | -------------------------------------------------------------------------------- /lib/pages/artwork/detail/provider/illust_detail_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:artvier/global/provider/requester_provider.dart'; 2 | import 'package:artvier/model_response/illusts/illust_detail.dart'; 3 | import 'package:dio/dio.dart'; 4 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 5 | import 'package:artvier/api_app/api_illusts.dart'; 6 | 7 | /// 插画详细信息 8 | /// - 若页面已经传递了详细信息,则不会通过这里获取 9 | final illustDetailProvider = FutureProvider.autoDispose.family((ref, illustId) async { 10 | final cancelToken = CancelToken(); 11 | ref.onDispose(() => cancelToken.cancel()); 12 | var res = await ApiIllusts(ref.read(httpRequesterProvider)).illustDetail(illustId); 13 | return res; 14 | }); 15 | -------------------------------------------------------------------------------- /lib/pages/artwork/detail/provider/illust_related_provider.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:artvier/api_app/api_illusts.dart'; 4 | import 'package:artvier/base/base_provider/base_notifier.dart'; 5 | import 'package:artvier/base/base_provider/illust_list_notifier.dart'; 6 | import 'package:artvier/model_response/illusts/common_illust.dart'; 7 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 8 | 9 | /// 相关作品 10 | final illustRelatedProvider = 11 | AutoDisposeAsyncNotifierProvider.family, String>(() { 12 | return RelatedArtworksNotifier(); 13 | }); 14 | 15 | // 相关作品 16 | class RelatedArtworksNotifier extends BaseAutoDisposeFamilyAsyncNotifier, String> 17 | with IllustListAsyncNotifierMixin { 18 | String get worksId => arg; 19 | 20 | @override 21 | FutureOr> build(String arg) async { 22 | handleCancel(ref); 23 | handleCollectState(ref); 24 | return fetch(); 25 | } 26 | 27 | /// 初始化数据 28 | @override 29 | Future> fetch() async { 30 | var result = await ApiIllusts(requester).getRelatedIllust(worksId, cancelToken: cancelToken); 31 | nextUrl = result.nextUrl; 32 | return result.illusts; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/pages/artwork/detail/widgets/related_artworks_content.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | import 'package:artvier/business_component/listview/illust_listview/illust_waterfall_gridview.dart'; 4 | import 'package:artvier/component/loading/request_loading.dart'; 5 | import 'package:artvier/pages/artwork/detail/provider/illust_related_provider.dart'; 6 | 7 | /// 相关作品(插画漫画) 8 | class RelatedArtworksContentWidget extends ConsumerWidget { 9 | const RelatedArtworksContentWidget({ 10 | super.key, 11 | required this.worksId, 12 | }); 13 | 14 | final String worksId; 15 | 16 | @override 17 | Widget build(BuildContext context, WidgetRef ref) { 18 | return Consumer( 19 | builder: (_, ref, __) => ref.watch(illustRelatedProvider(worksId)).when( 20 | data: (data) => SliverIllustWaterfallGridView( 21 | artworkList: data, 22 | onLazyload: () async => ref.read(illustRelatedProvider(worksId).notifier).next(), 23 | ), 24 | error: (_, __) => SliverToBoxAdapter( 25 | child: RequestLoadingFailed( 26 | onRetry: () async => ref.read(illustRelatedProvider(worksId).notifier).reload(), 27 | ), 28 | ), 29 | loading: () => const SliverToBoxAdapter(child: RequestLoading()), 30 | ), 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/pages/artwork/download_manage/provider/download_manage_provider.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:artvier/storage/downloads/downloads_db.dart'; 4 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 5 | 6 | /// TODO: 后续不再放全局,且不用的时候要 close 释放资源 7 | final downloadsDatabase = DownloadsDatabase(); 8 | 9 | ///下载队列的状态管理 10 | final downloadTaskQueueProvider = 11 | AutoDisposeAsyncNotifierProvider>(DownloadsNotifier.new); 12 | 13 | class DownloadsNotifier extends AutoDisposeAsyncNotifier> { 14 | @override 15 | FutureOr> build() { 16 | return downloadsDatabase.allDownloadTasks(); 17 | } 18 | 19 | /// 更新状态 20 | updateState(List newState) { 21 | state = AsyncValue.data(newState); 22 | } 23 | 24 | /// 重新加载状态 25 | reloadState() { 26 | downloadsDatabase.allDownloadTasks().then((value) { 27 | state = AsyncValue.data(value); 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/pages/artwork/history/logic.dart: -------------------------------------------------------------------------------- 1 | import 'package:artvier/global/logger.dart'; 2 | import 'package:artvier/pages/artwork/history/provider/history_provider.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 5 | import 'package:fluttertoast/fluttertoast.dart'; 6 | 7 | mixin HistoryPageLogic { 8 | WidgetRef get ref; 9 | 10 | /// 清空历史记录 11 | void handleClearHistory() { 12 | ref 13 | .read(viewingHistoryProvider.notifier) 14 | .clearAll() 15 | .then((value) => Fluttertoast.showToast(msg: "清空成功")) 16 | .catchError((err, _) { 17 | Fluttertoast.showToast(msg: "操作错误"); 18 | logger.e(err); 19 | return null; 20 | }).whenComplete(() => Navigator.of(ref.context).pop()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/pages/artwork/history/provider/history_provider.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:artvier/storage/viewing_history/viewing_history_db.dart'; 4 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 5 | 6 | /// 浏览历史的状态管理 7 | final viewingHistoryProvider = 8 | AutoDisposeAsyncNotifierProvider>(DownloadsNotifier.new); 9 | 10 | class DownloadsNotifier extends AutoDisposeAsyncNotifier> { 11 | int page = 1; 12 | int pageSize = 1000; 13 | int count = 0; 14 | 15 | @override 16 | FutureOr> build() async { 17 | count = await viewingHistoryDatabase.countAll(); 18 | var value = await viewingHistoryDatabase.getList(page: page, pageSize: pageSize); 19 | return value; 20 | } 21 | 22 | /// 更新状态 23 | updateState(List newState) { 24 | state = AsyncValue.data(newState); 25 | } 26 | 27 | /// 清空历史记录 28 | Future> clearAll() async { 29 | // 旧表设计失误,以移除数据库作为清空 30 | await viewingHistoryDatabase.close(); 31 | await viewingHistoryDatabase.deleteDatabase(); 32 | state = const AsyncValue.data([]); 33 | viewingHistoryDatabase = ViewingHistoryDatabase(); 34 | return []; 35 | } 36 | 37 | /// 重新加载状态 38 | reloadState() { 39 | return viewingHistoryDatabase.getList().then((value) { 40 | state = AsyncValue.data(value); 41 | }); 42 | } 43 | 44 | /// 下一页 45 | Future nextPage() async { 46 | var value = await viewingHistoryDatabase.getList(page: page, pageSize: pageSize); 47 | update((p0) => value); 48 | return false; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/pages/artwork/pixivision/logic.dart: -------------------------------------------------------------------------------- 1 | import 'package:artvier/pages/artwork/pixivision/model/pixivision_webview_page_arguments.dart'; 2 | import 'package:artvier/pages/artwork/pixivision/provider/illust_pixivision_provider.dart'; 3 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 4 | 5 | mixin IllustPixivisionPageLogic { 6 | WidgetRef get ref; 7 | 8 | /// Language 9 | late String language; 10 | 11 | /// Pixvision ID 12 | late int pixvisionId; 13 | 14 | late final illustPixivisionProvier = 15 | AsyncNotifierProvider.autoDispose.family( 16 | () => IllustPixivisionNotifier(), 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /lib/pages/artwork/pixivision/model/pixivision_body_illust_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'pixivision_body_illust_item.freezed.dart'; 4 | 5 | /// Pixivision Webview 页的传递参数 6 | @Freezed( 7 | copyWith: true, 8 | ) 9 | class PixivisionBodyIllustItem with _$PixivisionBodyIllustItem { 10 | const factory PixivisionBodyIllustItem({ 11 | /// 作品名 12 | required String illustTitle, 13 | 14 | /// 作品ID 15 | required String illustId, 16 | 17 | /// 图片地址 18 | required String illustUrl, 19 | 20 | /// 作者名 21 | required String authorName, 22 | 23 | /// 作者头像 24 | required String authorAvatar, 25 | 26 | /// 作者Id 27 | required String authorId, 28 | 29 | /// 作品描述 30 | required List? description, 31 | }) = _PixivisionBodyIllustItem; 32 | } 33 | -------------------------------------------------------------------------------- /lib/pages/artwork/pixivision/model/pixivision_webview_page_arguments.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'pixivision_webview_page_arguments.freezed.dart'; 4 | 5 | /// Pixivision Webview 页的传递参数 6 | @Freezed( 7 | copyWith: true, 8 | ) 9 | class PixivisionWebViewPageArguments with _$PixivisionWebViewPageArguments { 10 | const factory PixivisionWebViewPageArguments({ 11 | /// 语言 12 | required String language, 13 | /// ID 14 | required int id, 15 | /// 作品标题 16 | required String title, 17 | /// 作品封面图链接 18 | required String coverUrl, 19 | }) = _PixivisionWebViewPageArguments; 20 | } 21 | -------------------------------------------------------------------------------- /lib/pages/artwork/pixivision/provider/illust_pixivision_provider.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:artvier/api_app/api_illusts.dart'; 4 | import 'package:artvier/base/base_provider/base_notifier.dart'; 5 | import 'package:artvier/pages/artwork/pixivision/model/pixivision_webview_page_arguments.dart'; 6 | 7 | class IllustPixivisionNotifier extends BaseAutoDisposeFamilyAsyncNotifier { 8 | late String language; 9 | late int id; 10 | 11 | IllustPixivisionNotifier(); 12 | 13 | @override 14 | FutureOr build(PixivisionWebViewPageArguments arg) async { 15 | language = arg.language; 16 | id = arg.id; 17 | return fetch(); 18 | } 19 | 20 | /// 初始化数据 21 | @override 22 | Future fetch() async { 23 | var result = await ApiIllusts(requester).illustPixvisionWebContent(language, id); 24 | return result; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/pages/artwork/series/model/arguments.dart: -------------------------------------------------------------------------------- 1 | import 'package:artvier/model_response/manga/manga_series_list.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | 4 | part 'arguments.freezed.dart'; 5 | 6 | /// Pixivision Webview 页的传递参数 7 | @Freezed( 8 | copyWith: true, 9 | ) 10 | class MangaSeriesDetailPagePageArguments with _$MangaSeriesDetailPagePageArguments { 11 | const factory MangaSeriesDetailPagePageArguments({ 12 | /// ID 13 | required int id, 14 | 15 | /// 作品标题 16 | required String title, 17 | 18 | /// 封面图 19 | required String url, 20 | 21 | /// 作者 22 | required User user, 23 | }) = _MangaSeriesDetailPagePageArguments; 24 | } 25 | -------------------------------------------------------------------------------- /lib/pages/artwork/viewer/model/image_quality_url_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'image_quality_url_model.freezed.dart'; 4 | 5 | /// 图片多种画质的链接 6 | @Freezed( 7 | copyWith: true, 8 | ) 9 | class ImageQualityUrl with _$ImageQualityUrl { 10 | const factory ImageQualityUrl({ 11 | /// 普通画质 12 | required String normal, 13 | 14 | /// 原图画质 15 | required String original, 16 | }) = _ImageQualityUrl; 17 | } 18 | -------------------------------------------------------------------------------- /lib/pages/artwork/viewer/model/image_viewer_page_arguments.dart: -------------------------------------------------------------------------------- 1 | import 'package:artvier/config/enums.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | import 'package:artvier/pages/artwork/viewer/model/image_quality_url_model.dart'; 4 | 5 | part 'image_viewer_page_arguments.freezed.dart'; 6 | 7 | /// 图片浏览页的传递参数 8 | @Freezed( 9 | copyWith: true, 10 | ) 11 | class ImageViewerPageArguments with _$ImageViewerPageArguments { 12 | const factory ImageViewerPageArguments({ 13 | /// 图片列表 14 | required List urlList, 15 | 16 | /// 资源类型 17 | required DownloadType downloadType, 18 | 19 | /// 作品 id 20 | required String worksId, 21 | 22 | /// 作品标题 23 | required String title, 24 | 25 | /// 初始查看第几张图片 26 | required int index, 27 | }) = _ImageViewerPageArguments; 28 | } 29 | -------------------------------------------------------------------------------- /lib/pages/artwork/viewer/model/image_viewer_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'image_viewer_state.freezed.dart'; 4 | 5 | /// 图片预览页的状态 6 | @Freezed( 7 | copyWith: true, 8 | ) 9 | class ImageViewerPageState with _$ImageViewerPageState { 10 | const factory ImageViewerPageState({ 11 | /// 当前图片索引 12 | required int pageIndex, 13 | 14 | /// (分辨率)是否原图 15 | required bool isOriginal, 16 | }) = _ImageViewerPageState; 17 | } 18 | -------------------------------------------------------------------------------- /lib/pages/comment/model/comment_bar_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'comment_bar_model.freezed.dart'; 4 | 5 | /// 图片多种画质的链接 6 | @Freezed( 7 | copyWith: true, 8 | ) 9 | class CommentBarModel with _$CommentBarModel { 10 | const factory CommentBarModel({ 11 | /// 回复哪个评论 12 | int? parentCommentId, 13 | String? parentCommentName, 14 | 15 | /// 是否正在发布中 16 | @Default(false) bool isSending, 17 | 18 | /// 是否处于激活状态 19 | @Default(false) bool isActived, 20 | 21 | /// 输入框的内容 22 | String? comment, 23 | }) = _CommentBarModel; 24 | } 25 | -------------------------------------------------------------------------------- /lib/pages/comment/provider/comment_bar_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:artvier/base/base_provider/base_notifier.dart'; 2 | import 'package:artvier/pages/comment/model/comment_bar_model.dart'; 3 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 4 | 5 | final commentBarProvider = 6 | StateNotifierProvider.autoDispose.family((ref, worksId) { 7 | return CommentBarNotifier(const CommentBarModel(), ref: ref, worksId: worksId); 8 | }); 9 | 10 | /// Default send mode: [model.parentCommentId] is null. 11 | /// Reply mode: [model.parentCommentId] is not null. 12 | class CommentBarNotifier extends BaseStateNotifier { 13 | CommentBarNotifier(super.state, {required super.ref, required this.worksId}); 14 | 15 | String worksId; 16 | 17 | void setIsSending(bool isSending) { 18 | update(state.copyWith(isSending: isSending)); 19 | } 20 | 21 | void enableReply(int parentCommentId, String parentCommentName) async { 22 | state = state.copyWith(parentCommentId: parentCommentId, parentCommentName: parentCommentName); 23 | } 24 | 25 | void disableReply() async { 26 | state = state.copyWith(parentCommentId: null, parentCommentName: null); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/pages/main_navigation_tab_page/home/model/home_illust_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:artvier/model_response/illusts/common_illust.dart'; 3 | 4 | part 'home_illust_model.freezed.dart'; 5 | 6 | /// 插画首页的数据模型 7 | @Freezed( 8 | copyWith: true, 9 | ) 10 | class HomeIllustModel with _$HomeIllustModel { 11 | const factory HomeIllustModel({ 12 | required List ranking, 13 | required List recommended, 14 | }) = _HomeIllustModel; 15 | } 16 | -------------------------------------------------------------------------------- /lib/pages/main_navigation_tab_page/newest/provider/friends_newest_provider.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:artvier/config/enums.dart'; 4 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 5 | import 'package:artvier/api_app/api_newest.dart'; 6 | import 'package:artvier/base/base_provider/base_notifier.dart'; 7 | import 'package:artvier/base/base_provider/illust_list_notifier.dart'; 8 | import 'package:artvier/base/base_provider/novel_list_notifier.dart'; 9 | import 'package:artvier/model_response/illusts/common_illust.dart'; 10 | import 'package:artvier/model_response/novels/common_novel.dart'; 11 | 12 | final friendsNewestWorksTypeProvider = StateProvider((ref) { 13 | return WorksType.illust; 14 | }); 15 | 16 | class FriendsNewestArtworksNotifier extends BaseAsyncNotifier> with IllustListAsyncNotifierMixin { 17 | @override 18 | FutureOr> build() async { 19 | handleDispose(ref); 20 | handleCollectState(ref); 21 | return fetch(); 22 | } 23 | 24 | /// 初始化数据 25 | @override 26 | Future> fetch() async { 27 | var result = await ApiNewArtWork(requester).friendsNewestArtworks(cancelToken: cancelToken); 28 | nextUrl = result.nextUrl; 29 | return result.illusts; 30 | } 31 | } 32 | 33 | class FriendsNewestNovelsNotifier extends BaseAsyncNotifier> with NovelListAsyncNotifierMixin { 34 | @override 35 | FutureOr> build() async { 36 | beforeBuild(ref); 37 | return fetch(); 38 | } 39 | 40 | /// 初始化数据 41 | @override 42 | Future> fetch() async { 43 | var result = await ApiNewArtWork(requester).friendsNewestNovels(cancelToken: cancelToken); 44 | nextUrl = result.nextUrl; 45 | return result.novels; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/pages/main_navigation_tab_page/profile/models.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | typedef IconButtonModelBuilder = IconButtonModel Function(BuildContext context); 4 | typedef PerferenceBottomSheetBuilder = PerferenceBottomSheetModel Function(BuildContext context); 5 | 6 | class IconButtonModel { 7 | String text; 8 | Widget icon; 9 | 10 | /// 将要跳转路由的名字 11 | String routeName; 12 | 13 | /// 跳转路由携带的参数 14 | Object? argument; 15 | 16 | IconButtonModel(this.text, this.icon, this.routeName, this.argument); 17 | } 18 | 19 | class PerferenceBottomSheetModel { 20 | String text; 21 | Widget icon; 22 | Widget? widget; 23 | Object? argument; 24 | 25 | PerferenceBottomSheetModel(this.text, this.icon, this.widget, this.argument); 26 | } 27 | -------------------------------------------------------------------------------- /lib/pages/main_navigation_tab_page/profile/provider/profile_provider.dart: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/lib/pages/main_navigation_tab_page/profile/provider/profile_provider.dart -------------------------------------------------------------------------------- /lib/pages/main_navigation_tab_page/profile/quick_settings/proxy/proxy_bottom_sheet.dart: -------------------------------------------------------------------------------- 1 | import 'package:artvier/base/base_page.dart'; 2 | import 'package:artvier/component/bottom_sheet/slide_bar.dart'; 3 | import 'package:artvier/pages/main_navigation_tab_page/profile/quick_settings/proxy/logic.dart'; 4 | import 'package:artvier/pages/main_navigation_tab_page/profile/quick_settings/proxy/proxy_settings.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 7 | 8 | class ProxyOriginSettingsBottomSheet extends ConsumerStatefulWidget { 9 | const ProxyOriginSettingsBottomSheet({super.key}); 10 | 11 | @override 12 | ConsumerState createState() => SettingNetworkPageState(); 13 | } 14 | 15 | class SettingNetworkPageState extends BasePageState with ProxyLogic { 16 | late TextEditingController _hostController; 17 | late TextEditingController _portController; 18 | 19 | @override 20 | void initState() { 21 | super.initState(); 22 | _hostController = TextEditingController(); 23 | _portController = TextEditingController(); 24 | } 25 | 26 | @override 27 | void dispose() { 28 | _hostController.dispose(); 29 | _portController.dispose(); 30 | super.dispose(); 31 | } 32 | 33 | @override 34 | Widget build(BuildContext context) { 35 | return const Column( 36 | crossAxisAlignment: CrossAxisAlignment.start, 37 | children: [ 38 | BottomSheetSlideBar(), 39 | Padding( 40 | padding: EdgeInsets.only(left: 12.0, right: 12.0, bottom: 24), 41 | child: ProxySettings(), 42 | ), 43 | ], 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/pages/main_navigation_tab_page/profile/quick_settings/theme/theme_bottom_sheet.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | import 'package:artvier/component/bottom_sheet/slide_bar.dart'; 4 | import 'package:artvier/l10n/localization_intl.dart'; 5 | import 'package:artvier/pages/main_navigation_tab_page/profile/quick_settings/theme/widget/brightness_settings_panel.dart'; 6 | 7 | class ThemeSettingsBottomSheet extends ConsumerWidget { 8 | const ThemeSettingsBottomSheet({super.key}); 9 | 10 | @override 11 | Widget build(BuildContext context, WidgetRef ref) { 12 | var l10n = LocalizationIntl.of(context); 13 | return SafeArea( 14 | child: Column( 15 | children: [ 16 | const BottomSheetSlideBar(), 17 | Padding( 18 | padding: const EdgeInsets.symmetric(vertical: 12.0), 19 | child: Text(l10n.dartModeSettingsTitle, style: Theme.of(context).textTheme.titleMedium), 20 | ), 21 | const BrightnessSettingsPanel(), 22 | ], 23 | ), 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/pages/main_navigation_tab_page/search/provider/search_input_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 2 | 3 | /// 搜索输入框(还没进入搜索结果页) 4 | final searchInputProvider = StateProvider.autoDispose((ref) => ""); 5 | -------------------------------------------------------------------------------- /lib/pages/novel/detail/arguments/novel_detail_page_args.dart: -------------------------------------------------------------------------------- 1 | import 'package:artvier/model_response/novels/common_novel.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | 4 | part 'novel_detail_page_args.freezed.dart'; 5 | 6 | /// 小说详情页面的传递参数 7 | @Freezed(copyWith: true) 8 | class NovelDetailPageArguments with _$NovelDetailPageArguments { 9 | const factory NovelDetailPageArguments({ 10 | required String worksId, 11 | String? title, 12 | CommonNovel? detail, 13 | }) = _NovelDetailPageArguments; 14 | } 15 | -------------------------------------------------------------------------------- /lib/pages/novel/series/model/arguments.dart: -------------------------------------------------------------------------------- 1 | import 'package:artvier/model_response/novels/novel_series_list.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | 4 | part 'arguments.freezed.dart'; 5 | 6 | /// Pixivision Webview 页的传递参数 7 | @Freezed( 8 | copyWith: true, 9 | ) 10 | class NovelSeriesDetailPagePageArguments with _$NovelSeriesDetailPagePageArguments { 11 | const factory NovelSeriesDetailPagePageArguments({ 12 | /// ID 13 | required int id, 14 | 15 | /// 作品标题 16 | required String title, 17 | 18 | /// 封面图 19 | required String url, 20 | 21 | /// 作者 22 | required User user, 23 | }) = _NovelSeriesDetailPagePageArguments; 24 | } 25 | -------------------------------------------------------------------------------- /lib/pages/ranking/logic.dart: -------------------------------------------------------------------------------- 1 | import 'package:artvier/component/filter_dropdown/custom_dropdown.dart'; 2 | import 'package:artvier/pages/ranking/provider/ranking_provider.dart'; 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 5 | 6 | mixin RankingPageLogic { 7 | ScrollController get scrollController; 8 | CustomDropDownController get dropDownController; 9 | 10 | WidgetRef get ref; 11 | 12 | DateTime get datePickerValue; 13 | 14 | void handlePressedToTop() { 15 | scrollController.animateTo(0, duration: const Duration(milliseconds: 500), curve: Curves.decelerate); 16 | } 17 | 18 | void handlePressedDatePicker() { 19 | dropDownController.toggle(); 20 | } 21 | 22 | void handleRemoveDate() { 23 | handlePressedDateReset(); 24 | } 25 | 26 | void handlePressedDateReset() { 27 | ref.read(rankingDateProvier.notifier).update((state) => null); 28 | dropDownController.close(); 29 | } 30 | 31 | void handlePressedDateConfirm() { 32 | ref.read(rankingDateProvier.notifier).update((state) => datePickerValue); 33 | dropDownController.close(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/pages/search/result/arguments/seach_filter_arguments.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:artvier/api_app/api_serach.dart'; 3 | 4 | part 'seach_filter_arguments.freezed.dart'; 5 | 6 | @Freezed( 7 | copyWith: true, 8 | ) 9 | 10 | /// 搜索结果页,筛选的参数 11 | class SearchFilterArguments with _$SearchFilterArguments { 12 | const factory SearchFilterArguments({ 13 | /// 搜索对象 14 | @Default(ApiSearchConstants.tagPerfectMatch) String? searchTarget, 15 | 16 | /// AI 17 | @Default(0) int? searchAiType, 18 | 19 | /// 最早日期 20 | String? startDate, 21 | 22 | /// 最晚日期 23 | String? endDate, 24 | 25 | /// 排序方式 26 | @Default(ApiSearchConstants.dateDesc) String sort, 27 | 28 | /// 匹配规则 29 | @Default(ApiSearchConstants.tagPartialMatch) String match, 30 | 31 | /// 收藏数(标签、非会员) 32 | String? bookmarkCountNotPremium, 33 | }) = _SearchFilterArguments; 34 | } 35 | -------------------------------------------------------------------------------- /lib/pages/search/result/provider/search_filters_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 2 | import 'package:artvier/config/enums.dart'; 3 | import 'package:artvier/pages/search/result/arguments/seach_filter_arguments.dart'; 4 | 5 | /// 搜索类型 6 | final searchTypeProvider = StateProvider.autoDispose((ref) => SearchType.artwork); 7 | 8 | /// 搜索的筛选 9 | final searchFilterProvider = StateProvider.autoDispose((ref) => const SearchFilterArguments()); 10 | -------------------------------------------------------------------------------- /lib/pages/settings/check_update/provider/check_update_provider.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:artvier/api_app/api_update.dart'; 4 | import 'package:artvier/base/base_provider/base_notifier.dart'; 5 | import 'package:artvier/global/provider/requester_provider.dart'; 6 | import 'package:artvier/model_response/update/git_release.dart'; 7 | import 'package:dio/dio.dart'; 8 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 9 | 10 | final changeLogProvider = AutoDisposeAsyncNotifierProvider.family(() { 11 | return ChangeLogNotifier(); 12 | }); 13 | 14 | class ChangeLogNotifier extends BaseAutoDisposeFamilyAsyncNotifier { 15 | CancelToken cancelToken = CancelToken(); 16 | 17 | @override 18 | FutureOr build(String arg) { 19 | ref.onDispose(() { 20 | if (!cancelToken.isCancelled) cancelToken.cancel(); 21 | }); 22 | return fetch(); 23 | } 24 | 25 | @override 26 | Future fetch() { 27 | return ApiUpdate(ref.read(originHttpRequesterProvider)).releaseDetails(arg, cancelToken: cancelToken); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/pages/settings/develop/developer_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:artvier/base/base_page.dart'; 2 | import 'package:artvier/pages/settings/develop/widgets/refresh_token_settings.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 5 | 6 | class DeveloperPage extends ConsumerStatefulWidget { 7 | const DeveloperPage({super.key}); 8 | 9 | @override 10 | ConsumerState createState() => _DeveloperPageState(); 11 | } 12 | 13 | class _DeveloperPageState extends BasePageState { 14 | @override 15 | Widget build(BuildContext context) { 16 | return Scaffold( 17 | appBar: AppBar(title: const Text("Developer settings")), 18 | body: const Column( 19 | children: [ 20 | RefreshTokenSettings(), 21 | ], 22 | ), 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/pages/user/collection/logic.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | import 'package:artvier/component/bottom_sheet/bottom_sheets.dart'; 4 | import 'package:artvier/pages/user/collection/model/collections_filter_model.dart'; 5 | import 'package:artvier/pages/user/collection/provider/filter_provider.dart'; 6 | import 'package:artvier/pages/user/collection/widget/collections_filter_sheets.dart'; 7 | 8 | mixin MyCollectionPageLogic { 9 | WidgetRef get ref; 10 | 11 | /// 筛选按钮 12 | void handlePressedFilter() { 13 | BottomSheets.showCustomBottomSheet( 14 | context: ref.context, 15 | borderRadius: const BorderRadius.only( 16 | topLeft: Radius.circular(20), 17 | topRight: Radius.circular(20), 18 | ), 19 | exitOnClickModal: false, 20 | child: CollectionsFilterSheet(), 21 | ).then((value) { 22 | if (value != null) ref.read(collectionsFilterProvider.notifier).update((state) => value); 23 | }); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/pages/user/collection/model/collections_filter_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:artvier/config/enums.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | 4 | part 'collections_filter_model.freezed.dart'; 5 | 6 | /// 收藏作品的筛选 7 | @Freezed( 8 | copyWith: true, 9 | ) 10 | class CollectionsFilterModel with _$CollectionsFilterModel { 11 | const factory CollectionsFilterModel({ 12 | /// 隐私 13 | @Default(Restrict.public) Restrict restrict, 14 | 15 | /// 根据tag筛选,当null时不根据tag筛选,当值为""即Empty String时,表示”未分類“ 16 | String? tag, 17 | 18 | /// 作品类型 19 | required WorksType worksType, 20 | }) = _CollectionsFilterModel; 21 | } 22 | -------------------------------------------------------------------------------- /lib/pages/user/collection/provider/artwork_collections_provider.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 4 | import 'package:artvier/api_app/api_user.dart'; 5 | import 'package:artvier/base/base_provider/base_notifier.dart'; 6 | import 'package:artvier/base/base_provider/illust_list_notifier.dart'; 7 | import 'package:artvier/global/provider/current_account_provider.dart'; 8 | import 'package:artvier/model_response/illusts/common_illust.dart'; 9 | import 'package:artvier/pages/user/collection/model/collections_filter_model.dart'; 10 | import 'package:artvier/pages/user/collection/provider/filter_provider.dart'; 11 | 12 | /// 收藏页面(美术作品)的加载状态 13 | final myArtworkCollectionsStateProvider = 14 | AsyncNotifierProvider.autoDispose<_MyArtworkCollectionsStateNotifier, List>( 15 | _MyArtworkCollectionsStateNotifier.new); 16 | 17 | class _MyArtworkCollectionsStateNotifier extends BaseAutoDisposeAsyncNotifier> 18 | with IllustListAsyncNotifierMixin { 19 | late String userId; 20 | 21 | late CollectionsFilterModel filterModel; 22 | 23 | @override 24 | FutureOr> build() { 25 | handleCancel(ref); 26 | handleCollectState(ref); 27 | userId = ref.watch(globalCurrentAccountProvider)!.user.id; 28 | filterModel = ref.watch(collectionsFilterProvider); 29 | return fetch(); 30 | } 31 | 32 | /// 首次获取数据 33 | @override 34 | Future> fetch() async { 35 | var res = await ApiUser(requester).artworkCollections( 36 | userId: userId, 37 | tag: filterModel.tag?.isEmpty ?? false ? "未分類" : filterModel.tag, 38 | restrict: filterModel.restrict, 39 | cancelToken: cancelToken, 40 | ); 41 | nextUrl = res.nextUrl; 42 | return res.illusts; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/pages/user/collection/provider/filter_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 2 | import 'package:artvier/config/enums.dart'; 3 | import 'package:artvier/pages/user/collection/model/collections_filter_model.dart'; 4 | 5 | /// 收藏作品的筛选条件(会影响收藏作品列表) 6 | final collectionsFilterProvider = StateProvider.autoDispose( 7 | (ref) => const CollectionsFilterModel(worksType: WorksType.illust), 8 | ); 9 | 10 | /// 缓存临时的收藏作品的筛选条件(只会影响标签列表) 11 | final cachedCollectionsFilterProvider = StateProvider.autoDispose( 12 | (ref) => ref.watch(collectionsFilterProvider), 13 | ); 14 | -------------------------------------------------------------------------------- /lib/pages/user/collection/provider/novel_collections_provider.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 4 | import 'package:artvier/api_app/api_user.dart'; 5 | import 'package:artvier/base/base_provider/base_notifier.dart'; 6 | import 'package:artvier/base/base_provider/novel_list_notifier.dart'; 7 | import 'package:artvier/global/provider/current_account_provider.dart'; 8 | import 'package:artvier/model_response/novels/common_novel.dart'; 9 | import 'package:artvier/pages/user/collection/model/collections_filter_model.dart'; 10 | import 'package:artvier/pages/user/collection/provider/filter_provider.dart'; 11 | 12 | /// 收藏页面(美术作品)的加载状态 13 | final myNovelCollectionsStateProvider = 14 | AsyncNotifierProvider.autoDispose<_MyNovelCollectionsStateNotifier, List>( 15 | _MyNovelCollectionsStateNotifier.new); 16 | 17 | class _MyNovelCollectionsStateNotifier extends BaseAutoDisposeAsyncNotifier> 18 | with NovelListAsyncNotifierMixin { 19 | late String userId; 20 | 21 | late CollectionsFilterModel filterModel; 22 | 23 | @override 24 | FutureOr> build() { 25 | beforeBuild(ref); 26 | userId = ref.watch(globalCurrentAccountProvider)!.user.id; 27 | filterModel = ref.watch(collectionsFilterProvider); 28 | return fetch(); 29 | } 30 | 31 | /// 首次获取数据 32 | @override 33 | Future> fetch() async { 34 | var res = await ApiUser(requester).novelCollections( 35 | userId: userId, 36 | tag: filterModel.tag?.isEmpty ?? false ? "未分類" : filterModel.tag, 37 | restrict: filterModel.restrict, 38 | cancelToken: cancelToken, 39 | ); 40 | nextUrl = res.nextUrl; 41 | return res.novels; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/pages/user/collection/tabpage/artworks_tabpage.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | import 'package:artvier/business_component/listview/illust_listview/illust_waterfall_gridview.dart'; 4 | import 'package:artvier/component/loading/request_loading.dart'; 5 | import 'package:artvier/config/enums.dart'; 6 | import 'package:artvier/model_response/illusts/common_illust.dart'; 7 | import 'package:artvier/pages/user/collection/provider/artwork_collections_provider.dart'; 8 | 9 | class MyCollectArtworksTabPage extends ConsumerStatefulWidget { 10 | const MyCollectArtworksTabPage({super.key}); 11 | 12 | @override 13 | ConsumerState createState() => _MyCollectArtworksTabPageState(); 14 | } 15 | 16 | class _MyCollectArtworksTabPageState extends ConsumerState 17 | with AutomaticKeepAliveClientMixin { 18 | @override 19 | Widget build(BuildContext context) { 20 | super.build(context); 21 | return ref.watch(myArtworkCollectionsStateProvider).when( 22 | loading: () => const RequestLoading(), 23 | data: (List artworks) => IllustWaterfallGridView( 24 | artworkList: artworks, 25 | lazyloadState: LazyloadState.idle, 26 | onLazyload: () async => ref.read(myArtworkCollectionsStateProvider.notifier).next(), 27 | ), 28 | error: (_, __) => RequestLoadingFailed( 29 | onRetry: () async => ref.read(myArtworkCollectionsStateProvider.notifier).reload(), 30 | ), 31 | ); 32 | } 33 | 34 | @override 35 | bool get wantKeepAlive => true; 36 | } 37 | -------------------------------------------------------------------------------- /lib/pages/user/collection/tabpage/novels_tabpage.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | import 'package:artvier/business_component/listview/novel_listview/novel_list.dart'; 4 | import 'package:artvier/component/loading/request_loading.dart'; 5 | import 'package:artvier/config/enums.dart'; 6 | import 'package:artvier/model_response/novels/common_novel.dart'; 7 | import 'package:artvier/pages/user/collection/provider/novel_collections_provider.dart'; 8 | 9 | class MyCollectNovelsTabPage extends ConsumerStatefulWidget { 10 | const MyCollectNovelsTabPage({super.key}); 11 | 12 | @override 13 | ConsumerState createState() => _MyCollectNovelsTabPageState(); 14 | } 15 | 16 | class _MyCollectNovelsTabPageState extends ConsumerState with AutomaticKeepAliveClientMixin { 17 | @override 18 | Widget build(BuildContext context) { 19 | super.build(context); 20 | return ref.watch(myNovelCollectionsStateProvider).when( 21 | loading: () => const RequestLoading(), 22 | data: (List list) => NovelListView( 23 | novelList: list, 24 | lazyloadState: LazyloadState.idle, 25 | onLazyload: () async => ref.read(myNovelCollectionsStateProvider.notifier).next(), 26 | ), 27 | error: (_, __) => RequestLoadingFailed( 28 | onRetry: () async => ref.read(myNovelCollectionsStateProvider.notifier).reload(), 29 | ), 30 | ); 31 | } 32 | 33 | @override 34 | bool get wantKeepAlive => true; 35 | } 36 | -------------------------------------------------------------------------------- /lib/pages/user/collection/widget/tag_list_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class TagListItemWidget extends StatelessWidget { 4 | final String name; 5 | final int? count; 6 | final bool isActived; 7 | final void Function() onTap; 8 | 9 | const TagListItemWidget({ 10 | super.key, 11 | required this.name, 12 | required this.count, 13 | required this.isActived, 14 | required this.onTap, 15 | }); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return ClipRRect( 20 | borderRadius: const BorderRadius.all(Radius.circular(10.0)), 21 | child: Material( 22 | shadowColor: Colors.transparent, 23 | color: isActived ? Theme.of(context).colorScheme.secondaryContainer : Colors.transparent, 24 | child: InkWell( 25 | onTap: onTap, 26 | child: Container( 27 | padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0), 28 | decoration: BoxDecoration( 29 | borderRadius: const BorderRadius.all(Radius.circular(10.0)), 30 | border: Border.all( 31 | color: isActived ? Theme.of(context).colorScheme.primary.withAlpha(100) : Colors.transparent), 32 | ), 33 | child: Text( 34 | name, 35 | style: TextStyle( 36 | fontSize: 16, 37 | fontWeight: isActived ? FontWeight.w500 : FontWeight.w400, 38 | color: isActived ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.onSurface, 39 | ), 40 | overflow: TextOverflow.ellipsis, 41 | ), 42 | ), 43 | ), 44 | ), 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/pages/user/detail/logic.dart: -------------------------------------------------------------------------------- 1 | import 'package:artvier/base/base_page.dart'; 2 | import 'package:artvier/pages/user/detail/user_detail_page.dart'; 3 | 4 | mixin UserDetailPageLogic on BasePageState { 5 | String get userId; 6 | } 7 | -------------------------------------------------------------------------------- /lib/pages/user/detail/provider/user_follow_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 2 | import 'package:artvier/config/enums.dart'; 3 | import 'package:artvier/global/model/following_state_changed_arguments%20copy/following_state_changed_arguments.dart'; 4 | import 'package:artvier/global/provider/follow_state_provider.dart'; 5 | 6 | /// 用户详情页的关注状态 7 | final userFollowStateProvider = 8 | StateNotifierProvider.autoDispose.family((ref, userId) { 9 | // 监听全局的关注通知 10 | ref.listen(globalFollowingStateChangedProvider, (previous, next) { 11 | if (next != null && next.userId == userId) { 12 | ref.notifier.setFollowState(next.state); 13 | } 14 | }); 15 | 16 | return FollowNotifier(UserFollowState.notFollow, ref: ref, userId: userId); 17 | }); 18 | -------------------------------------------------------------------------------- /lib/pages/user/works/my_works.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class MyWorksPage extends StatefulWidget { 4 | const MyWorksPage(Object? arguments, {super.key}); 5 | 6 | @override 7 | State createState() => _MyBookmarksState(); 8 | } 9 | 10 | class _MyBookmarksState extends State with TickerProviderStateMixin { 11 | @override 12 | Widget build(BuildContext context) { 13 | return const Scaffold( 14 | body: Center( 15 | child: Text("Empty"), 16 | ), 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/request/default_interceptor.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:artvier/global/logger.dart'; 3 | 4 | class DefaultInterceptor extends InterceptorsWrapper { 5 | DefaultInterceptor(); 6 | 7 | @override 8 | void onError(DioException err, ErrorInterceptorHandler handler) { 9 | try { 10 | if (err.type == DioExceptionType.cancel) { 11 | logger.i(err.type); 12 | } else { 13 | // ignore: prefer_interpolation_to_compose_strings, prefer_adjacent_string_concatenation 14 | logger.w("Request error.\n" + 15 | "${err.toString()}\n" + 16 | "Request uri: ${err.requestOptions.uri}\n" + 17 | "Request params: ${err.requestOptions.queryParameters.toString()}\n" + 18 | "Request header: ${err.requestOptions.headers}\n"); 19 | } 20 | } catch (e) { 21 | logger.w(err.response); 22 | } 23 | super.onError(err, handler); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/request/my_http_overrides.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:artvier/global/logger.dart'; 4 | 5 | // import 'package:flutter_riverpod/flutter_riverpod.dart'; 6 | 7 | // final httpOverridesProvider = Provider((ref) => MyHttpOverrides()); 8 | 9 | class MyHttpOverrides extends HttpOverrides { 10 | factory MyHttpOverrides() => _instance; 11 | 12 | static final MyHttpOverrides _instance = MyHttpOverrides._(); 13 | 14 | static MyHttpOverrides get instance => _instance; 15 | 16 | MyHttpOverrides._(); 17 | 18 | /// 代理配置,格式为[HOST/IP:PORT],当null时则不使用代理 19 | String? _proxy; 20 | 21 | /// 设置代理,当null时则不使用代理 22 | void setProxyAddress(String? proxy) { 23 | if (_proxy == proxy) return; 24 | _proxy = proxy; 25 | logger.i("The Proxy in HttpOverrides is changed (null means disabled): \nproxy=【$proxy】"); 26 | } 27 | 28 | @override 29 | HttpClient createHttpClient(SecurityContext? context) { 30 | return super.createHttpClient(context) 31 | ..badCertificateCallback = (X509Certificate cert, String host, int port) { 32 | return true; 33 | } 34 | // ..findProxy = (url) { 35 | // return 'PROXY $proxy'; 36 | // }; 37 | ..findProxy = _findProxy; 38 | } 39 | 40 | // 配置代理设置 41 | String _findProxy(url) { 42 | if (_proxy == null) { 43 | return HttpClient.findProxyFromEnvironment(url); 44 | } else { 45 | return HttpClient.findProxyFromEnvironment(url, environment: { 46 | "http_proxy": _proxy!, 47 | "https_proxy": _proxy!, 48 | "no_proxy": "", 49 | }); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/storage/downloads/download_task_table.dart: -------------------------------------------------------------------------------- 1 | import 'package:artvier/config/enums.dart'; 2 | import 'package:drift/drift.dart'; 3 | 4 | /// 下载任务的数据库表 5 | class DownloadTaskTable extends Table { 6 | /// 任务 Id 7 | IntColumn get taskId => integer().nullable().withDefault(const Constant(1)).autoIncrement()(); 8 | 9 | /// 标题 10 | TextColumn get title => text()(); 11 | 12 | /// 作品 Id 13 | TextColumn get worksId => text()(); 14 | 15 | /// 下载链接 16 | TextColumn get downloadUrl => text()(); 17 | 18 | /// 保存路径 19 | TextColumn get savePath => text().nullable()(); 20 | 21 | /// 预览图片链接(必须是小图) 22 | TextColumn get previewImageUrl => text().nullable()(); 23 | 24 | /// 总大小 25 | Column get totalBytes => integer().withDefault(const Constant(0))(); 26 | 27 | /// 已下载大小 28 | Column get receivedBytes => integer().withDefault(const Constant(0))(); 29 | 30 | /// 资源类型 31 | TextColumn get type => textEnum()(); 32 | 33 | /// 下载状态 34 | TextColumn get status => textEnum()(); 35 | } 36 | -------------------------------------------------------------------------------- /lib/storage/language_store.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:artvier/base/base_storage.dart'; 4 | import 'package:artvier/config/constants.dart'; 5 | 6 | class LanguageStorage extends BaseStorage { 7 | LanguageStorage(super.sharedPreferences); 8 | 9 | static const String enableAutoLanguageKey = "enable_auto_language"; 10 | static const String languageCodeKey = "language_code"; 11 | static const String countryCodeKey = "language_country_code"; 12 | 13 | Future setAutoLanguage(bool enableAutoLanguage) async { 14 | return await sharedPreferences.setBool(enableAutoLanguageKey, enableAutoLanguage); 15 | } 16 | 17 | bool getAutoLanguage() { 18 | return sharedPreferences.getBool(enableAutoLanguageKey) ?? true; 19 | } 20 | 21 | Future setLanguage(String languageCode, String countryCode) async { 22 | bool h = await sharedPreferences.setString(languageCodeKey, languageCode); 23 | return h && await sharedPreferences.setString(countryCodeKey, countryCode); 24 | } 25 | 26 | Locale getLanguageLocale() { 27 | String languageCode = sharedPreferences.getString(languageCodeKey) ?? CONSTANTS.default_language_code; 28 | String countryCode = sharedPreferences.getString(countryCodeKey) ?? CONSTANTS.default_country_code; 29 | return Locale(languageCode, countryCode); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/storage/novel_viewer_store.dart: -------------------------------------------------------------------------------- 1 | import 'package:artvier/base/base_storage.dart'; 2 | 3 | class NovelViewerStorage extends BaseStorage { 4 | NovelViewerStorage(super.sharedPreferences); 5 | 6 | static const _textSize = "text_size"; 7 | 8 | // 设置主题模式 9 | Future setTextSize(double textSize) async { 10 | return await sharedPreferences.setDouble(_textSize, textSize); 11 | } 12 | 13 | // 获取主题模式 14 | double? textSize() { 15 | return sharedPreferences.getDouble(_textSize); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/storage/theme_storage.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: constant_identifier_names 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:artvier/base/base_storage.dart'; 5 | 6 | class ThemeStorage extends BaseStorage { 7 | ThemeStorage(super.sharedPreferences); 8 | 9 | // 当前主题模式 10 | static const _themeMode = "theme_mode"; 11 | 12 | /// 将[ThemeMode] 映射成int类型 13 | static const Map themeModeMap = { 14 | 0: ThemeMode.system, 15 | 1: ThemeMode.light, 16 | 2: ThemeMode.dark, 17 | }; 18 | 19 | // 设置主题模式 20 | Future setThemeMode(ThemeMode themeMode) async { 21 | return await sharedPreferences.setInt( 22 | _themeMode, 23 | themeModeMap.entries.firstWhere((element) => element.value == themeMode).key, 24 | ); 25 | } 26 | 27 | // 获取主题模式 28 | ThemeMode themeMode() { 29 | int? result = sharedPreferences.getInt(_themeMode); 30 | return themeModeMap[themeModeMap.keys.contains(result) ? result : 0]!; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/storage/viewing_history/viewing_history_table.dart: -------------------------------------------------------------------------------- 1 | import 'package:artvier/config/enums.dart'; 2 | import 'package:drift/drift.dart'; 3 | 4 | /// 浏览历史的数据库表 5 | class ViewingHistoryTable extends Table { 6 | /// 标题 7 | TextColumn get title => text()(); 8 | 9 | /// 作品类型 10 | TextColumn get type => textEnum()(); 11 | 12 | /// 作品 Id 13 | TextColumn get worksId => text()(); 14 | 15 | /// 作者 16 | TextColumn get authorName => text().nullable()(); 17 | 18 | /// 预览图片链接(必须是小图) 19 | TextColumn get previewImageUrl => text().nullable()(); 20 | 21 | /// 最后浏览时间 22 | DateTimeColumn get lastTime => dateTime().withDefault(currentDate)(); 23 | } 24 | -------------------------------------------------------------------------------- /lib/util/save_image_util.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | typedef ProgressCallback = void Function(int received, int total); 4 | 5 | 6 | // TODO: 多种下载保存方式 7 | class SaveImageUtil { 8 | // 保存图片 9 | static saveImageToGallery( 10 | String url, { 11 | int quality = 100, 12 | ProgressCallback? onProgress, 13 | }) async { 14 | // Response response; 15 | // response = await Dio() 16 | // .get(url, options: Options(responseType: ResponseType.bytes, headers: {"referer": CONSTANTS.referer}), 17 | // onReceiveProgress: (received, total) { 18 | // if (total != -1) { 19 | // ///当前下载的百分比 20 | // logger.i("$url: ${(received / total * 100).toStringAsFixed(1)}%"); 21 | // if (onProgress != null) onProgress(received, total); 22 | // } 23 | // }); 24 | 25 | // var saveResult = await ImageGallerySaver.saveImage(Uint8List.fromList(response.data), quality: quality); 26 | // return saveResult["isSuccess"]; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/util/string_util.dart: -------------------------------------------------------------------------------- 1 | /// 字符串工具 2 | class StringUtil { 3 | /// 用于 `Text` 组件,以字符的方式截断而不是单词形式截断 4 | static String breakChars(String text) { 5 | if (text.isEmpty) { 6 | return text; 7 | } 8 | String breakWord = ''; 9 | for (var element in text.runes) { 10 | breakWord += String.fromCharCode(element); 11 | breakWord += '\u200B'; 12 | } 13 | return breakWord; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /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 that Flutter provides. 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:artvier/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/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/web/favicon.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerrinz/pixiv-artvier/88731dc0d2a26258486ced0013a798e06e8e6891/web/icons/Icon-512.png -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | Artvier 30 | 31 | 32 | 33 | 36 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Artvier", 3 | "short_name": "Artvier", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A third-party app for pixiv.", 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 | } 24 | --------------------------------------------------------------------------------