├── .github └── workflows │ └── build.yml ├── .gitignore ├── .metadata ├── LICENSE ├── README.md ├── analysis_options.yaml ├── assets ├── app_icon.ico ├── app_icon_1024.png ├── app_icon_128.png ├── app_icon_16.png ├── app_icon_256.png ├── app_icon_32.png ├── app_icon_512.png ├── app_icon_64.png ├── logo.png ├── logo2.png └── logo3.png ├── fonts └── PCL-English.ttf ├── lib ├── components │ ├── button │ │ ├── my_check_button.dart │ │ └── my_normal_button.dart │ ├── input │ │ ├── my_normal_label.dart │ │ └── my_text_input.dart │ └── other │ │ └── transition_page.dart ├── logic │ └── change_body.dart ├── main.dart └── view │ ├── about │ └── left.dart │ ├── body.dart │ ├── download │ └── left.dart │ ├── home │ ├── content │ │ ├── microsoft.dart │ │ ├── offline.dart │ │ └── thirdparty.dart │ └── left.dart │ ├── nav_bar.dart │ ├── online │ └── left.dart │ └── setting │ └── left.dart └── pubspec.yaml /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Minimal Source Build 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | 7 | jobs: 8 | generate-and-build: 9 | strategy: 10 | matrix: 11 | os: [ubuntu-latest, macos-latest, windows-latest] 12 | runs-on: ${{ matrix.os }} 13 | steps: 14 | - name: Checkout code 15 | uses: actions/checkout@v4 16 | - name: Set up Flutter 17 | uses: subosito/flutter-action@v2 18 | with: 19 | channel: stable 20 | - name: Initialize Project 21 | run: flutter create --platforms=windows,macos,linux --no-overwrite . 22 | - name: Start Build 23 | run: | 24 | flutter pub get 25 | if ("${{ runner.os }}" -eq "Linux") { 26 | sudo apt-get update -y 27 | sudo apt-get install -y ninja-build libgtk-3-dev 28 | flutter build linux --release 29 | } elseif ("${{ runner.os }}" -eq "macOS") { 30 | # rm ./macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png 31 | # rm ./macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png 32 | # rm ./macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png 33 | # rm ./macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png 34 | # rm ./macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png 35 | # rm ./macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png 36 | # rm ./macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png 37 | cp ./assets/app_icon_16.png ./macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png 38 | cp ./assets/app_icon_32.png ./macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png 39 | cp ./assets/app_icon_64.png ./macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png 40 | cp ./assets/app_icon_128.png ./macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png 41 | cp ./assets/app_icon_256.png ./macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png 42 | cp ./assets/app_icon_512.png ./macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png 43 | cp ./assets/app_icon_1024.png ./macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png 44 | flutter build macos --release 45 | } else { 46 | copy ./assets/app_icon.ico ./windows/runner/resources/app_icon.ico 47 | flutter build windows --release 48 | } 49 | shell: pwsh 50 | - name: Zip all releases 51 | run: | 52 | mkdir releases 53 | if ("${{ runner.os }}" -eq "Linux") { 54 | cd build/linux/x64/release/bundle 55 | zip -9 -r "../../../../../releases/PCL-Newer-Project-Linux.zip" . 56 | } elseif ("${{ runner.os }}" -eq "macOS") { 57 | cd build/macos/Build/Products/Release 58 | zip -9 -r "../../../../../releases/PCL-Newer-Project-macOS.zip" . 59 | } else { 60 | cd build/windows/x64/runner/Release 61 | 7z a "../../../../../releases/PCL-Newer-Project-Windows.zip" ./* 62 | } 63 | shell: pwsh 64 | - name: Upload all Artifacts 65 | uses: actions/upload-artifact@v4 66 | with: 67 | name: ${{ runner.os }}-bundle 68 | path: ./releases/PCL-Newer-Project-${{ runner.os }}.zip -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .build/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | .swiftpm/ 13 | migrate_working_dir/ 14 | 15 | # IntelliJ related 16 | *.iml 17 | *.ipr 18 | *.iws 19 | .idea/ 20 | 21 | # The .vscode folder contains launch configuration and tasks you configure in 22 | # VS Code which you may wish to be included in version control, so this line 23 | # is commented out by default. 24 | #.vscode/ 25 | 26 | # Flutter/Dart/Pub related 27 | **/doc/api/ 28 | **/ios/Flutter/.last_build_id 29 | .dart_tool/ 30 | .flutter-plugins 31 | .flutter-plugins-dependencies 32 | .pub-cache/ 33 | .pub/ 34 | /build/ 35 | 36 | # Symbolication related 37 | app.*.symbols 38 | 39 | # Obfuscation related 40 | app.*.map.json 41 | 42 | # Android Studio will place build artifacts here 43 | /android/app/debug 44 | /android/app/profile 45 | /android/app/release 46 | 47 | # ignore my custom file 48 | android/ 49 | ios/ 50 | linux/ 51 | macos/ 52 | web/ 53 | windows/ 54 | test/ 55 | pubspec.lock 56 | my/ 57 | -------------------------------------------------------------------------------- /.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: "ea121f8859e4b13e47a8f845e4586164519588bc" 8 | channel: "stable" 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: ea121f8859e4b13e47a8f845e4586164519588bc 17 | base_revision: ea121f8859e4b13e47a8f845e4586164519588bc 18 | - platform: android 19 | create_revision: ea121f8859e4b13e47a8f845e4586164519588bc 20 | base_revision: ea121f8859e4b13e47a8f845e4586164519588bc 21 | - platform: ios 22 | create_revision: ea121f8859e4b13e47a8f845e4586164519588bc 23 | base_revision: ea121f8859e4b13e47a8f845e4586164519588bc 24 | - platform: linux 25 | create_revision: ea121f8859e4b13e47a8f845e4586164519588bc 26 | base_revision: ea121f8859e4b13e47a8f845e4586164519588bc 27 | - platform: macos 28 | create_revision: ea121f8859e4b13e47a8f845e4586164519588bc 29 | base_revision: ea121f8859e4b13e47a8f845e4586164519588bc 30 | - platform: web 31 | create_revision: ea121f8859e4b13e47a8f845e4586164519588bc 32 | base_revision: ea121f8859e4b13e47a8f845e4586164519588bc 33 | - platform: windows 34 | create_revision: ea121f8859e4b13e47a8f845e4586164519588bc 35 | base_revision: ea121f8859e4b13e47a8f845e4586164519588bc 36 | 37 | # User provided section 38 | 39 | # List of Local paths (relative to this file) that should be 40 | # ignored by the migrate tool. 41 | # 42 | # Files that are not part of the templates will be ignored by default. 43 | unmanaged_files: 44 | - 'lib/main.dart' 45 | - 'ios/Runner.xcodeproj/project.pbxproj' 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright Xphost 2025 PCL-Newer-Project 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![logo](assets/logo2.png) 2 | 3 | # PCL II Newer Project 4 | 5 | 一个使用Flutter写的PCL II Newer Project! 6 | 7 | ## 使用说明 8 | 9 | 目前并没有提供二进制下载,因为还没做好( 10 | 11 | 不过你可以自行download zip然后自行构建~ 12 | 13 | 在构建之前,你必须去[Flutter SDK](https://docs.flutter.cn/release/archive)下载Flutter的开发文件,之后为你所需要构建的平台提供一个环境【例如MacOS的dmg构建、Linux的tar.gz构建等】 14 | 15 | 由于我开源的工作空间里缺少所有的运行时环境【仅提供源码】,那这样的话,你恐怕只能首先使用`flutter create pcl_newer`构建一个名为`pcl_newer`的一个项目,然后把我的项目源码放进入即可。 16 | 17 | 之后,请在项目根目录里面输入`Flutter run`即可开始预览你的应用程序。如果你想打包成exe或者dmg,你可以输入以下指令`flutter build --help`,这个指令可以让你查看Flutter的构建命令,你可以自行选择构建到windows、macos或者linux。 18 | 19 | 目前,第一个可用的版本预计在2026年7月份发布! 20 | 21 | 如果你想讨论Newer的任何事情,欢迎加群:435225858 22 | 23 | ## 鸣谢人员 24 | 25 | 1. 非常感谢[IceFire_Icer](https://space.bilibili.com/393107707)提供的Logo! -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at https://dart.dev/lints. 17 | # 18 | # Instead of disabling a lint rule for the entire project in the 19 | # section below, it can also be suppressed for a single line of code 20 | # or a specific dart file by using the `// ignore: name_of_lint` and 21 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 22 | # producing the lint. 23 | rules: 24 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 25 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 26 | 27 | # Additional information about this file can be found at 28 | # https://dart.dev/guides/language/analysis-options 29 | -------------------------------------------------------------------------------- /assets/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PCL-Community/PCL2-Newer-Project/987b5ac0129fdba29a5c7f2d407689e96c1c8e38/assets/app_icon.ico -------------------------------------------------------------------------------- /assets/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PCL-Community/PCL2-Newer-Project/987b5ac0129fdba29a5c7f2d407689e96c1c8e38/assets/app_icon_1024.png -------------------------------------------------------------------------------- /assets/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PCL-Community/PCL2-Newer-Project/987b5ac0129fdba29a5c7f2d407689e96c1c8e38/assets/app_icon_128.png -------------------------------------------------------------------------------- /assets/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PCL-Community/PCL2-Newer-Project/987b5ac0129fdba29a5c7f2d407689e96c1c8e38/assets/app_icon_16.png -------------------------------------------------------------------------------- /assets/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PCL-Community/PCL2-Newer-Project/987b5ac0129fdba29a5c7f2d407689e96c1c8e38/assets/app_icon_256.png -------------------------------------------------------------------------------- /assets/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PCL-Community/PCL2-Newer-Project/987b5ac0129fdba29a5c7f2d407689e96c1c8e38/assets/app_icon_32.png -------------------------------------------------------------------------------- /assets/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PCL-Community/PCL2-Newer-Project/987b5ac0129fdba29a5c7f2d407689e96c1c8e38/assets/app_icon_512.png -------------------------------------------------------------------------------- /assets/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PCL-Community/PCL2-Newer-Project/987b5ac0129fdba29a5c7f2d407689e96c1c8e38/assets/app_icon_64.png -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PCL-Community/PCL2-Newer-Project/987b5ac0129fdba29a5c7f2d407689e96c1c8e38/assets/logo.png -------------------------------------------------------------------------------- /assets/logo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PCL-Community/PCL2-Newer-Project/987b5ac0129fdba29a5c7f2d407689e96c1c8e38/assets/logo2.png -------------------------------------------------------------------------------- /assets/logo3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PCL-Community/PCL2-Newer-Project/987b5ac0129fdba29a5c7f2d407689e96c1c8e38/assets/logo3.png -------------------------------------------------------------------------------- /fonts/PCL-English.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PCL-Community/PCL2-Newer-Project/987b5ac0129fdba29a5c7f2d407689e96c1c8e38/fonts/PCL-English.ttf -------------------------------------------------------------------------------- /lib/components/button/my_check_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pcl2_newer/logic/change_body.dart'; 3 | 4 | class MyCheckButton extends StatefulWidget { 5 | final bool isChecked; 6 | final IconData iconData; 7 | final String text; 8 | final int index; 9 | final Function onPressed; 10 | const MyCheckButton({ 11 | super.key, 12 | this.isChecked = false, 13 | required this.iconData, 14 | required this.text, 15 | required this.index, 16 | required this.onPressed, 17 | }); 18 | 19 | @override 20 | State createState() => _MyCheckButtonState(); 21 | } 22 | 23 | class _MyCheckButtonState extends State { 24 | bool isHovered = false; 25 | @override 26 | Widget build(BuildContext context) { 27 | final backColor = isHovered 28 | ? darkModeListen.value 29 | ? Color.fromARGB(255, 32, 32, 32) 30 | : Color.fromARGB(255, 222, 237, 252) 31 | : Colors.transparent; 32 | final activeBackColor = Color.fromARGB(255, 18, 122, 225); 33 | final fontColor = widget.isChecked 34 | ? Colors.white 35 | : darkModeListen.value 36 | ? Colors.white 37 | : Color.fromARGB(255, 20, 123, 225); 38 | return MouseRegion( 39 | onHover: (event) => setState(() => isHovered = true), 40 | onExit: (event) => setState(() => isHovered = false), 41 | child: AnimatedContainer( 42 | height: 28, 43 | width: 80, 44 | duration: Duration(milliseconds: 200), 45 | margin: EdgeInsets.fromLTRB(4, 0, 4, 0), 46 | decoration: BoxDecoration( 47 | borderRadius: BorderRadius.circular(30), 48 | color: widget.isChecked ? activeBackColor : backColor, 49 | ), 50 | child: TextButton( 51 | onPressed: () => widget.onPressed(), 52 | style: ButtonStyle( 53 | mouseCursor: WidgetStateProperty.all( 54 | widget.isChecked 55 | ? SystemMouseCursors.basic 56 | : SystemMouseCursors.click, 57 | ), 58 | splashFactory: NoSplash.splashFactory, 59 | overlayColor: WidgetStateProperty.all(Colors.transparent), 60 | ), 61 | child: Row( 62 | mainAxisAlignment: MainAxisAlignment.center, 63 | children: [ 64 | Icon( 65 | widget.iconData, 66 | color: fontColor, 67 | size: 16, 68 | ), 69 | Text( 70 | " ${widget.text}", 71 | style: TextStyle( 72 | fontSize: 14, 73 | color: fontColor, 74 | ), 75 | ), 76 | ], 77 | ), 78 | ), 79 | )); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /lib/components/button/my_normal_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pcl2_newer/logic/change_body.dart'; 3 | 4 | class MyNormalButton extends StatefulWidget { 5 | final VoidCallback onClick; 6 | final double width; 7 | final double height; 8 | final Widget child; 9 | final int duration; 10 | const MyNormalButton({ 11 | super.key, 12 | required this.onClick, 13 | required this.width, 14 | required this.height, 15 | required this.child, 16 | this.duration = 100, 17 | }); 18 | @override 19 | State createState() => _MyNormalButtonState(); 20 | } 21 | 22 | class _MyNormalButtonState extends State { 23 | bool _isHovered = false; 24 | bool _isPressed = false; 25 | @override 26 | Widget build(BuildContext c) { 27 | return MouseRegion( 28 | cursor: SystemMouseCursors.click, 29 | onHover: (_) => setState(() => _isHovered = true), 30 | onExit: (_) => setState(() => _isHovered = false), 31 | child: GestureDetector( 32 | onTapDown: (_) => setState(() => _isPressed = true), 33 | onTapUp: (_) => setState(() => _isPressed = false), 34 | onTapCancel: () => setState(() => _isPressed = false), 35 | onTap: () => widget.onClick(), 36 | child: AnimatedContainer( 37 | decoration: BoxDecoration( 38 | border: Border.all(color: Color.fromARGB(255, 51, 62, 72)), 39 | borderRadius: BorderRadius.all(Radius.circular(10)), 40 | color: _isHovered 41 | ? darkModeListen.value 42 | ? Color.fromARGB(220, 69, 75, 79) 43 | : Color.fromARGB(220, 222, 236, 253) 44 | : darkModeListen.value 45 | ? Color.fromARGB(220, 58, 58, 59) 46 | : Color.fromARGB(220, 245, 248, 252), 47 | ), 48 | duration: Duration(milliseconds: widget.duration), 49 | width: widget.width, 50 | height: widget.height, 51 | transform: Matrix4.identity() 52 | ..translate(widget.width / 2, widget.height / 2) 53 | ..scale(_isPressed ? 0.98 : 1.0) 54 | ..translate(-widget.width / 2, -widget.height / 2), 55 | child: Center( 56 | child: widget.child, 57 | ), 58 | ), 59 | ), 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/components/input/my_normal_label.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class MyTextInput extends StatefulWidget { 4 | final String text; 5 | final Color? color; 6 | const MyTextInput({super.key, required this.text, this.color}); 7 | @override 8 | State createState() => _MyTextInputState(); 9 | } 10 | 11 | class _MyTextInputState extends State { 12 | @override 13 | Widget build(BuildContext context) { 14 | return Text(""); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/components/input/my_text_input.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | class MyTextInput extends StatefulWidget { 3 | const MyTextInput({super.key}); 4 | @override 5 | State createState() => _MyTextInputState(); 6 | } 7 | class _MyTextInputState extends State { 8 | @override 9 | Widget build(BuildContext context) { 10 | return Container(); 11 | } 12 | } -------------------------------------------------------------------------------- /lib/components/other/transition_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class TransitionPage extends StatefulWidget { 4 | final bool visible; 5 | final Widget child; 6 | final VoidCallback onAfterLeave; 7 | final Offset offset; 8 | final double opacity; 9 | final double scale; 10 | 11 | const TransitionPage({ 12 | super.key, 13 | this.visible = true, 14 | required this.child, 15 | required this.onAfterLeave, 16 | this.offset = const Offset(0, 0), 17 | this.opacity = 1, 18 | this.scale = 1, 19 | }); 20 | 21 | @override 22 | State createState() => _TransitionPageState(); 23 | } 24 | 25 | class _TransitionPageState extends State 26 | with SingleTickerProviderStateMixin { 27 | late AnimationController _controller; 28 | late Animation _slide; 29 | late Animation _opacity; 30 | late Animation _scale; 31 | bool _visible = false; 32 | @override 33 | void initState() { 34 | super.initState(); 35 | _controller = AnimationController( 36 | vsync: this, 37 | duration: const Duration(milliseconds: 200), 38 | ); 39 | _controller.addStatusListener((status) { 40 | if (status.isDismissed && !widget.visible) { 41 | setState(() => _visible = false); 42 | widget.onAfterLeave(); 43 | } 44 | }); 45 | final curved = CurvedAnimation( 46 | parent: _controller, 47 | curve: Curves.easeInOut, // 统一缓动曲线 48 | ); 49 | _slide = Tween( 50 | begin: widget.offset, 51 | end: Offset.zero, 52 | ).animate(curved); 53 | _opacity = Tween( 54 | begin: widget.opacity, 55 | end: 1, 56 | ).animate(curved); 57 | _scale = Tween( 58 | begin: widget.scale, 59 | end: 1, 60 | ).animate(curved); 61 | _visible = widget.visible; 62 | if (_visible) { 63 | _controller.forward(); 64 | } 65 | } 66 | 67 | @override 68 | void didUpdateWidget(TransitionPage oldWidget) { 69 | super.didUpdateWidget(oldWidget); 70 | if (widget.visible != oldWidget.visible) { 71 | if (widget.visible) { 72 | setState(() => _visible = true); 73 | _controller.forward(); 74 | } else { 75 | _controller.reverse(); 76 | } 77 | } 78 | } 79 | 80 | @override 81 | void dispose() { 82 | _controller.dispose(); 83 | super.dispose(); 84 | } 85 | 86 | @override 87 | Widget build(BuildContext context) { 88 | if (!_visible) { 89 | return const SizedBox.shrink(); 90 | } 91 | return AnimatedBuilder( 92 | animation: _controller, 93 | builder: (context, child) { 94 | return Transform( 95 | transform: Matrix4.identity() 96 | ..translate( 97 | _slide.value.dx, 98 | _slide.value.dy, 99 | 0, 100 | ) 101 | ..scale(_scale.value), 102 | child: Opacity( 103 | opacity: _opacity.value, 104 | child: child, 105 | ), 106 | ); 107 | }, 108 | child: widget.child, 109 | ); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /lib/logic/change_body.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | final ValueNotifier darkModeListen = ValueNotifier(false); 5 | final ValueNotifier currentPageListen = ValueNotifier(1); 6 | final ValueNotifier currentAccountListen = ValueNotifier(1); 7 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pcl2_newer/view/body.dart'; 3 | import 'package:pcl2_newer/view/nav_bar.dart'; 4 | import 'package:pcl2_newer/logic/change_body.dart'; 5 | // ignore: depend_on_referenced_packages 6 | import 'package:window_manager/window_manager.dart'; 7 | 8 | void main() async { 9 | WidgetsFlutterBinding.ensureInitialized(); 10 | await windowManager.ensureInitialized(); 11 | WindowOptions windowOptions = WindowOptions( 12 | size: Size(874, 520), 13 | center: true, 14 | titleBarStyle: TitleBarStyle.hidden, 15 | minimumSize: Size(784, 440), 16 | windowButtonVisibility: false, 17 | ); 18 | windowManager.waitUntilReadyToShow(windowOptions, () async { 19 | await windowManager.show(); 20 | await windowManager.focus(); 21 | }); 22 | runApp(const MyApp()); 23 | } 24 | 25 | class MyApp extends StatelessWidget { 26 | const MyApp({super.key}); 27 | @override 28 | Widget build(BuildContext context) { 29 | return MaterialApp( 30 | title: 'PCL II Newer Project', 31 | theme: ThemeData( 32 | colorScheme: ColorScheme.fromSeed( 33 | seedColor: darkModeListen.value 34 | ? Color.fromARGB(255, 7, 20, 35) 35 | : Color.fromARGB(255, 203, 224, 247), 36 | ), 37 | useMaterial3: true, 38 | fontFamily: "PCL-English"), 39 | home: const MyHomePage(), 40 | ); 41 | } 42 | } 43 | 44 | class MyHomePage extends StatefulWidget { 45 | const MyHomePage({super.key}); 46 | 47 | @override 48 | State createState() => _MyHomePageState(); 49 | } 50 | 51 | class _MyHomePageState extends State with WindowListener { 52 | bool isDarkMode = false; 53 | void updateDark() { 54 | setState(() { 55 | isDarkMode = darkModeListen.value; 56 | }); 57 | } 58 | 59 | @override 60 | void initState() { 61 | super.initState(); 62 | darkModeListen.addListener(updateDark); 63 | } 64 | 65 | @override 66 | void dispose() { 67 | darkModeListen.removeListener(updateDark); 68 | super.dispose(); 69 | } 70 | 71 | @override 72 | Widget build(BuildContext context) { 73 | return Scaffold( 74 | backgroundColor: Colors.transparent, 75 | appBar: NavBar(), 76 | body: AnimatedContainer( 77 | duration: const Duration(milliseconds: 300), 78 | color: isDarkMode 79 | ? Color.fromARGB(255, 7, 20, 35) 80 | : Color.fromARGB(255, 203, 224, 247), 81 | child: Body(), 82 | ), 83 | ); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /lib/view/about/left.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class AboutLeft extends StatefulWidget { 4 | const AboutLeft({super.key}); 5 | 6 | @override 7 | State createState() => _AboutLeftState(); 8 | } 9 | 10 | class _AboutLeftState extends State { 11 | @override 12 | Widget build(BuildContext context) { 13 | return Container(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/view/body.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pcl2_newer/logic/change_body.dart'; 3 | import 'package:pcl2_newer/components/other/transition_page.dart'; 4 | import 'package:pcl2_newer/view/about/left.dart'; 5 | import 'package:pcl2_newer/view/download/left.dart'; 6 | import 'package:pcl2_newer/view/home/left.dart'; 7 | import 'package:pcl2_newer/view/online/left.dart'; 8 | import 'package:pcl2_newer/view/setting/left.dart'; 9 | 10 | class Body extends StatefulWidget { 11 | const Body({super.key}); 12 | 13 | @override 14 | State createState() => _BodyState(); 15 | } 16 | 17 | class _BodyState extends State { 18 | int currentPage = 1; 19 | int forwardPage = 1; 20 | bool isDarkMode = false; 21 | void updateListen() { 22 | setState(() { 23 | currentPage = 0; 24 | forwardPage = currentPageListen.value; 25 | }); 26 | } 27 | 28 | void updateDark() { 29 | setState(() { 30 | isDarkMode = darkModeListen.value; 31 | }); 32 | } 33 | 34 | @override 35 | void initState() { 36 | super.initState(); 37 | currentPageListen.addListener(updateListen); 38 | darkModeListen.addListener(updateDark); 39 | } 40 | 41 | @override 42 | void dispose() { 43 | currentPageListen.removeListener(updateListen); 44 | darkModeListen.removeListener(updateDark); 45 | super.dispose(); 46 | } 47 | 48 | @override 49 | Widget build(BuildContext context) { 50 | final screenSize = MediaQuery.of(context).size; 51 | return Stack( 52 | children: [ 53 | TransitionPage( 54 | visible: currentPage == 1, 55 | onAfterLeave: () => setState(() => currentPage = forwardPage), 56 | offset: Offset(-300, 0), 57 | opacity: 0, 58 | child: AnimatedContainer( 59 | duration: Duration(milliseconds: 300), 60 | width: 300, 61 | height: double.infinity, 62 | color: isDarkMode 63 | ? Color.fromARGB(255, 41, 41, 43) 64 | : Color.fromARGB(255, 245, 248, 252), 65 | child: HomeLeft(), 66 | ), 67 | ), 68 | TransitionPage( 69 | visible: currentPage == 1, 70 | onAfterLeave: () => setState(() => currentPage = forwardPage), 71 | offset: Offset(0, -screenSize.height), 72 | opacity: 0, 73 | child: AnimatedContainer( 74 | duration: Duration(milliseconds: 300), 75 | margin: EdgeInsets.fromLTRB(300, 0, 0, 0), 76 | width: screenSize.width - 300, 77 | height: double.infinity, 78 | color: Colors.transparent, 79 | ), 80 | ), 81 | TransitionPage( 82 | visible: currentPage == 2, 83 | onAfterLeave: () => setState(() => currentPage = forwardPage), 84 | offset: Offset(-150, 0), 85 | opacity: 0, 86 | child: AnimatedContainer( 87 | duration: Duration(milliseconds: 300), 88 | width: 150, 89 | height: double.infinity, 90 | color: isDarkMode 91 | ? Color.fromARGB(255, 41, 41, 43) 92 | : Color.fromARGB(255, 245, 248, 252), 93 | child: DownloadLeft(), 94 | ), 95 | ), 96 | TransitionPage( 97 | visible: currentPage == 2, 98 | onAfterLeave: () => setState(() => currentPage = forwardPage), 99 | offset: Offset(0, -screenSize.height), 100 | opacity: 0, 101 | child: AnimatedContainer( 102 | duration: Duration(milliseconds: 300), 103 | margin: EdgeInsets.fromLTRB(150, 0, 0, 0), 104 | width: screenSize.width - 150, 105 | height: double.infinity, 106 | color: Colors.transparent, 107 | ), 108 | ), 109 | TransitionPage( 110 | visible: currentPage == 3, 111 | onAfterLeave: () => setState(() => currentPage = forwardPage), 112 | offset: Offset(-122, 0), 113 | opacity: 0, 114 | child: AnimatedContainer( 115 | duration: Duration(milliseconds: 300), 116 | width: 122, 117 | height: double.infinity, 118 | color: isDarkMode 119 | ? Color.fromARGB(255, 41, 41, 43) 120 | : Color.fromARGB(255, 245, 248, 252), 121 | child: OnlineLeft(), 122 | ), 123 | ), 124 | TransitionPage( 125 | visible: currentPage == 3, 126 | onAfterLeave: () => setState(() => currentPage = forwardPage), 127 | offset: Offset(0, -screenSize.height), 128 | opacity: 0, 129 | child: AnimatedContainer( 130 | duration: Duration(milliseconds: 300), 131 | margin: EdgeInsets.fromLTRB(122, 0, 0, 0), 132 | width: screenSize.width - 122, 133 | height: double.infinity, 134 | color: Colors.transparent, 135 | ), 136 | ), 137 | TransitionPage( 138 | visible: currentPage == 4, 139 | onAfterLeave: () => setState(() => currentPage = forwardPage), 140 | offset: Offset(-122, 0), 141 | opacity: 0, 142 | child: AnimatedContainer( 143 | duration: Duration(milliseconds: 300), 144 | width: 122, 145 | height: double.infinity, 146 | color: isDarkMode 147 | ? Color.fromARGB(255, 41, 41, 43) 148 | : Color.fromARGB(255, 245, 248, 252), 149 | child: SettingLeft(), 150 | ), 151 | ), 152 | TransitionPage( 153 | visible: currentPage == 4, 154 | onAfterLeave: () => setState(() => currentPage = forwardPage), 155 | offset: Offset(0, -screenSize.height), 156 | opacity: 0, 157 | child: AnimatedContainer( 158 | duration: Duration(milliseconds: 300), 159 | margin: EdgeInsets.fromLTRB(122, 0, 0, 0), 160 | width: screenSize.width - 122, 161 | height: double.infinity, 162 | color: Colors.transparent, 163 | ), 164 | ), 165 | TransitionPage( 166 | visible: currentPage == 5, 167 | onAfterLeave: () => setState(() => currentPage = forwardPage), 168 | offset: Offset(-150, 0), 169 | opacity: 0, 170 | child: AnimatedContainer( 171 | duration: Duration(milliseconds: 300), 172 | width: 150, 173 | height: double.infinity, 174 | color: isDarkMode 175 | ? Color.fromARGB(255, 41, 41, 43) 176 | : Color.fromARGB(255, 245, 248, 252), 177 | child: AboutLeft(), 178 | ), 179 | ), 180 | TransitionPage( 181 | visible: currentPage == 5, 182 | onAfterLeave: () => setState(() => currentPage = forwardPage), 183 | offset: Offset(0, -screenSize.height), 184 | opacity: 0, 185 | child: AnimatedContainer( 186 | duration: Duration(milliseconds: 300), 187 | margin: EdgeInsets.fromLTRB(150, 0, 0, 0), 188 | width: screenSize.width - 150, 189 | height: double.infinity, 190 | color: Colors.transparent, 191 | ), 192 | ), 193 | ], 194 | ); 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /lib/view/download/left.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class DownloadLeft extends StatefulWidget { 4 | const DownloadLeft({super.key}); 5 | 6 | @override 7 | State createState() => _DownloadLeftState(); 8 | } 9 | 10 | class _DownloadLeftState extends State { 11 | @override 12 | Widget build(BuildContext context) { 13 | return Container(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/view/home/content/microsoft.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pcl2_newer/components/button/my_normal_button.dart'; 3 | 4 | class Microsoft extends StatefulWidget { 5 | const Microsoft({super.key}); 6 | @override 7 | State createState() => _MicrosoftState(); 8 | } 9 | 10 | class _MicrosoftState extends State { 11 | @override 12 | Widget build(BuildContext context) { 13 | return Center( 14 | child: Column( 15 | children: [ 16 | Text("微软登录"), 17 | MyNormalButton( 18 | onClick: () {}, 19 | width: 200, 20 | height: 50, 21 | child: Text("点我登录"), 22 | ), 23 | ], 24 | ), 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/view/home/content/offline.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pcl2_newer/logic/change_body.dart'; 3 | 4 | class Offline extends StatefulWidget { 5 | const Offline({super.key}); 6 | @override 7 | State createState() => _OfflineState(); 8 | } 9 | 10 | class _OfflineState extends State { 11 | @override 12 | Widget build(BuildContext context) { 13 | return Center( 14 | child: Column( 15 | children: [ 16 | ElevatedButton( 17 | onPressed: () => 18 | setState(() => darkModeListen.value = !darkModeListen.value), 19 | child: Text("切换深色"), 20 | ), 21 | ], 22 | ), 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/view/home/content/thirdparty.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | class Thirdparty extends StatefulWidget { 3 | const Thirdparty({super.key}); 4 | @override 5 | State createState() => _ThirdpartyState(); 6 | } 7 | class _ThirdpartyState extends State { 8 | @override 9 | Widget build(BuildContext context) { 10 | return Container( 11 | color: Colors.purple, 12 | ); 13 | } 14 | } -------------------------------------------------------------------------------- /lib/view/home/left.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pcl2_newer/components/button/my_check_button.dart'; 3 | // ignore: unused_import 4 | import 'package:pcl2_newer/components/button/my_normal_button.dart'; 5 | import 'package:pcl2_newer/components/other/transition_page.dart'; 6 | import 'package:pcl2_newer/logic/change_body.dart'; 7 | import 'package:pcl2_newer/view/home/content/microsoft.dart'; 8 | import 'package:pcl2_newer/view/home/content/offline.dart'; 9 | import 'package:pcl2_newer/view/home/content/thirdparty.dart'; 10 | 11 | class HomeLeft extends StatefulWidget { 12 | const HomeLeft({super.key}); 13 | 14 | @override 15 | State createState() => _HomeLeftState(); 16 | } 17 | 18 | class _HomeLeftState extends State { 19 | int currentAccount = 1; 20 | int forwardAccount = 1; 21 | void updateListen() { 22 | setState(() { 23 | currentAccount = 0; 24 | forwardAccount = currentAccountListen.value; 25 | }); 26 | } 27 | 28 | @override 29 | void initState() { 30 | super.initState(); 31 | currentAccountListen.addListener(updateListen); 32 | currentAccount = currentAccountListen.value; 33 | } 34 | 35 | @override 36 | void dispose() { 37 | currentAccountListen.removeListener(updateListen); 38 | super.dispose(); 39 | } 40 | 41 | @override 42 | Widget build(BuildContext context) { 43 | return Column( 44 | children: [ 45 | SizedBox( 46 | height: 50, 47 | width: 300, 48 | child: Align( 49 | alignment: Alignment.bottomCenter, 50 | child: Row( 51 | mainAxisAlignment: MainAxisAlignment.center, 52 | children: [ 53 | MyCheckButton( 54 | iconData: Icons.shield_moon, 55 | text: "正版", 56 | index: 1, 57 | onPressed: () => currentAccountListen.value = 1, 58 | isChecked: currentAccount == 1, 59 | ), 60 | MyCheckButton( 61 | iconData: Icons.medication_outlined, 62 | text: "离线", 63 | index: 2, 64 | onPressed: () => currentAccountListen.value = 2, 65 | isChecked: currentAccount == 2, 66 | ), 67 | MyCheckButton( 68 | iconData: Icons.account_box_rounded, 69 | text: "外置", 70 | index: 3, 71 | onPressed: () => currentAccountListen.value = 3, 72 | isChecked: currentAccount == 3, 73 | ), 74 | ], 75 | ), 76 | ), 77 | ), 78 | Expanded( 79 | child: Column( 80 | children: [ 81 | SizedBox( 82 | height: 150, 83 | width: 300, 84 | child: Stack( 85 | children: [ 86 | TransitionPage( 87 | visible: currentAccount == 1, 88 | onAfterLeave: () => 89 | setState(() => currentAccount = forwardAccount), 90 | opacity: 0, 91 | child: Microsoft(), 92 | ), 93 | TransitionPage( 94 | visible: currentAccount == 2, 95 | onAfterLeave: () => 96 | setState(() => currentAccount = forwardAccount), 97 | opacity: 0, 98 | child: Offline(), 99 | ), 100 | TransitionPage( 101 | visible: currentAccount == 3, 102 | onAfterLeave: () => 103 | setState(() => currentAccount = forwardAccount), 104 | opacity: 0, 105 | child: Thirdparty(), 106 | ), 107 | ], 108 | ), 109 | ), 110 | ], 111 | ), 112 | // child: Center( 113 | // child: MyNormalButton( 114 | // width: 120, 115 | // height: 120, 116 | // onClick: () {}, 117 | // child: Text("HelloWorld!"), 118 | // ), 119 | // ), 120 | ), 121 | Container( 122 | width: 300, 123 | height: 120, 124 | color: Colors.amber, 125 | ), 126 | ], 127 | ); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /lib/view/nav_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | // ignore: depend_on_referenced_packages 3 | import 'package:window_manager/window_manager.dart'; 4 | import 'package:pcl2_newer/logic/change_body.dart'; 5 | 6 | class _Navbutton extends StatefulWidget { 7 | final String buttonText; 8 | final IconData iconData; 9 | final int index; 10 | final Function onPressed; 11 | 12 | const _Navbutton( 13 | {required this.index, 14 | required this.buttonText, 15 | required this.iconData, 16 | required this.onPressed}); 17 | @override 18 | State<_Navbutton> createState() => _NavbuttonState(); 19 | } 20 | 21 | class _NavbuttonState extends State<_Navbutton> { 22 | int currentPage = 1; 23 | bool isDarkMode = false; 24 | 25 | void updateListen() { 26 | setState(() { 27 | currentPage = currentPageListen.value; 28 | }); 29 | } 30 | 31 | void updateDark() { 32 | setState(() { 33 | isDarkMode = darkModeListen.value; 34 | }); 35 | } 36 | 37 | @override 38 | void initState() { 39 | super.initState(); 40 | currentPageListen.addListener(updateListen); 41 | darkModeListen.addListener(updateDark); 42 | } 43 | 44 | @override 45 | void dispose() { 46 | currentPageListen.removeListener(updateListen); 47 | darkModeListen.removeListener(updateDark); 48 | super.dispose(); 49 | } 50 | 51 | bool isHovered = false; 52 | @override 53 | Widget build(BuildContext context) { 54 | final backColor = isHovered 55 | ? Color.fromARGB(255, 64, 144, 227) 56 | : Color.fromARGB(255, 17, 111, 205); 57 | final activeBackColor = 58 | isDarkMode ? Color.fromARGB(255, 10, 10, 10) : Colors.white; 59 | final fontColor = widget.index == currentPage 60 | ? isDarkMode 61 | ? Colors.white 62 | : Color.fromARGB(255, 64, 144, 227) 63 | : Colors.white; 64 | return MouseRegion( 65 | onHover: (event) => setState(() => isHovered = true), 66 | onExit: (event) => setState(() => isHovered = false), 67 | child: AnimatedContainer( 68 | height: 28, 69 | width: 80, 70 | duration: Duration(milliseconds: 200), 71 | margin: EdgeInsets.fromLTRB(4, 0, 4, 0), 72 | decoration: BoxDecoration( 73 | borderRadius: BorderRadius.circular(30), 74 | color: widget.index == currentPage ? activeBackColor : backColor, 75 | ), 76 | child: TextButton( 77 | onPressed: () => widget.onPressed(), 78 | style: ButtonStyle( 79 | mouseCursor: WidgetStateProperty.all( 80 | widget.index == currentPage 81 | ? SystemMouseCursors.basic 82 | : SystemMouseCursors.click, 83 | ), 84 | splashFactory: NoSplash.splashFactory, 85 | overlayColor: WidgetStateProperty.all(Colors.transparent), 86 | ), 87 | child: Row( 88 | mainAxisAlignment: MainAxisAlignment.center, 89 | children: [ 90 | Icon( 91 | widget.iconData, 92 | color: fontColor, 93 | size: 16, 94 | ), 95 | Text( 96 | " ${widget.buttonText}", 97 | style: TextStyle( 98 | fontSize: 14, 99 | color: fontColor, 100 | ), 101 | ), 102 | ], 103 | ), 104 | ), 105 | ), 106 | ); 107 | } 108 | } 109 | 110 | class NavBar extends StatefulWidget implements PreferredSizeWidget { 111 | final double toolbarHeight; 112 | const NavBar({super.key, this.toolbarHeight = 48}); 113 | @override 114 | State createState() => _NavBarState(); 115 | @override 116 | Size get preferredSize => Size.fromHeight(toolbarHeight); // 设置高度 117 | } 118 | 119 | class _NavBarState extends State with WindowListener { 120 | Future _toggleMaximize() async { 121 | final isMaximized = await windowManager.isMaximized(); 122 | if (isMaximized) { 123 | await windowManager.unmaximize(); 124 | } else { 125 | await windowManager.maximize(); 126 | } 127 | } 128 | 129 | @override 130 | Widget build(BuildContext context) { 131 | return AppBar( 132 | toolbarHeight: widget.toolbarHeight, 133 | backgroundColor: Color.fromARGB(255, 17, 111, 205), 134 | title: SizedBox( 135 | width: double.infinity, 136 | height: widget.toolbarHeight, 137 | child: Stack( 138 | children: [ 139 | Positioned( 140 | top: 0, 141 | left: 0, 142 | right: 0, 143 | bottom: 0, 144 | child: GestureDetector( 145 | behavior: HitTestBehavior.opaque, 146 | onPanStart: (_) => windowManager.startDragging(), 147 | onDoubleTap: () => _toggleMaximize(), 148 | ), 149 | ), 150 | Positioned( 151 | top: 0, 152 | left: 0, 153 | right: 0, 154 | bottom: 0, 155 | child: Row( 156 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 157 | children: [ 158 | Container( 159 | alignment: Alignment.centerLeft, 160 | width: 100, 161 | height: widget.toolbarHeight, 162 | child: Text( 163 | "PCL II", 164 | style: TextStyle( 165 | fontSize: 20, 166 | color: Colors.white, 167 | ), 168 | ), 169 | ), 170 | Expanded( 171 | child: Row( 172 | mainAxisAlignment: MainAxisAlignment.center, 173 | children: [ 174 | _Navbutton( 175 | index: 1, 176 | buttonText: "主页", 177 | iconData: Icons.home, 178 | onPressed: () => currentPageListen.value = 1, 179 | ), 180 | _Navbutton( 181 | index: 2, 182 | buttonText: "下载", 183 | iconData: Icons.download, 184 | onPressed: () => currentPageListen.value = 2, 185 | ), 186 | _Navbutton( 187 | index: 3, 188 | buttonText: "联机", 189 | iconData: Icons.wifi, 190 | onPressed: () => currentPageListen.value = 3, 191 | ), 192 | _Navbutton( 193 | index: 4, 194 | buttonText: "设置", 195 | iconData: Icons.settings, 196 | onPressed: () => currentPageListen.value = 4, 197 | ), 198 | _Navbutton( 199 | index: 5, 200 | buttonText: "更多", 201 | iconData: Icons.grid_view, 202 | onPressed: () => currentPageListen.value = 5, 203 | ), 204 | ], 205 | ), 206 | ), 207 | Container( 208 | alignment: Alignment.centerRight, 209 | width: 100, 210 | height: widget.toolbarHeight, 211 | child: Row( 212 | mainAxisAlignment: MainAxisAlignment.end, 213 | children: [ 214 | IconButton( 215 | onPressed: () { 216 | windowManager.minimize(); 217 | }, 218 | icon: Icon( 219 | Icons.horizontal_rule, 220 | color: Colors.white, 221 | ), 222 | ), 223 | IconButton( 224 | onPressed: () { 225 | windowManager.close(); 226 | }, 227 | icon: Icon( 228 | Icons.close, 229 | color: Colors.white, 230 | ), 231 | ), 232 | ], 233 | ), 234 | ), 235 | ], 236 | ), 237 | ), 238 | ], 239 | ), 240 | ), 241 | ); 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /lib/view/online/left.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class OnlineLeft extends StatefulWidget { 4 | const OnlineLeft({super.key}); 5 | 6 | @override 7 | State createState() => _OnlineLeftState(); 8 | } 9 | 10 | class _OnlineLeftState extends State { 11 | @override 12 | Widget build(BuildContext context) { 13 | return Container(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/view/setting/left.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class SettingLeft extends StatefulWidget { 4 | const SettingLeft({super.key}); 5 | 6 | @override 7 | State createState() => _SettingLeftState(); 8 | } 9 | 10 | class _SettingLeftState extends State { 11 | @override 12 | Widget build(BuildContext context) { 13 | return Container(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: pcl2_newer 2 | description: "A new Flutter project." 3 | # The following line prevents the package from being accidentally published to 4 | # pub.dev using `flutter pub publish`. This is preferred for private packages. 5 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 6 | 7 | # The following defines the version and build number for your application. 8 | # A version number is three numbers separated by dots, like 1.2.43 9 | # followed by an optional build number separated by a +. 10 | # Both the version and the builder number may be overridden in flutter 11 | # build by specifying --build-name and --build-number, respectively. 12 | # In Android, build-name is used as versionName while build-number used as versionCode. 13 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 14 | # In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. 15 | # Read more about iOS versioning at 16 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 17 | # In Windows, build-name is used as the major, minor, and patch parts 18 | # of the product and file versions while build-number is used as the build suffix. 19 | version: 1.0.0+1 20 | 21 | environment: 22 | sdk: ^3.6.2 23 | 24 | # Dependencies specify other packages that your package needs in order to work. 25 | # To automatically upgrade your package dependencies to the latest versions 26 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 27 | # dependencies can be manually updated by changing the version numbers below to 28 | # the latest version available on pub.dev. To see which dependencies have newer 29 | # versions available, run `flutter pub outdated`. 30 | dependencies: 31 | flutter: 32 | sdk: flutter 33 | 34 | # The following adds the Cupertino Icons font to your application. 35 | # Use with the CupertinoIcons class for iOS style icons. 36 | cupertino_icons: ^1.0.8 37 | 38 | dev_dependencies: 39 | flutter_test: 40 | sdk: flutter 41 | 42 | # The "flutter_lints" package below contains a set of recommended lints to 43 | # encourage good coding practices. The lint set provided by the package is 44 | # activated in the `analysis_options.yaml` file located at the root of your 45 | # package. See that file for information about deactivating specific lint 46 | # rules and activating additional ones. 47 | flutter_lints: ^5.0.0 48 | window_manager: ^0.4.2 49 | 50 | # For information on the generic Dart part of this file, see the 51 | # following page: https://dart.dev/tools/pub/pubspec 52 | 53 | # The following section is specific to Flutter packages. 54 | flutter: 55 | 56 | # The following line ensures that the Material Icons font is 57 | # included with your application, so that you can use the icons in 58 | # the material Icons class. 59 | uses-material-design: true 60 | 61 | # To add assets to your application, add an assets section, like this: 62 | # assets: 63 | # - images/a_dot_burr.jpeg 64 | # - images/a_dot_ham.jpeg 65 | 66 | # An image asset can refer to one or more resolution-specific "variants", see 67 | # https://flutter.dev/to/resolution-aware-images 68 | 69 | # For details regarding adding assets from package dependencies, see 70 | # https://flutter.dev/to/asset-from-package 71 | 72 | # To add custom fonts to your application, add a fonts section here, 73 | # in this "flutter" section. Each entry in this list should have a 74 | # "family" key with the font family name, and a "fonts" key with a 75 | # list giving the asset and other descriptors for the font. For 76 | # example: 77 | fonts: 78 | - family: PCL-English 79 | fonts: 80 | - asset: "fonts/PCL-English.ttf" 81 | # fonts: 82 | # - family: Schyler 83 | # fonts: 84 | # - asset: fonts/Schyler-Regular.ttf 85 | # - asset: fonts/Schyler-Italic.ttf 86 | # style: italic 87 | # - family: Trajan Pro 88 | # fonts: 89 | # - asset: fonts/TrajanPro.ttf 90 | # - asset: fonts/TrajanPro_Bold.ttf 91 | # weight: 700 92 | # 93 | # For details regarding fonts from package dependencies, 94 | # see https://flutter.dev/to/font-from-package 95 | --------------------------------------------------------------------------------