├── .gitignore
├── .metadata
├── .vscode
└── launch.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── android
├── app
│ └── src
│ │ └── main
│ │ └── java
│ │ └── io
│ │ └── flutter
│ │ └── plugins
│ │ └── GeneratedPluginRegistrant.java
└── local.properties
├── assets
├── fonts
│ └── CoolUI.ttf
└── images
│ └── loading.png
├── cool_ui.iml
├── documents
├── custom_keyboard.md
├── images
│ ├── custom_keyboard.gif
│ ├── popover_demo.gif
│ └── toast_demo.gif
├── popover.md
└── weui_toast.md
├── example
├── .gitignore
├── .metadata
├── README.md
├── analysis_options.yaml
├── android
│ ├── .gitignore
│ ├── app
│ │ ├── build.gradle
│ │ └── src
│ │ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── kotlin
│ │ │ │ └── com
│ │ │ │ │ └── example
│ │ │ │ │ └── example
│ │ │ │ │ └── MainActivity.kt
│ │ │ └── res
│ │ │ │ ├── drawable-v21
│ │ │ │ └── launch_background.xml
│ │ │ │ ├── drawable
│ │ │ │ └── launch_background.xml
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── values-night
│ │ │ │ └── styles.xml
│ │ │ │ └── values
│ │ │ │ └── styles.xml
│ │ │ └── profile
│ │ │ └── AndroidManifest.xml
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ └── gradle-wrapper.properties
│ └── settings.gradle
├── assets
│ └── fonts
│ │ └── CoolUIExample.ttf
├── lib
│ ├── cool_u_i_example_icons.dart
│ ├── keyboards
│ │ └── test_keyboard.dart
│ ├── main.dart
│ └── pages
│ │ ├── custom_keyboard.dart
│ │ ├── paint_event_demo.dart
│ │ ├── popover_demo.dart
│ │ ├── table_demo.dart
│ │ └── weui_toast_demo.dart
├── pubspec.yaml
├── test
│ └── widget_test.dart
└── windows
│ ├── .gitignore
│ ├── CMakeLists.txt
│ ├── flutter
│ ├── CMakeLists.txt
│ ├── generated_plugin_registrant.cc
│ ├── generated_plugin_registrant.h
│ └── generated_plugins.cmake
│ └── runner
│ ├── CMakeLists.txt
│ ├── Runner.rc
│ ├── flutter_window.cpp
│ ├── flutter_window.h
│ ├── main.cpp
│ ├── resource.h
│ ├── resources
│ └── app_icon.ico
│ ├── runner.exe.manifest
│ ├── utils.cpp
│ ├── utils.h
│ ├── win32_window.cpp
│ └── win32_window.h
├── ios
├── Flutter
│ └── flutter_export_environment.sh
└── Runner
│ ├── GeneratedPluginRegistrant.h
│ └── GeneratedPluginRegistrant.m
├── lib
├── cool_ui.dart
├── dialogs
│ └── weui_toast.dart
├── icons
│ └── cool_ui_icons.dart
├── keyboards
│ ├── keyboard_controller.dart
│ ├── keyboard_manager.dart
│ ├── keyboard_media_query.dart
│ ├── keyboard_root.dart
│ ├── mocks
│ │ ├── mock_binary_messenger.dart
│ │ └── mock_binding.dart
│ └── number_keyboard.dart
├── utils
│ ├── screen_util.dart
│ ├── scroll_utils.dart
│ └── widget_util.dart
└── widgets
│ ├── popover
│ ├── cupertino_popover.dart
│ └── cupertino_popover_menu_item.dart
│ ├── tables
│ └── table.dart
│ └── utils
│ └── paint_event.dart
├── pub.bat
├── pubspec.lock
├── pubspec.yaml
└── test
└── cool_ui_test.dart
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .dart_tool/
3 |
4 | .packages
5 | .pub/
6 |
7 | build/
8 | ios/.generated/
9 | ios/Flutter/Generated.xcconfig
10 | ios/Runner/GeneratedPluginRegistrant.*
11 | /.idea
12 |
--------------------------------------------------------------------------------
/.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: 2f630446040b9f5b65fbfff9a76be1fe01c45e99
8 | channel: master
9 |
10 | project_type: package
11 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // 使用 IntelliSense 了解相关属性。
3 | // 悬停以查看现有属性的描述。
4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 |
8 | {
9 | "name": "Flutter",
10 | "request": "launch",
11 | "type": "dart"
12 | }
13 | ]
14 | }
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [1.2.0]
2 | * TODO: 修复了flutter3.0后WidgetsBinding.instance无法判断是否为空导致报错
3 |
4 | ## [1.1.0]
5 | * TODO: 修复了flutter2.5后删除了setMockMessageHandler导致无法使用的问题
6 |
7 | ## [1.0.2]
8 | * TODO: 修复收起虚拟键盘报错
9 | * TODO: 修复因空安全导致的报错
10 |
11 | ## [1.0.1]
12 | * TODO: 修复警告错误
13 |
14 | ## [1.0.0]
15 | * TODO: 升级至flutter 2.0
16 | * TODO: 处理了空安全
17 |
18 | ## [0.6.3]
19 | * TODO: 指定back_button_interceptor依赖
20 |
21 | ## [0.6.2]
22 | * TODO: 指定back_button_interceptor依赖
23 |
24 | ## [0.6.1]
25 | * TODO: CupertinoPopoverDirection添加了left和right
26 |
27 | ## [0.6.0]
28 | * TODO: 重构了键盘弹出的方式,修复长按输入框报错问题
29 |
30 | ## [0.5.5]
31 | * TODO: Popover添加了阴影效果
32 | * TODO: Popover 添加了方向
33 |
34 | ## [0.5.4]
35 | * TODO: Fix Bug Issues:47
36 |
37 | ## [0.5.3]
38 | * TODO: 修复键盘有时候不会收起来的问题
39 |
40 | ## [0.5.0]
41 | * TODO: 修复Flutter1.17版本后无法在打开第二个自定义键盘不显示的问题
42 |
43 | ## [0.4.1]
44 | * TODO: 修复了键盘高度从小变大的时候无法改变的问题, issues: 36
45 | * TODO: 键盘添加了传参功能,可从外部传参到键盘内部
46 |
47 | ## [0.3.3]
48 | * TODO: 修复了键盘高度不同的时候不会改变问题, issues: 36
49 |
50 | ## [0.3.2]
51 | * TODO: 修复页面高度未刷新问题
52 |
53 |
54 | ## [0.3.1]
55 | * TODO: 修复了键盘监听高度未释放的问题
56 |
57 | ## [0.3.0]
58 | * TODO: 优化了键盘的使用方式
59 |
60 | ## [0.2.2]
61 | * TODO: 修复了有默认值时,键盘无法输入数据的问题
62 |
63 | ## [0.2.1]
64 | * TODO: 修复了多个自定义键盘无法切换的问题
65 |
66 | ## [0.2.0]
67 | * TODO: 优化了WeuiToast的效果,添加了WeuiToastConfig,可以全局配置默认设置
68 | * TODO: 添加了Keyboard对android 返回按钮的监听
69 |
70 | ## [0.1.16]
71 | * Updated SDK constraint to support new error message formats
72 | * Updated error message formats
73 |
74 | ## [0.1.14] - TODO:修复了自定义键盘切换回原生键盘无法输入的问题
75 | * 修复了自定义键盘切换回原生键盘无法输入的问题
76 | * Popover可以设置遮罩层颜色
77 |
78 | ## [0.1.13] - TODO:修复hide后pop无效的问题
79 |
80 | ## [0.1.8] - TODO:完善了键盘的文档
81 | * 完善了键盘的文档
82 | * 完善了可发送的TextInputAction的类型
83 |
84 | ## [0.1.7] - TODO:添加了自定义键盘
85 | * TODO:添加了自定义键盘
86 | * TODO:修改了下文档结构
87 |
88 | ## [0.1.6] - TODO:修改了Toast的位置计算方式
89 | * TODO: 修改了Toast的位置计算方式
90 | * TODO: 开放了Toast的位置设置
91 | * TODO: 修改了README.md文档的大小
92 |
93 | ## [0.1.5] - TODO:添加了popoverConstraints参数
94 | * TODO: CupertionPopoverButton添加了CupertionPopoverButton参数,用于设置最大最小宽度,取消必填的高宽
95 |
96 | ## [0.1.4] - TODO:修改了onTap事件
97 | * TODO: CupertionPopoverButton添加了onTap事件,返回True不打开Popover
98 | * TODO: CupertinoPopoverMenuItem修改了onTap事件,返回True不关闭Popover
99 |
100 | ## [0.1.1] - TODO:添加了WeuiToast效果,完善了文档信息
101 | * TODO: 添加了WeuiToast效果
102 | * TODO: 完善了文档信息
103 | * TODO: 添加了PaintEvent事件,具体请看Demo
104 |
105 | ## [0.1.0] - TODO: 改进了CupertionPopover和添加了CupertinoPopoverMenuItem
106 |
107 | * TODO: 改进了CupertionPopover箭头的位置
108 | * TODO: 改进了CupertinoPopoverMenuItem按下的动画,并且添加了onTap
109 |
110 | ## [0.0.9] - TODO: 添加了一些控件,改进了CupertionPopover
111 |
112 | * TODO: 添加了CupertinoPopoverMenuList
113 | * TODO: 添加了CupertinoPopoverMenuItem
114 | * TODO: 修改了CupertionPopover动画,
115 |
116 | ## [0.0.1] - TODO: Add release date.
117 |
118 | * TODO: Describe initial release.
119 |
120 |
--------------------------------------------------------------------------------
/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 [yyyy] [name of copyright owner]
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.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # cool_ui [](https://pub.dartlang.org/packages/cool_ui)
2 |
3 | 用flutter实现一些我认为好看的UI控件
4 | 希望大家提一些觉得不错的控件,我自己一个人想有时候想到的比较有限
5 |
6 |
7 | Usage Add this to your package's pubspec.yaml file:
8 |
9 | Flutter >=3.3
10 | ``` yaml
11 | dependencies:
12 | cool_ui: "^1.3.0"
13 | ```
14 |
15 | Flutter >=3.0
16 | ``` yaml
17 | dependencies:
18 | cool_ui: "^1.2.0"
19 | ```
20 |
21 | Flutter >=2.5
22 | ``` yaml
23 | dependencies:
24 | cool_ui: "^1.1.0"
25 | ```
26 |
27 |
28 | Flutter >=2.0
29 | ``` yaml
30 | dependencies:
31 | cool_ui: "^1.0.2"
32 | ```
33 |
34 | Flutter >=1.17
35 | ``` yaml
36 | dependencies:
37 | cool_ui: "^0.6.1"
38 | ```
39 |
40 | Flutter >=1.7
41 | ``` yaml
42 | dependencies:
43 | cool_ui: "^0.4.1"
44 | ```
45 |
46 | Flutter < 1.7
47 | ``` yaml
48 | dependencies:
49 | cool_ui: "0.1.15"
50 | ```
51 |
52 |
53 | # 控件
54 |
55 | - [CupertinoPopoverButton](documents/popover.md#CupertinoPopoverButton)
56 | - [CupertinoPopoverMenuList](documents/popover.md#CupertinoPopoverMenuList)
57 | - [CupertinoPopoverMenuItem](documents/popover.md#CupertinoPopoverMenuItem)
58 | - [showWeuiToast](documents/weui_toast.md#showWeuiToast)
59 | - [showWeuiSuccessToast](documents/weui_toast.md#showWeuiSuccessToast)
60 | - [showWeuiLoadingToast](documents/weui_toast.md#showWeuiLoadingToast)
61 |
62 |
63 | # 自定义键盘(Customize Keyboard)
64 |
65 | - [Get started](documents/custom_keyboard.md#Step1)
66 | - [KeyboardController](documents/custom_keyboard.md#KeyboardController)
67 |
--------------------------------------------------------------------------------
/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java:
--------------------------------------------------------------------------------
1 | package io.flutter.plugins;
2 |
3 | import io.flutter.plugin.common.PluginRegistry;
4 |
5 | /**
6 | * Generated file. Do not edit.
7 | */
8 | public final class GeneratedPluginRegistrant {
9 | public static void registerWith(PluginRegistry registry) {
10 | if (alreadyRegisteredWith(registry)) {
11 | return;
12 | }
13 | }
14 |
15 | private static boolean alreadyRegisteredWith(PluginRegistry registry) {
16 | final String key = GeneratedPluginRegistrant.class.getCanonicalName();
17 | if (registry.hasPlugin(key)) {
18 | return true;
19 | }
20 | registry.registrarFor(key);
21 | return false;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/android/local.properties:
--------------------------------------------------------------------------------
1 | sdk.dir=D:\\Android\\SDK
2 | flutter.sdk=D:\\flutter
--------------------------------------------------------------------------------
/assets/fonts/CoolUI.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Im-Kevin/cool_ui/6b5864fec5dda333a4ad5a54dadeeff08383101e/assets/fonts/CoolUI.ttf
--------------------------------------------------------------------------------
/assets/images/loading.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Im-Kevin/cool_ui/6b5864fec5dda333a4ad5a54dadeeff08383101e/assets/images/loading.png
--------------------------------------------------------------------------------
/cool_ui.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/documents/custom_keyboard.md:
--------------------------------------------------------------------------------
1 | # 自定义键盘使用方法快速入门
2 |
3 | ## 效果
4 |
5 |
6 |
7 | ## Flutter 2.5后添加的步骤
8 | 替换runApp为runMockApp
9 | ```dart
10 | void main() {
11 | // runApp(MyApp()); // 旧的
12 | runMockApp(KeyboardRootWidget(child: MyApp())); // 新的
13 | }
14 | ```
15 |
16 |
17 | ## Step1
18 | 编写个性化的键盘
19 |
20 | ```dart
21 | class NumberKeyboard extends StatelessWidget{
22 | static const CKTextInputType inputType = const CKTextInputType(name:'CKNumberKeyboard'); //定义InputType类型
23 | static double getHeight(BuildContext ctx){ //编写获取高度的方法
24 | ...
25 | }
26 |
27 |
28 | final KeyboardController controller ; //用于控制键盘输出的Controller
29 | const NumberKeyboard({this.controller});
30 |
31 | static register(){ //注册键盘的方法
32 | CoolKeyboard.addKeyboard(NumberKeyboard.inputType,KeyboardConfig(builder: (context,controller, params){ // 可通过CKTextInputType传参数到键盘内部
33 | return NumberKeyboard(controller: controller);
34 | },getHeight: NumberKeyboard.getHeight));
35 | }
36 |
37 | @override
38 | Widget build(BuildContext context) { //键盘的具体内容
39 | ...
40 | return Container( //例子
41 | color: Colors.white,
42 | child: GestureDetector(
43 | behavior: HitTestBehavior.translucent,
44 | child: Center(child: Text('1'),),
45 | onTap: (){
46 | controller.addText('1'); 往输入框光标位置添加一个1
47 | },
48 | ),
49 | )
50 | }
51 | }
52 | ```
53 |
54 | ## Step2
55 | 注册键盘,并且添加了KeyboardRootWidget
56 |
57 | ```dart
58 | void main(){
59 | NumberKeyboard.register(); //注册键盘
60 | runApp(KeyboardRootWidget(child: MyApp())); //添加了KeyboardRootWidget
61 | }
62 | ```
63 |
64 | ## Step3
65 | 给需要使用自定义键盘的页面添加以下代码
66 |
67 | ```dart
68 | @override
69 | Widget build(BuildContext context) {
70 | return KeyboardMediaQuery(//用于键盘弹出的时候页面可以滚动到输入框的位置
71 | child: Page
72 | );
73 | }
74 | ```
75 |
76 |
77 | ## Step4
78 | 具体使用
79 | ```dart
80 | TextField(
81 | ...
82 | keyboardType: NumberKeyboard.inputType, 像平常一样设置键盘输入类型一样将Step1编写的inputType传递进去
83 | ...
84 | )
85 | ```
86 |
87 | # KeyboardController
88 |
89 |
90 | - [deleteOne](#deleteOne)
91 | - [addText](#addText)
92 | - [doneAction](#doneAction)
93 | - [nextAction](#nextAction)
94 | - [sendPerformAction](#sendPerformAction)
95 |
96 |
97 | ### deleteOne()
98 | 删除一个字符,一般用于键盘的删除键
99 |
100 | ### addText(String insertText)
101 | 在光标位置添加文字,一般用于键盘输入
102 |
103 | ### doneAction()
104 | 触发键盘的完成Action
105 |
106 | ### nextAction()
107 | 触发键盘的下一项Action
108 |
109 | ### newLineAction()
110 | 触发键盘的换行Action
111 |
112 | ### sendPerformAction(TextInputAction action)
113 | ///发送其他Action
114 |
115 |
116 | ## 特别问题讲解:
117 | 1. 报错: CoolKeyboard can only be used in MockBinding
118 | 答: 可能是因为调用了WidgetsFlutterBinding.ensureInitialized()导致的,请将WidgetsFlutterBinding.ensureInitialized() 替换成 MockBinding.ensureInitialized()
119 |
--------------------------------------------------------------------------------
/documents/images/custom_keyboard.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Im-Kevin/cool_ui/6b5864fec5dda333a4ad5a54dadeeff08383101e/documents/images/custom_keyboard.gif
--------------------------------------------------------------------------------
/documents/images/popover_demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Im-Kevin/cool_ui/6b5864fec5dda333a4ad5a54dadeeff08383101e/documents/images/popover_demo.gif
--------------------------------------------------------------------------------
/documents/images/toast_demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Im-Kevin/cool_ui/6b5864fec5dda333a4ad5a54dadeeff08383101e/documents/images/toast_demo.gif
--------------------------------------------------------------------------------
/documents/popover.md:
--------------------------------------------------------------------------------
1 |
2 | ## CupertinoPopoverButton
3 | 仿iOS的UIPopover效果的
4 |
5 | 用于弹窗的按钮
6 | ```dart
7 | CupertinoPopoverButton({
8 | this.child,
9 | this.popoverBuild,
10 | this.popoverColor=Colors.white,
11 | this.popoverBoxShadow,
12 | @required this.popoverWidth,
13 | @required this.popoverHeight,
14 | BoxConstraints popoverConstraints,
15 | this.direction = CupertinoPopoverDirection.bottom,
16 | this.onTap,
17 | this.transitionDuration=const Duration(milliseconds: 200),
18 | this.barrierColor = Colors.black54,
19 | this.radius=8.0});
20 | ```
21 |
22 |
23 | | Param | Type | Default | Description |
24 | | --- | --- | --- | --- |
25 | | child | Widget
| | 按钮的内容 |
26 | | popoverBuild | WidgetBuilder
| | 生成弹出框的内容 |
27 | | [popoverWidth] | double
| | 弹出框的宽度 |
28 | | [popoverHeight] | double
| | 弹出框的高度 |
29 | | [popoverConstraints] | BoxConstraints
| maxHeight:123.0 maxWidth:150.0 | 弹出框的最大最小高宽|
30 | | [direction] | CupertinoPopoverDirection
| CupertinoPopoverDirection.bottom | 方向|
31 | | [onTap] | BoolCallback
| | 按钮点击事件,返回true取消默认反应(不打开Popover) |
32 | | [popoverColor] | Color
| 白色 | 弹出框的背景颜色 |
33 | | [popoverBoxShadow] | BoxShadow
| | 弹出框的阴影 |
34 | | [barrierColor] | Color
| Colors.black54 | 遮罩层的颜色,目前不允许设置透明,如需要透明则使用Color.fromRGBO(0, 0, 0, 0.01)可达到类似效果|
35 | | [transitionDuration] | Duration
| 0.2s | 过度动画时间 |
36 | | [radius] | double
| 8.0 | 弹出框的圆角弧度 |
37 |
38 |
39 | **Example**
40 |
41 | ```dart
42 | CupertinoPopoverButton(
43 | child: Container(
44 | margin: EdgeInsets.all(20.0),
45 | width: 80.0,
46 | height: 40.0,
47 | decoration: BoxDecoration(
48 | color: Colors.white,
49 | borderRadius: BorderRadius.all(Radius.circular(5.0)),
50 | boxShadow: [BoxShadow(color: Colors.black12,blurRadius: 5.0)]
51 | ),
52 | child: Center(child:Text('左上角')),
53 | ),
54 | popoverBuild:(BuildContext context){
55 | return Container(
56 | width: 100.0,
57 | height: 100.0,
58 | child: Text('左上角内容'),
59 | )
60 | });
61 | ```
62 |
63 |
64 |
65 |
66 | ## CupertinoPopoverMenuList
67 | Popover弹出的菜单样式列表,一般与[CupertinoPopoverMenuItem](#CupertinoPopoverMenuItem)一起用,会给两个Item加间隔线
68 | ```dart
69 | CupertinoPopoverMenuList({this.children})
70 | ```
71 | | Param | Type | Description |
72 | | --- | --- | --- |
73 | | children | List
| 子元素,一般是CupertinoPopoverMenuItem |
74 |
75 |
76 | ## CupertinoPopoverMenuItem
77 | 单个菜单项
78 |
79 | ```dart
80 | const CupertinoPopoverMenuItem({
81 | this.leading,
82 | this.child,
83 | this.onTap,
84 | this.background = Colors.white,
85 | this.activeBackground = const Color(0xFFd9d9d9),
86 | this.isTapClosePopover=true
87 | });
88 | ```
89 | | Param | Type | Default | Description |
90 | | --- | --- | --- | --- |
91 | | [leading] | Widget
| 菜单左边,一般放图标 |
92 | | [child] | Widget
| 菜单内容 |
93 | | [onTap] | BoolCallback
| | 按钮点击事件,返回true取消默认反应(不关闭Popover) |
94 | | [activeBackground] | Color
| Color(0xFFd9d9d9) | 按下时背景色 |
95 | | [background] | Color
| Colors.white | 默认背景色 |
96 | | [isTapClosePopover] | bool
| 是否点击关闭 |
97 |
98 | #### 案例核心代码
99 | ```dart
100 | CupertinoPopoverMenuList(
101 | children: [
102 | CupertinoPopoverMenuItem(leading: Icon(Icons.add),child: Text("新增"),),
103 | CupertinoPopoverMenuItem(leading: Icon(Icons.edit),child: Text("修改"),),
104 | CupertinoPopoverMenuItem(leading: Icon(Icons.delete),child: Text("删除"),)
105 | ],
106 | )
107 | ```
108 |
109 |
--------------------------------------------------------------------------------
/documents/weui_toast.md:
--------------------------------------------------------------------------------
1 |
2 | ## showWeuiToast
3 | 仿Weui的Toast效果
4 | ```dart
5 | VoidCallback showWeuiToast({
6 | @required BuildContext context,
7 | @required Widget message,
8 | @required Widget icon,
9 | bool stopEvent = false,
10 | Alignment alignment,
11 | bool backButtonClose})
12 | ```
13 | | Param | Type | Default | Description |
14 | | --- | --- | --- | --- |
15 | | [context] | BuildContext
| | 上下文 |
16 | | [message] | Widget
| | 提示消息 |
17 | | [icon] | Widget
| | 图标 |
18 | | [stopEvent] | bool
| false | 阻止父页面事件触发 |
19 | | [alignment] | Alignment
| 默认是居中偏上 | Toast的位置 |
20 | | [backButtonClose] | bool
| | 安卓返回按钮是否关闭Toast |
21 |
22 | 返回参数:VoidCallback,用于关闭Toast
23 |
24 |
25 |
26 |
27 | ## showWeuiSuccessToast
28 | 仿Weui的SuccessToast效果
29 | ```dart
30 | Future showWeuiSuccessToast({
31 | @required BuildContext context,
32 | Widget message,
33 | bool stopEvent,
34 | bool backButtonClose,
35 | Alignment alignment,
36 | Duration closeDuration
37 | })
38 | ```
39 | | Param | Type | Default | Description |
40 | | --- | --- | --- | --- |
41 | | [context] | BuildContext
| | 上下文 |
42 | | [alignment] | Alignment
| 默认是居中偏上 | Toast的位置 |
43 | | [message] | Widget
| 成功| 提示消息 |
44 | | [stopEvent] | bool
| false | 阻止父页面事件触发 |
45 | | [closeDuration] | Duration
| 3s | 关闭时间 |
46 | | [backButtonClose] | bool
| true | 安卓返回按钮是否关闭Toast |
47 |
48 | 返回参数:Future dart 异步操作,代表Toast已关闭
49 |
50 |
51 | ## showWeuiLoadingToast
52 | 仿Weui的LoadingToast效果
53 | ```dart
54 | VoidCallback showWeuiToast({
55 | @required BuildContext context,
56 | Widget message,
57 | stopEvent = true,
58 | bool backButtonClose,
59 | Alignment alignment
60 | })
61 | ```
62 | | Param | Type | Default | Description |
63 | | --- | --- | --- | --- |
64 | | [context] | BuildContext
| | 上下文 |
65 | | [message] | Widget
| | 提示消息 |
66 | | [stopEvent] | bool
| true | 阻止父页面事件触发 |
67 | | [backButtonClose] | bool
| false | 安卓返回按钮是否关闭Toast |
68 | | [alignment] | Alignment
| 默认是居中偏上 | Toast的位置 |
69 |
70 | 返回参数:VoidCallback,用于关闭Toast
71 |
72 |
73 | ## WeuiToastConfigData
74 | 设置默认Toast效果
75 | ```dart
76 | const WeuiToastConfigData({this.successText = '成功',
77 | this.successDuration = const Duration(seconds: 3),
78 | this.successBackButtonClose = true,
79 | this.loadingText = '加载中',
80 | this.loadingBackButtonClose = false,
81 | this.toastAlignment = const Alignment(0.0, -0.2)});
82 | ```
83 |
84 | | Param | Type | Default | Description |
85 | | --- | --- | --- | --- |
86 | | [successText] | String
| 成功 | 成功提示消息 |
87 | | [successDuration] | Duration
|3s | 成功Toast关闭事件 |
88 | | [successBackButtonClose] | bool
| true | 成功安卓返回按钮是否关闭Toast |
89 | | [loadingText] | String
| 加载中 | 加载中提示消息 |
90 | | [loadingBackButtonClose] | false
| true | 加载中安卓返回按钮是否关闭Toast |
91 | | [alignment] | Alignment
| 默认是居中偏上 | Toast的位置 |
92 |
93 | ## WeuiToastConfig
94 | 设置默认Toast效果 配合WeuiToastConfigData使用
95 | ```dart
96 | WeuiToastConfig({Widget child,this.data})
97 | ```
98 | | Param | Type | Default | Description |
99 | | --- | --- | --- | --- |
100 | | [child] | Widget
| | Widget |
101 | | [data] | WeuiToastConfigData
| | Toast配置数据 |
102 | 在Widget中添加了WeuiToastConfig,然后子Widget的context就可以应用到效果了
103 | 例如:
104 | ```dart
105 | main.dart
106 | class MyApp extends StatelessWidget {
107 | // This widget is the root of your application.
108 | @override
109 | Widget build(BuildContext context) {
110 | return WeuiToastConfig( // --关键代码
111 | data: WeuiToastConfigData( // --关键代码
112 | successText: '测试ConfigData' // --关键代码
113 | ), // --关键代码
114 | child:MaterialApp(
115 | title: 'Flutter Demo',
116 | theme: ThemeData(
117 | primarySwatch: Colors.blue
118 | ),
119 | home: MyHomePage(title: 'Flutter Demo Home Page')
120 | ));
121 | }
122 | }
123 | ```
124 | 代表全局默认配置
125 |
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.lock
4 | *.log
5 | *.pyc
6 | *.swp
7 | .DS_Store
8 | .atom/
9 | .buildlog/
10 | .history
11 | .svn/
12 |
13 | # IntelliJ related
14 | *.iml
15 | *.ipr
16 | *.iws
17 | .idea/
18 |
19 | # Visual Studio Code related
20 | .vscode/
21 |
22 | # Flutter/Dart/Pub related
23 | **/doc/api/
24 | .dart_tool/
25 | .flutter-plugins
26 | .packages
27 | .pub-cache/
28 | .pub/
29 | build/
30 |
31 | # Android related
32 | **/android/**/gradle-wrapper.jar
33 | **/android/.gradle
34 | **/android/captures/
35 | **/android/gradlew
36 | **/android/gradlew.bat
37 | **/android/local.properties
38 | **/android/**/GeneratedPluginRegistrant.java
39 |
40 | # iOS/XCode related
41 | **/ios/**/*.mode1v3
42 | **/ios/**/*.mode2v3
43 | **/ios/**/*.moved-aside
44 | **/ios/**/*.pbxuser
45 | **/ios/**/*.perspectivev3
46 | **/ios/**/*sync/
47 | **/ios/**/.sconsign.dblite
48 | **/ios/**/.tags*
49 | **/ios/**/.vagrant/
50 | **/ios/**/DerivedData/
51 | **/ios/**/Icon?
52 | **/ios/**/Pods/
53 | **/ios/**/.symlinks/
54 | **/ios/**/profile
55 | **/ios/**/xcuserdata
56 | **/ios/.generated/
57 | **/ios/Flutter/App.framework
58 | **/ios/Flutter/Flutter.framework
59 | **/ios/Flutter/Generated.xcconfig
60 | **/ios/Flutter/app.flx
61 | **/ios/Flutter/app.zip
62 | **/ios/Flutter/flutter_assets/
63 | **/ios/ServiceDefinitions.json
64 | **/ios/Runner/GeneratedPluginRegistrant.*
65 |
66 | # Exceptions to above rules.
67 | !**/ios/**/default.mode1v3
68 | !**/ios/**/default.mode2v3
69 | !**/ios/**/default.pbxuser
70 | !**/ios/**/default.perspectivev3
71 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
72 |
--------------------------------------------------------------------------------
/example/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled.
5 |
6 | version:
7 | revision: ffccd96b62ee8cec7740dab303538c5fc26ac543
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: ffccd96b62ee8cec7740dab303538c5fc26ac543
17 | base_revision: ffccd96b62ee8cec7740dab303538c5fc26ac543
18 | - platform: windows
19 | create_revision: ffccd96b62ee8cec7740dab303538c5fc26ac543
20 | base_revision: ffccd96b62ee8cec7740dab303538c5fc26ac543
21 |
22 | # User provided section
23 |
24 | # List of Local paths (relative to this file) that should be
25 | # ignored by the migrate tool.
26 | #
27 | # Files that are not part of the templates will be ignored by default.
28 | unmanaged_files:
29 | - 'lib/main.dart'
30 | - 'ios/Runner.xcodeproj/project.pbxproj'
31 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # cool_ui_example
2 |
3 | Cool UI Example
4 |
5 | ## Getting Started
6 |
7 | For help getting started with Flutter, view our online
8 | [documentation](https://flutter.io/).
9 |
--------------------------------------------------------------------------------
/example/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | # This file configures the analyzer, which statically analyzes Dart code to
2 | # check for errors, warnings, and lints.
3 | #
4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled
5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
6 | # invoked from the command line by running `flutter analyze`.
7 |
8 | # The following line activates a set of recommended lints for Flutter apps,
9 | # packages, and plugins designed to encourage good coding practices.
10 | include: package:flutter_lints/flutter.yaml
11 |
12 | linter:
13 | # The lint rules applied to this project can be customized in the
14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml`
15 | # included above or to enable additional rules. A list of all available lints
16 | # and their documentation is published at
17 | # https://dart-lang.github.io/linter/lints/index.html.
18 | #
19 | # Instead of disabling a lint rule for the entire project in the
20 | # section below, it can also be suppressed for a single line of code
21 | # or a specific dart file by using the `// ignore: name_of_lint` and
22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file
23 | # producing the lint.
24 | rules:
25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule
26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
27 |
28 | # Additional information about this file can be found at
29 | # https://dart.dev/guides/language/analysis-options
30 |
--------------------------------------------------------------------------------
/example/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 | **/*.keystore
13 | **/*.jks
14 |
--------------------------------------------------------------------------------
/example/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply plugin: 'kotlin-android'
26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27 |
28 | android {
29 | compileSdkVersion flutter.compileSdkVersion
30 | ndkVersion flutter.ndkVersion
31 |
32 | compileOptions {
33 | sourceCompatibility JavaVersion.VERSION_1_8
34 | targetCompatibility JavaVersion.VERSION_1_8
35 | }
36 |
37 | kotlinOptions {
38 | jvmTarget = '1.8'
39 | }
40 |
41 | sourceSets {
42 | main.java.srcDirs += 'src/main/kotlin'
43 | }
44 |
45 | defaultConfig {
46 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
47 | applicationId "com.example.example"
48 | // You can update the following values to match your application needs.
49 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
50 | minSdkVersion flutter.minSdkVersion
51 | targetSdkVersion flutter.targetSdkVersion
52 | versionCode flutterVersionCode.toInteger()
53 | versionName flutterVersionName
54 | }
55 |
56 | buildTypes {
57 | release {
58 | // TODO: Add your own signing config for the release build.
59 | // Signing with the debug keys for now, so `flutter run --release` works.
60 | signingConfig signingConfigs.debug
61 | }
62 | }
63 | }
64 |
65 | flutter {
66 | source '../..'
67 | }
68 |
69 | dependencies {
70 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
71 | }
72 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
7 |
15 |
19 |
23 |
24 |
25 |
26 |
27 |
28 |
30 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.example
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Im-Kevin/cool_ui/6b5864fec5dda333a4ad5a54dadeeff08383101e/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Im-Kevin/cool_ui/6b5864fec5dda333a4ad5a54dadeeff08383101e/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Im-Kevin/cool_ui/6b5864fec5dda333a4ad5a54dadeeff08383101e/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Im-Kevin/cool_ui/6b5864fec5dda333a4ad5a54dadeeff08383101e/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Im-Kevin/cool_ui/6b5864fec5dda333a4ad5a54dadeeff08383101e/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/example/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.6.10'
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:7.1.2'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | mavenCentral()
18 | }
19 | }
20 |
21 | rootProject.buildDir = '../build'
22 | subprojects {
23 | project.buildDir = "${rootProject.buildDir}/${project.name}"
24 | }
25 | subprojects {
26 | project.evaluationDependsOn(':app')
27 | }
28 |
29 | task clean(type: Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/example/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.4-all.zip
7 |
--------------------------------------------------------------------------------
/example/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 |
--------------------------------------------------------------------------------
/example/assets/fonts/CoolUIExample.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Im-Kevin/cool_ui/6b5864fec5dda333a4ad5a54dadeeff08383101e/example/assets/fonts/CoolUIExample.ttf
--------------------------------------------------------------------------------
/example/lib/cool_u_i_example_icons.dart:
--------------------------------------------------------------------------------
1 | /// Flutter icons CoolUIExample
2 | /// Copyright (C) 2018 by original authors @ fluttericon.com, fontello.com
3 | /// This font was generated by FlutterIcon.com, which is derived from Fontello.
4 | ///
5 | /// To use this font, place it in your fonts/ directory and include the
6 | /// following in your pubspec.yaml
7 | ///
8 | /// flutter:
9 | /// fonts:
10 | /// - family: CoolUIExample
11 | /// fonts:
12 | /// - asset: fonts/CoolUIExample.ttf
13 | ///
14 | ///
15 | ///
16 | import 'package:flutter/widgets.dart';
17 |
18 | class CoolUIExampleIcon {
19 | CoolUIExampleIcon._();
20 |
21 | static const _kFontFam = 'CoolUIExample';
22 |
23 | static const IconData popover = const IconData(0xe801, fontFamily: _kFontFam);
24 | }
25 |
--------------------------------------------------------------------------------
/example/lib/keyboards/test_keyboard.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:cool_ui/cool_ui.dart';
3 | import 'package:flutter/material.dart';
4 |
5 | class TestKeyboard extends StatelessWidget{
6 | static const CKTextInputType inputType = const CKTextInputType(name:'CKTestKeyboard');
7 | static double getHeight(BuildContext ctx){
8 | MediaQueryData mediaQuery = MediaQuery.of(ctx);
9 | return mediaQuery.size.width / 3 / 2 * 2;
10 | }
11 | final KeyboardController controller ;
12 | const TestKeyboard({required this.controller});
13 |
14 | static register(){
15 | CoolKeyboard.addKeyboard(TestKeyboard.inputType,KeyboardConfig(builder: (context,controller, params){
16 | return TestKeyboard(controller: controller);
17 | },getHeight: TestKeyboard.getHeight));
18 | }
19 |
20 |
21 | @override
22 | Widget build(BuildContext context) {
23 | MediaQueryData mediaQuery = MediaQuery.of(context);
24 | return Material(
25 | child: DefaultTextStyle(style: TextStyle(fontWeight: FontWeight.w500,color: Colors.black,fontSize: 23.0), child: Container(
26 | height:getHeight(context),
27 | width: mediaQuery.size.width,
28 | decoration: BoxDecoration(
29 | color: Color(0xffafafaf),
30 | ),
31 | child: GridView.count(
32 | childAspectRatio: 2/1,
33 | mainAxisSpacing:0.5,
34 | crossAxisSpacing:0.5,
35 | padding: EdgeInsets.all(0.0),
36 | crossAxisCount: 3,
37 | children: [
38 | buildButton('A'),
39 | buildButton('B'),
40 | buildButton('C'),
41 | buildButton('D'),
42 | buildButton('E'),
43 | buildButton('F'),
44 | buildButton('G'),
45 | buildButton('H'),
46 | buildButton('J'),
47 | Container(
48 | color: Color(0xFFd3d6dd),
49 | child: GestureDetector(
50 | behavior: HitTestBehavior.translucent,
51 | child: Center(child: Icon(Icons.expand_more),),
52 | onTap: (){
53 | controller.doneAction();
54 | },
55 | ),
56 | ),
57 | buildButton('0'),
58 | Container(
59 | color: Color(0xFFd3d6dd),
60 | child: GestureDetector(
61 | behavior: HitTestBehavior.translucent,
62 | child: Center(child: Text('X'),),
63 | onTap: (){
64 | controller.deleteOne();
65 | },
66 | ),
67 | ),
68 | ]),
69 | )),
70 | );
71 | }
72 |
73 | Widget buildButton(String title,{String? value}){
74 | return Container(
75 | color: Colors.white,
76 | child: GestureDetector(
77 | behavior: HitTestBehavior.translucent,
78 | child: Center(child: Text(title),),
79 | onTap: (){
80 | controller.addText(value ?? title);
81 | },
82 | ),
83 | );
84 | }
85 | }
86 |
87 |
88 |
--------------------------------------------------------------------------------
/example/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:cool_ui_example/cool_u_i_example_icons.dart';
2 | import 'package:cool_ui_example/pages/custom_keyboard.dart';
3 | import 'package:cool_ui_example/pages/paint_event_demo.dart';
4 | import 'package:cool_ui_example/pages/popover_demo.dart';
5 | import 'package:cool_ui_example/pages/table_demo.dart';
6 | import 'package:cool_ui_example/pages/weui_toast_demo.dart';
7 | import 'package:cool_ui/cool_ui.dart';
8 | import 'package:flutter/material.dart';
9 |
10 | import 'keyboards/test_keyboard.dart';
11 |
12 | void main() {
13 | NumberKeyboard.register();
14 | TestKeyboard.register();
15 | runMockApp(KeyboardRootWidget(child: MyApp()));
16 | // runApp(MyApp());
17 | }
18 |
19 | class MyApp extends StatelessWidget {
20 | // This widget is the root of your application.
21 | @override
22 | Widget build(BuildContext context) {
23 | return WeuiToastConfig(
24 | data: WeuiToastConfigData(successText: '测试ConfigData'),
25 | child: MaterialApp(
26 | title: 'Flutter Demo',
27 | theme: ThemeData(
28 | // This is the theme of your application.
29 | //
30 | // Try running your application with "flutter run". You'll see the
31 | // application has a blue toolbar. Then, without quitting the app, try
32 | // changing the primarySwatch below to Colors.green and then invoke
33 | // "hot reload" (press "r" in the console where you ran "flutter run",
34 | // or press Run > Flutter Hot Reload in IntelliJ). Notice that the
35 | // counter didn't reset back to zero; the application is not restarted.
36 | primarySwatch: Colors.blue),
37 | home: MyHomePage(title: 'Flutter Demo Home Page')));
38 | }
39 | }
40 |
41 | class MyHomePage extends StatefulWidget {
42 | MyHomePage({Key? key, this.title = ''}) : super(key: key);
43 |
44 | // This widget is the home page of your application. It is stateful, meaning
45 | // that it has a State object (defined below) that contains fields that affect
46 | // how it looks.
47 |
48 | // This class is the configuration for the state. It holds the values (in this
49 | // case the title) provided by the parent (in this case the App widget) and
50 | // used by the build method of the State. Fields in a Widget subclass are
51 | // always marked "final".
52 |
53 | final String title;
54 |
55 | @override
56 | _MyHomePageState createState() => _MyHomePageState();
57 | }
58 |
59 | class _MyHomePageState extends State {
60 | int _counter = 0;
61 |
62 | void _incrementCounter() {
63 | setState(() {
64 | // This call to setState tells the Flutter framework that something has
65 | // changed in this State, which causes it to rerun the build method below
66 | // so that the display can reflect the updated values. If we changed
67 | // _counter without calling setState(), then the build method would not be
68 | // called again, and so nothing would appear to happen.
69 | _counter++;
70 | });
71 | }
72 |
73 | @override
74 | Widget build(BuildContext context) {
75 | // This method is rerun every time setState is called, for instance as done
76 | // by the _incrementCounter method above.
77 | //
78 | // The Flutter framework has been optimized to make rerunning build methods
79 | // fast, so that you can just rebuild anything that needs updating rather
80 | // than having to individually change instances of widgets.
81 | return Scaffold(
82 | appBar: AppBar(
83 | // Here we take the value from the MyHomePage object that was created by
84 | // the App.build method, and use it to set our appbar title.
85 | title: Text(widget.title),
86 | ),
87 | body: ListView(
88 | children: [
89 | ListTile(
90 | leading: Icon(CoolUIExampleIcon.popover),
91 | title: Text("Popover"),
92 | onTap: () {
93 | Navigator.of(context).push(
94 | MaterialPageRoute(builder: (context) => PopoverDemo()));
95 | },
96 | ),
97 | ListTile(
98 | title: Text("PaintEvent"),
99 | onTap: () {
100 | Navigator.of(context).push(
101 | MaterialPageRoute(builder: (context) => PaintEventDemo()));
102 | },
103 | ),
104 | ListTile(
105 | title: Text("WeuiToastEvent"),
106 | onTap: () {
107 | Navigator.of(context).push(
108 | MaterialPageRoute(builder: (context) => WeuiToastDemo()));
109 | },
110 | ),
111 | ListTile(
112 | title: Text("CustomKeyboardEvent"),
113 | onTap: () {
114 | Navigator.of(context).push(MaterialPageRoute(
115 | builder: (context) => CustomKeyboardDemo()));
116 | },
117 | ),
118 | ListTile(
119 | title: Text("TableEvent"),
120 | onTap: () {
121 | Navigator.of(context)
122 | .push(MaterialPageRoute(builder: (context) => TableDemo()));
123 | },
124 | )
125 | ],
126 | ));
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/example/lib/pages/custom_keyboard.dart:
--------------------------------------------------------------------------------
1 | import 'package:cool_ui_example/keyboards/test_keyboard.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter/cupertino.dart';
4 | import 'package:cool_ui/cool_ui.dart';
5 | import 'package:flutter/services.dart';
6 |
7 | class CustomKeyboardDemo extends StatefulWidget {
8 | @override
9 | State createState() {
10 | // TODO: implement createState
11 | return CustomKeyboardDemoState();
12 | }
13 | }
14 |
15 | class CustomKeyboardDemoState extends State {
16 | TextEditingController textEditingController =
17 | TextEditingController(text: 'test');
18 | TextEditingController textEditing2Controller =
19 | TextEditingController(text: 'test');
20 |
21 | @override
22 | Widget build(BuildContext context) {
23 | // TODO: implement build
24 | return KeyboardMediaQuery(child: Builder(builder: (ctx) {
25 | // CoolKeyboard.init(ctx);
26 | return Scaffold(
27 | appBar: AppBar(
28 | title: Text("Custom Keyboard Demo"),
29 | ),
30 | body: ListView(
31 | children: [
32 | TextField(
33 | controller: textEditingController,
34 | keyboardType: TextInputType.text,
35 | ),
36 | Container(
37 | height: 300,
38 | ),
39 | MaterialButton(
40 | child: Text('弹出功能演示'),
41 | onPressed: () {
42 | showInputDialogs(
43 | context: context,
44 | messageWidget: Text('弹出输入功能演示'),
45 | keyboardType: NumberKeyboard.inputType);
46 | },
47 | ),
48 | TextField(
49 | controller: textEditing2Controller,
50 | decoration: InputDecoration(labelText: '演示键盘弹出后滚动'),
51 | keyboardType: NumberKeyboard.inputType,
52 | ),
53 | TextField(
54 | decoration: InputDecoration(labelText: '多个键盘演示'),
55 | keyboardType: NumberKeyboard.inputType,
56 | )
57 | ],
58 | ));
59 | }));
60 | }
61 |
62 | static Future showInputDialogs(
63 | {required BuildContext context,
64 | Widget? titleWidget,
65 | Widget? messageWidget,
66 | List? inputFormatters,
67 | TextInputType keyboardType = TextInputType.number}) {
68 | String? value;
69 | return showCupertinoDialog(
70 | context: context,
71 | builder: (context) {
72 | // The minimum insets for contents of the Scaffold to keep visible.
73 | List children = [];
74 | if (messageWidget != null) {
75 | children.add(messageWidget);
76 | }
77 | children.add(Form(
78 | child: Container(
79 | padding: EdgeInsets.only(top: 10),
80 | child: Material(
81 | child: Column(
82 | children: [
83 | TextField(
84 | inputFormatters: inputFormatters,
85 | keyboardType: keyboardType,
86 | autofocus: true,
87 | onChanged: (newValue) {
88 | value = newValue;
89 | },
90 | ),
91 | TextField(
92 | inputFormatters: inputFormatters,
93 | keyboardType: TextInputType.text,
94 | onChanged: (newValue) {
95 | value = newValue;
96 | },
97 | ),
98 | TextField(
99 | inputFormatters: inputFormatters,
100 | keyboardType: keyboardType,
101 | onChanged: (newValue) {
102 | value = newValue;
103 | },
104 | )
105 | ],
106 | )))));
107 | return CupertinoAlertDialog(
108 | title: titleWidget,
109 | content: Column(
110 | children: children,
111 | ),
112 | actions: [
113 | CupertinoDialogAction(
114 | child: Text("取消"),
115 | onPressed: () => Navigator.of(context).pop(),
116 | ),
117 | CupertinoDialogAction(
118 | child: Text("確認"),
119 | onPressed: () {
120 | Navigator.of(context).pop(value ?? '');
121 | },
122 | )
123 | ],
124 | );
125 | });
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/example/lib/pages/paint_event_demo.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/cupertino.dart';
3 | import 'package:cool_ui/cool_ui.dart';
4 |
5 | class PaintEventDemo extends StatefulWidget {
6 | @override
7 | State createState() {
8 | // TODO: implement createState
9 | return PaintEventDemoState();
10 | }
11 | }
12 |
13 | class PaintEventDemoState extends State {
14 | bool isPaintBackgroud = false;
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 | // TODO: implement build
19 | return Scaffold(
20 | appBar: AppBar(
21 | title: Text("PaintEvent Demo"),
22 | ),
23 | body: Row(
24 | children: [
25 | MaterialButton(
26 | onPressed: () {
27 | setState(() {
28 | isPaintBackgroud = !isPaintBackgroud;
29 | });
30 | },
31 | child: Text(isPaintBackgroud ? "渲染前填充颜色" : "渲染后填充颜色")),
32 | PaintEvent(
33 | child: Text("子Widget文字"),
34 | paintAfter: (context, offset, size) {
35 | if (!isPaintBackgroud) {
36 | final Paint paint = Paint();
37 | paint.color = Colors.red;
38 | context.canvas.drawRect(offset & size, paint);
39 | }
40 | },
41 | paintBefore: (context, offset, size) {
42 | if (isPaintBackgroud) {
43 | final Paint paint = Paint();
44 | paint.color = Colors.red;
45 | context.canvas.drawRect(offset & size, paint);
46 | }
47 | },
48 | )
49 | ],
50 | ));
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/example/lib/pages/popover_demo.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/cupertino.dart';
3 | import 'package:cool_ui/cool_ui.dart';
4 |
5 |
6 | class PopoverDemo extends StatefulWidget{
7 | @override
8 | State createState() {
9 | // TODO: implement createState
10 | return PopoverDemoState();
11 | }
12 |
13 | }
14 |
15 | class PopoverDemoState extends State{
16 | @override
17 | Widget build(BuildContext context) {
18 | // TODO: implement build
19 | return Scaffold(
20 | appBar: AppBar(
21 | title: Text("Popover Demo"),
22 | ),
23 | body: Column(
24 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
25 | mainAxisSize: MainAxisSize.max,
26 | children: [
27 | Row(
28 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
29 | mainAxisSize: MainAxisSize.max,
30 | children: [
31 | _buildPopoverButton("左上角","左上角内容"),
32 | _buildPopoverButton("中间上方","中间上方内容"),
33 | _buildPopoverButton("右上角","右上角内容")
34 | ],
35 | ),
36 | Row(
37 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
38 | mainAxisSize: MainAxisSize.max,
39 | children:[
40 | _buildPopoverButton("中间左方","中间左方内容"),
41 | _buildPopoverButton("正中间","正中间内容"),
42 | _buildPopoverButton("中间左方","中间左方内容")
43 | ]
44 | ),
45 | Row(
46 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
47 | mainAxisSize: MainAxisSize.max,
48 | children: [
49 | _buildPopoverButton("左下角","左下角内容"),
50 | _buildPopoverButton("中间下方","中间下方内容"),
51 | _buildPopoverButton("右下角","右下角内容")
52 | ],
53 | )
54 | ],
55 | ),
56 | );
57 | }
58 |
59 | Widget _buildPopoverButton(String btnTitle,String bodyMessage){
60 | return Padding(
61 | padding: EdgeInsets.all(20.0),
62 | child:CupertinoPopoverButton(
63 | popoverBoxShadow: [
64 | BoxShadow(color: Colors.black12,blurRadius: 5.0)
65 | ],
66 | barrierColor: Color(0x01FFFFFF),
67 | child: Container(
68 | width: 80.0,
69 | height: 40.0,
70 | decoration: BoxDecoration(
71 | color: Colors.white,
72 | borderRadius: BorderRadius.all(Radius.circular(5.0)),
73 | boxShadow: [BoxShadow(color: Colors.black12,blurRadius: 5.0)]
74 | ),
75 | child: Center(child:Text(btnTitle)),
76 | ),
77 | popoverBuild: (context) {
78 | // return Text("satatastas");
79 | return CupertinoPopoverMenuList(
80 | children: [
81 | CupertinoPopoverMenuItem(leading: Icon(Icons.add),child: Text("新增"),),
82 | CupertinoPopoverMenuItem(leading: Icon(Icons.edit),child: Text("修改"),),
83 | CupertinoPopoverMenuItem(leading: Icon(Icons.delete),child: Text("删除"),)
84 | ],
85 | );
86 | })
87 |
88 | );
89 |
90 | }
91 | }
--------------------------------------------------------------------------------
/example/lib/pages/table_demo.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/cupertino.dart';
3 | import 'package:cool_ui/cool_ui.dart';
4 |
5 |
6 | class TableDemo extends StatefulWidget{
7 | @override
8 | State createState() {
9 | // TODO: implement createState
10 | return TableDemoState();
11 | }
12 |
13 | }
14 |
15 | class TableDemoState extends State{
16 |
17 |
18 | @override
19 | Widget build(BuildContext context) {
20 | // TODO: implement build
21 | return Scaffold(
22 | appBar: AppBar(
23 | title: Text("Table Demo"),
24 | ),
25 | body: CoolTable()
26 | );
27 | }
28 |
29 | }
--------------------------------------------------------------------------------
/example/lib/pages/weui_toast_demo.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/cupertino.dart';
3 | import 'package:cool_ui/cool_ui.dart';
4 |
5 | class WeuiToastDemo extends StatefulWidget {
6 | @override
7 | State createState() {
8 | // TODO: implement createState
9 | return WeuiToastDemoState();
10 | }
11 | }
12 |
13 | class WeuiToastDemoState extends State {
14 | bool isPaintBackgroud = false;
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 | // TODO: implement build
19 | return Scaffold(
20 | appBar: AppBar(
21 | title: Text("WeuiToast Demo"),
22 | ),
23 | body: ListView(
24 | padding: const EdgeInsets.symmetric(vertical: 24.0, horizontal: 72.0),
25 | children: [
26 | MaterialButton(
27 | color: Colors.blue[400],
28 | child: Text(
29 | "成功",
30 | style: TextStyle(color: Colors.white),
31 | ),
32 | onPressed: () => showWeuiSuccessToast(context: context),
33 | ),
34 | MaterialButton(
35 | color: Colors.blue[400],
36 | child: Text(
37 | "加载中",
38 | style: TextStyle(color: Colors.white),
39 | ),
40 | onPressed: () {
41 | var hide = showWeuiLoadingToast(context: context);
42 | Future.delayed(Duration(seconds: 5), () {
43 | hide();
44 | });
45 | },
46 | ),
47 | ].map((Widget button) {
48 | return Container(
49 | padding: const EdgeInsets.symmetric(vertical: 8.0),
50 | child: button);
51 | }).toList(),
52 | ));
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: cool_ui_example
2 | description: Cool UI Example
3 |
4 | # The following defines the version and build number for your application.
5 | # A version number is three numbers separated by dots, like 1.2.43
6 | # followed by an optional build number separated by a +.
7 | # Both the version and the builder number may be overridden in flutter
8 | # build by specifying --build-name and --build-number, respectively.
9 | # Read more about versioning at semver.org.
10 | version: 1.0.0+1
11 |
12 | environment:
13 | sdk: ">=2.12.0 <3.0.0"
14 |
15 | dependencies:
16 | flutter:
17 | sdk: flutter
18 |
19 | # The following adds the Cupertino Icons font to your application.
20 | # Use with the CupertinoIcons class for iOS style icons.
21 | cupertino_icons: ^0.1.2
22 | cool_ui:
23 | path: ../
24 |
25 | dev_dependencies:
26 | flutter_test:
27 | sdk: flutter
28 |
29 |
30 | # For information on the generic Dart part of this file, see the
31 | # following page: https://www.dartlang.org/tools/pub/pubspec
32 |
33 | # The following section is specific to Flutter.
34 | flutter:
35 |
36 | # The following line ensures that the Material Icons font is
37 | # included with your application, so that you can use the icons in
38 | # the material Icons class.
39 | uses-material-design: true
40 |
41 | # To add assets to your application, add an assets section, like this:
42 | # assets:
43 | # - images/a_dot_burr.jpeg
44 | # - images/a_dot_ham.jpeg
45 |
46 | # An image asset can refer to one or more resolution-specific "variants", see
47 | # https://flutter.io/assets-and-images/#resolution-aware.
48 |
49 | # For details regarding adding assets from package dependencies, see
50 | # https://flutter.io/assets-and-images/#from-packages
51 |
52 | # To add custom fonts to your application, add a fonts section here,
53 | # in this "flutter" section. Each entry in this list should have a
54 | # "family" key with the font family name, and a "fonts" key with a
55 | # list giving the asset and other descriptors for the font. For
56 | # example:
57 | fonts:
58 | - family: CoolUIExample
59 | fonts:
60 | - asset: assets/fonts/CoolUIExample.ttf
61 | # - asset: fonts/Schyler-Italic.ttf
62 | # style: italic
63 | # - family: Trajan Pro
64 | # fonts:
65 | # - asset: fonts/TrajanPro.ttf
66 | # - asset: fonts/TrajanPro_Bold.ttf
67 | # weight: 700
68 | #
69 | # For details regarding fonts from package dependencies,
70 | # see https://flutter.io/custom-fonts/#from-packages
71 |
--------------------------------------------------------------------------------
/example/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:cool_ui_example/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(new 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 |
--------------------------------------------------------------------------------
/example/windows/.gitignore:
--------------------------------------------------------------------------------
1 | flutter/ephemeral/
2 |
3 | # Visual Studio user-specific files.
4 | *.suo
5 | *.user
6 | *.userosscache
7 | *.sln.docstates
8 |
9 | # Visual Studio build-related files.
10 | x64/
11 | x86/
12 |
13 | # Visual Studio cache files
14 | # files ending in .cache can be ignored
15 | *.[Cc]ache
16 | # but keep track of directories ending in .cache
17 | !*.[Cc]ache/
18 |
--------------------------------------------------------------------------------
/example/windows/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # Project-level configuration.
2 | cmake_minimum_required(VERSION 3.14)
3 | project(example LANGUAGES CXX)
4 |
5 | # The name of the executable created for the application. Change this to change
6 | # the on-disk name of your application.
7 | set(BINARY_NAME "example")
8 |
9 | # Explicitly opt in to modern CMake behaviors to avoid warnings with recent
10 | # versions of CMake.
11 | cmake_policy(SET CMP0063 NEW)
12 |
13 | # Define build configuration option.
14 | get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
15 | if(IS_MULTICONFIG)
16 | set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
17 | CACHE STRING "" FORCE)
18 | else()
19 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
20 | set(CMAKE_BUILD_TYPE "Debug" CACHE
21 | STRING "Flutter build mode" FORCE)
22 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
23 | "Debug" "Profile" "Release")
24 | endif()
25 | endif()
26 | # Define settings for the Profile build mode.
27 | set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
28 | set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
29 | set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
30 | set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
31 |
32 | # Use Unicode for all projects.
33 | add_definitions(-DUNICODE -D_UNICODE)
34 |
35 | # Compilation settings that should be applied to most targets.
36 | #
37 | # Be cautious about adding new options here, as plugins use this function by
38 | # default. In most cases, you should add new options to specific targets instead
39 | # of modifying this function.
40 | function(APPLY_STANDARD_SETTINGS TARGET)
41 | target_compile_features(${TARGET} PUBLIC cxx_std_17)
42 | target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
43 | target_compile_options(${TARGET} PRIVATE /EHsc)
44 | target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
45 | target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>")
46 | endfunction()
47 |
48 | # Flutter library and tool build rules.
49 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
50 | add_subdirectory(${FLUTTER_MANAGED_DIR})
51 |
52 | # Application build; see runner/CMakeLists.txt.
53 | add_subdirectory("runner")
54 |
55 | # Generated plugin build rules, which manage building the plugins and adding
56 | # them to the application.
57 | include(flutter/generated_plugins.cmake)
58 |
59 |
60 | # === Installation ===
61 | # Support files are copied into place next to the executable, so that it can
62 | # run in place. This is done instead of making a separate bundle (as on Linux)
63 | # so that building and running from within Visual Studio will work.
64 | set(BUILD_BUNDLE_DIR "$")
65 | # Make the "install" step default, as it's required to run.
66 | set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
67 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
68 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
69 | endif()
70 |
71 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
72 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
73 |
74 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
75 | COMPONENT Runtime)
76 |
77 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
78 | COMPONENT Runtime)
79 |
80 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
81 | COMPONENT Runtime)
82 |
83 | if(PLUGIN_BUNDLED_LIBRARIES)
84 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
85 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
86 | COMPONENT Runtime)
87 | endif()
88 |
89 | # Fully re-copy the assets directory on each build to avoid having stale files
90 | # from a previous install.
91 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
92 | install(CODE "
93 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
94 | " COMPONENT Runtime)
95 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
96 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
97 |
98 | # Install the AOT library on non-Debug builds only.
99 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
100 | CONFIGURATIONS Profile;Release
101 | COMPONENT Runtime)
102 |
--------------------------------------------------------------------------------
/example/windows/flutter/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # This file controls Flutter-level build steps. It should not be edited.
2 | cmake_minimum_required(VERSION 3.14)
3 |
4 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
5 |
6 | # Configuration provided via flutter tool.
7 | include(${EPHEMERAL_DIR}/generated_config.cmake)
8 |
9 | # TODO: Move the rest of this into files in ephemeral. See
10 | # https://github.com/flutter/flutter/issues/57146.
11 | set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
12 |
13 | # === Flutter Library ===
14 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
15 |
16 | # Published to parent scope for install step.
17 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
18 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
19 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
20 | set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE)
21 |
22 | list(APPEND FLUTTER_LIBRARY_HEADERS
23 | "flutter_export.h"
24 | "flutter_windows.h"
25 | "flutter_messenger.h"
26 | "flutter_plugin_registrar.h"
27 | "flutter_texture_registrar.h"
28 | )
29 | list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/")
30 | add_library(flutter INTERFACE)
31 | target_include_directories(flutter INTERFACE
32 | "${EPHEMERAL_DIR}"
33 | )
34 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib")
35 | add_dependencies(flutter flutter_assemble)
36 |
37 | # === Wrapper ===
38 | list(APPEND CPP_WRAPPER_SOURCES_CORE
39 | "core_implementations.cc"
40 | "standard_codec.cc"
41 | )
42 | list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
43 | list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
44 | "plugin_registrar.cc"
45 | )
46 | list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
47 | list(APPEND CPP_WRAPPER_SOURCES_APP
48 | "flutter_engine.cc"
49 | "flutter_view_controller.cc"
50 | )
51 | list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
52 |
53 | # Wrapper sources needed for a plugin.
54 | add_library(flutter_wrapper_plugin STATIC
55 | ${CPP_WRAPPER_SOURCES_CORE}
56 | ${CPP_WRAPPER_SOURCES_PLUGIN}
57 | )
58 | apply_standard_settings(flutter_wrapper_plugin)
59 | set_target_properties(flutter_wrapper_plugin PROPERTIES
60 | POSITION_INDEPENDENT_CODE ON)
61 | set_target_properties(flutter_wrapper_plugin PROPERTIES
62 | CXX_VISIBILITY_PRESET hidden)
63 | target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
64 | target_include_directories(flutter_wrapper_plugin PUBLIC
65 | "${WRAPPER_ROOT}/include"
66 | )
67 | add_dependencies(flutter_wrapper_plugin flutter_assemble)
68 |
69 | # Wrapper sources needed for the runner.
70 | add_library(flutter_wrapper_app STATIC
71 | ${CPP_WRAPPER_SOURCES_CORE}
72 | ${CPP_WRAPPER_SOURCES_APP}
73 | )
74 | apply_standard_settings(flutter_wrapper_app)
75 | target_link_libraries(flutter_wrapper_app PUBLIC flutter)
76 | target_include_directories(flutter_wrapper_app PUBLIC
77 | "${WRAPPER_ROOT}/include"
78 | )
79 | add_dependencies(flutter_wrapper_app flutter_assemble)
80 |
81 | # === Flutter tool backend ===
82 | # _phony_ is a non-existent file to force this command to run every time,
83 | # since currently there's no way to get a full input/output list from the
84 | # flutter tool.
85 | set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_")
86 | set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE)
87 | add_custom_command(
88 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
89 | ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
90 | ${CPP_WRAPPER_SOURCES_APP}
91 | ${PHONY_OUTPUT}
92 | COMMAND ${CMAKE_COMMAND} -E env
93 | ${FLUTTER_TOOL_ENVIRONMENT}
94 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
95 | windows-x64 $
96 | VERBATIM
97 | )
98 | add_custom_target(flutter_assemble DEPENDS
99 | "${FLUTTER_LIBRARY}"
100 | ${FLUTTER_LIBRARY_HEADERS}
101 | ${CPP_WRAPPER_SOURCES_CORE}
102 | ${CPP_WRAPPER_SOURCES_PLUGIN}
103 | ${CPP_WRAPPER_SOURCES_APP}
104 | )
105 |
--------------------------------------------------------------------------------
/example/windows/flutter/generated_plugin_registrant.cc:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | // clang-format off
6 |
7 | #include "generated_plugin_registrant.h"
8 |
9 |
10 | void RegisterPlugins(flutter::PluginRegistry* registry) {
11 | }
12 |
--------------------------------------------------------------------------------
/example/windows/flutter/generated_plugin_registrant.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | // clang-format off
6 |
7 | #ifndef GENERATED_PLUGIN_REGISTRANT_
8 | #define GENERATED_PLUGIN_REGISTRANT_
9 |
10 | #include
11 |
12 | // Registers Flutter plugins.
13 | void RegisterPlugins(flutter::PluginRegistry* registry);
14 |
15 | #endif // GENERATED_PLUGIN_REGISTRANT_
16 |
--------------------------------------------------------------------------------
/example/windows/flutter/generated_plugins.cmake:
--------------------------------------------------------------------------------
1 | #
2 | # Generated file, do not edit.
3 | #
4 |
5 | list(APPEND FLUTTER_PLUGIN_LIST
6 | )
7 |
8 | list(APPEND FLUTTER_FFI_PLUGIN_LIST
9 | )
10 |
11 | set(PLUGIN_BUNDLED_LIBRARIES)
12 |
13 | foreach(plugin ${FLUTTER_PLUGIN_LIST})
14 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})
15 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
16 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $)
17 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
18 | endforeach(plugin)
19 |
20 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
21 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})
22 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
23 | endforeach(ffi_plugin)
24 |
--------------------------------------------------------------------------------
/example/windows/runner/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.14)
2 | project(runner LANGUAGES CXX)
3 |
4 | # Define the application target. To change its name, change BINARY_NAME in the
5 | # top-level CMakeLists.txt, not the value here, or `flutter run` will no longer
6 | # work.
7 | #
8 | # Any new source files that you add to the application should be added here.
9 | add_executable(${BINARY_NAME} WIN32
10 | "flutter_window.cpp"
11 | "main.cpp"
12 | "utils.cpp"
13 | "win32_window.cpp"
14 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
15 | "Runner.rc"
16 | "runner.exe.manifest"
17 | )
18 |
19 | # Apply the standard set of build settings. This can be removed for applications
20 | # that need different build settings.
21 | apply_standard_settings(${BINARY_NAME})
22 |
23 | # Add preprocessor definitions for the build version.
24 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"")
25 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}")
26 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}")
27 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}")
28 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}")
29 |
30 | # Disable Windows macros that collide with C++ standard library functions.
31 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
32 |
33 | # Add dependency libraries and include directories. Add any application-specific
34 | # dependencies here.
35 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
36 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
37 |
38 | # Run the Flutter tool portions of the build. This must not be removed.
39 | add_dependencies(${BINARY_NAME} flutter_assemble)
40 |
--------------------------------------------------------------------------------
/example/windows/runner/Runner.rc:
--------------------------------------------------------------------------------
1 | // Microsoft Visual C++ generated resource script.
2 | //
3 | #pragma code_page(65001)
4 | #include "resource.h"
5 |
6 | #define APSTUDIO_READONLY_SYMBOLS
7 | /////////////////////////////////////////////////////////////////////////////
8 | //
9 | // Generated from the TEXTINCLUDE 2 resource.
10 | //
11 | #include "winres.h"
12 |
13 | /////////////////////////////////////////////////////////////////////////////
14 | #undef APSTUDIO_READONLY_SYMBOLS
15 |
16 | /////////////////////////////////////////////////////////////////////////////
17 | // English (United States) resources
18 |
19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
20 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
21 |
22 | #ifdef APSTUDIO_INVOKED
23 | /////////////////////////////////////////////////////////////////////////////
24 | //
25 | // TEXTINCLUDE
26 | //
27 |
28 | 1 TEXTINCLUDE
29 | BEGIN
30 | "resource.h\0"
31 | END
32 |
33 | 2 TEXTINCLUDE
34 | BEGIN
35 | "#include ""winres.h""\r\n"
36 | "\0"
37 | END
38 |
39 | 3 TEXTINCLUDE
40 | BEGIN
41 | "\r\n"
42 | "\0"
43 | END
44 |
45 | #endif // APSTUDIO_INVOKED
46 |
47 |
48 | /////////////////////////////////////////////////////////////////////////////
49 | //
50 | // Icon
51 | //
52 |
53 | // Icon with lowest ID value placed first to ensure application icon
54 | // remains consistent on all systems.
55 | IDI_APP_ICON ICON "resources\\app_icon.ico"
56 |
57 |
58 | /////////////////////////////////////////////////////////////////////////////
59 | //
60 | // Version
61 | //
62 |
63 | #if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD)
64 | #define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD
65 | #else
66 | #define VERSION_AS_NUMBER 1,0,0,0
67 | #endif
68 |
69 | #if defined(FLUTTER_VERSION)
70 | #define VERSION_AS_STRING FLUTTER_VERSION
71 | #else
72 | #define VERSION_AS_STRING "1.0.0"
73 | #endif
74 |
75 | VS_VERSION_INFO VERSIONINFO
76 | FILEVERSION VERSION_AS_NUMBER
77 | PRODUCTVERSION VERSION_AS_NUMBER
78 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
79 | #ifdef _DEBUG
80 | FILEFLAGS VS_FF_DEBUG
81 | #else
82 | FILEFLAGS 0x0L
83 | #endif
84 | FILEOS VOS__WINDOWS32
85 | FILETYPE VFT_APP
86 | FILESUBTYPE 0x0L
87 | BEGIN
88 | BLOCK "StringFileInfo"
89 | BEGIN
90 | BLOCK "040904e4"
91 | BEGIN
92 | VALUE "CompanyName", "com.example" "\0"
93 | VALUE "FileDescription", "example" "\0"
94 | VALUE "FileVersion", VERSION_AS_STRING "\0"
95 | VALUE "InternalName", "example" "\0"
96 | VALUE "LegalCopyright", "Copyright (C) 2022 com.example. All rights reserved." "\0"
97 | VALUE "OriginalFilename", "example.exe" "\0"
98 | VALUE "ProductName", "example" "\0"
99 | VALUE "ProductVersion", VERSION_AS_STRING "\0"
100 | END
101 | END
102 | BLOCK "VarFileInfo"
103 | BEGIN
104 | VALUE "Translation", 0x409, 1252
105 | END
106 | END
107 |
108 | #endif // English (United States) resources
109 | /////////////////////////////////////////////////////////////////////////////
110 |
111 |
112 |
113 | #ifndef APSTUDIO_INVOKED
114 | /////////////////////////////////////////////////////////////////////////////
115 | //
116 | // Generated from the TEXTINCLUDE 3 resource.
117 | //
118 |
119 |
120 | /////////////////////////////////////////////////////////////////////////////
121 | #endif // not APSTUDIO_INVOKED
122 |
--------------------------------------------------------------------------------
/example/windows/runner/flutter_window.cpp:
--------------------------------------------------------------------------------
1 | #include "flutter_window.h"
2 |
3 | #include
4 |
5 | #include "flutter/generated_plugin_registrant.h"
6 |
7 | FlutterWindow::FlutterWindow(const flutter::DartProject& project)
8 | : project_(project) {}
9 |
10 | FlutterWindow::~FlutterWindow() {}
11 |
12 | bool FlutterWindow::OnCreate() {
13 | if (!Win32Window::OnCreate()) {
14 | return false;
15 | }
16 |
17 | RECT frame = GetClientArea();
18 |
19 | // The size here must match the window dimensions to avoid unnecessary surface
20 | // creation / destruction in the startup path.
21 | flutter_controller_ = std::make_unique(
22 | frame.right - frame.left, frame.bottom - frame.top, project_);
23 | // Ensure that basic setup of the controller was successful.
24 | if (!flutter_controller_->engine() || !flutter_controller_->view()) {
25 | return false;
26 | }
27 | RegisterPlugins(flutter_controller_->engine());
28 | SetChildContent(flutter_controller_->view()->GetNativeWindow());
29 | return true;
30 | }
31 |
32 | void FlutterWindow::OnDestroy() {
33 | if (flutter_controller_) {
34 | flutter_controller_ = nullptr;
35 | }
36 |
37 | Win32Window::OnDestroy();
38 | }
39 |
40 | LRESULT
41 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
42 | WPARAM const wparam,
43 | LPARAM const lparam) noexcept {
44 | // Give Flutter, including plugins, an opportunity to handle window messages.
45 | if (flutter_controller_) {
46 | std::optional result =
47 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
48 | lparam);
49 | if (result) {
50 | return *result;
51 | }
52 | }
53 |
54 | switch (message) {
55 | case WM_FONTCHANGE:
56 | flutter_controller_->engine()->ReloadSystemFonts();
57 | break;
58 | }
59 |
60 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
61 | }
62 |
--------------------------------------------------------------------------------
/example/windows/runner/flutter_window.h:
--------------------------------------------------------------------------------
1 | #ifndef RUNNER_FLUTTER_WINDOW_H_
2 | #define RUNNER_FLUTTER_WINDOW_H_
3 |
4 | #include
5 | #include
6 |
7 | #include
8 |
9 | #include "win32_window.h"
10 |
11 | // A window that does nothing but host a Flutter view.
12 | class FlutterWindow : public Win32Window {
13 | public:
14 | // Creates a new FlutterWindow hosting a Flutter view running |project|.
15 | explicit FlutterWindow(const flutter::DartProject& project);
16 | virtual ~FlutterWindow();
17 |
18 | protected:
19 | // Win32Window:
20 | bool OnCreate() override;
21 | void OnDestroy() override;
22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
23 | LPARAM const lparam) noexcept override;
24 |
25 | private:
26 | // The project to run.
27 | flutter::DartProject project_;
28 |
29 | // The Flutter instance hosted by this window.
30 | std::unique_ptr flutter_controller_;
31 | };
32 |
33 | #endif // RUNNER_FLUTTER_WINDOW_H_
34 |
--------------------------------------------------------------------------------
/example/windows/runner/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include "flutter_window.h"
6 | #include "utils.h"
7 |
8 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
9 | _In_ wchar_t *command_line, _In_ int show_command) {
10 | // Attach to console when present (e.g., 'flutter run') or create a
11 | // new console when running with a debugger.
12 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
13 | CreateAndAttachConsole();
14 | }
15 |
16 | // Initialize COM, so that it is available for use in the library and/or
17 | // plugins.
18 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
19 |
20 | flutter::DartProject project(L"data");
21 |
22 | std::vector command_line_arguments =
23 | GetCommandLineArguments();
24 |
25 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
26 |
27 | FlutterWindow window(project);
28 | Win32Window::Point origin(10, 10);
29 | Win32Window::Size size(1280, 720);
30 | if (!window.CreateAndShow(L"example", origin, size)) {
31 | return EXIT_FAILURE;
32 | }
33 | window.SetQuitOnClose(true);
34 |
35 | ::MSG msg;
36 | while (::GetMessage(&msg, nullptr, 0, 0)) {
37 | ::TranslateMessage(&msg);
38 | ::DispatchMessage(&msg);
39 | }
40 |
41 | ::CoUninitialize();
42 | return EXIT_SUCCESS;
43 | }
44 |
--------------------------------------------------------------------------------
/example/windows/runner/resource.h:
--------------------------------------------------------------------------------
1 | //{{NO_DEPENDENCIES}}
2 | // Microsoft Visual C++ generated include file.
3 | // Used by Runner.rc
4 | //
5 | #define IDI_APP_ICON 101
6 |
7 | // Next default values for new objects
8 | //
9 | #ifdef APSTUDIO_INVOKED
10 | #ifndef APSTUDIO_READONLY_SYMBOLS
11 | #define _APS_NEXT_RESOURCE_VALUE 102
12 | #define _APS_NEXT_COMMAND_VALUE 40001
13 | #define _APS_NEXT_CONTROL_VALUE 1001
14 | #define _APS_NEXT_SYMED_VALUE 101
15 | #endif
16 | #endif
17 |
--------------------------------------------------------------------------------
/example/windows/runner/resources/app_icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Im-Kevin/cool_ui/6b5864fec5dda333a4ad5a54dadeeff08383101e/example/windows/runner/resources/app_icon.ico
--------------------------------------------------------------------------------
/example/windows/runner/runner.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PerMonitorV2
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/example/windows/runner/utils.cpp:
--------------------------------------------------------------------------------
1 | #include "utils.h"
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | #include
9 |
10 | void CreateAndAttachConsole() {
11 | if (::AllocConsole()) {
12 | FILE *unused;
13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
14 | _dup2(_fileno(stdout), 1);
15 | }
16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
17 | _dup2(_fileno(stdout), 2);
18 | }
19 | std::ios::sync_with_stdio();
20 | FlutterDesktopResyncOutputStreams();
21 | }
22 | }
23 |
24 | std::vector GetCommandLineArguments() {
25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.
26 | int argc;
27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
28 | if (argv == nullptr) {
29 | return std::vector();
30 | }
31 |
32 | std::vector command_line_arguments;
33 |
34 | // Skip the first argument as it's the binary name.
35 | for (int i = 1; i < argc; i++) {
36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i]));
37 | }
38 |
39 | ::LocalFree(argv);
40 |
41 | return command_line_arguments;
42 | }
43 |
44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) {
45 | if (utf16_string == nullptr) {
46 | return std::string();
47 | }
48 | int target_length = ::WideCharToMultiByte(
49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
50 | -1, nullptr, 0, nullptr, nullptr);
51 | std::string utf8_string;
52 | if (target_length == 0 || target_length > utf8_string.max_size()) {
53 | return utf8_string;
54 | }
55 | utf8_string.resize(target_length);
56 | int converted_length = ::WideCharToMultiByte(
57 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
58 | -1, utf8_string.data(),
59 | target_length, nullptr, nullptr);
60 | if (converted_length == 0) {
61 | return std::string();
62 | }
63 | return utf8_string;
64 | }
65 |
--------------------------------------------------------------------------------
/example/windows/runner/utils.h:
--------------------------------------------------------------------------------
1 | #ifndef RUNNER_UTILS_H_
2 | #define RUNNER_UTILS_H_
3 |
4 | #include
5 | #include
6 |
7 | // Creates a console for the process, and redirects stdout and stderr to
8 | // it for both the runner and the Flutter library.
9 | void CreateAndAttachConsole();
10 |
11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string
12 | // encoded in UTF-8. Returns an empty std::string on failure.
13 | std::string Utf8FromUtf16(const wchar_t* utf16_string);
14 |
15 | // Gets the command line arguments passed in as a std::vector,
16 | // encoded in UTF-8. Returns an empty std::vector on failure.
17 | std::vector GetCommandLineArguments();
18 |
19 | #endif // RUNNER_UTILS_H_
20 |
--------------------------------------------------------------------------------
/example/windows/runner/win32_window.cpp:
--------------------------------------------------------------------------------
1 | #include "win32_window.h"
2 |
3 | #include
4 |
5 | #include "resource.h"
6 |
7 | namespace {
8 |
9 | constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
10 |
11 | // The number of Win32Window objects that currently exist.
12 | static int g_active_window_count = 0;
13 |
14 | using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
15 |
16 | // Scale helper to convert logical scaler values to physical using passed in
17 | // scale factor
18 | int Scale(int source, double scale_factor) {
19 | return static_cast(source * scale_factor);
20 | }
21 |
22 | // Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
23 | // This API is only needed for PerMonitor V1 awareness mode.
24 | void EnableFullDpiSupportIfAvailable(HWND hwnd) {
25 | HMODULE user32_module = LoadLibraryA("User32.dll");
26 | if (!user32_module) {
27 | return;
28 | }
29 | auto enable_non_client_dpi_scaling =
30 | reinterpret_cast(
31 | GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
32 | if (enable_non_client_dpi_scaling != nullptr) {
33 | enable_non_client_dpi_scaling(hwnd);
34 | FreeLibrary(user32_module);
35 | }
36 | }
37 |
38 | } // namespace
39 |
40 | // Manages the Win32Window's window class registration.
41 | class WindowClassRegistrar {
42 | public:
43 | ~WindowClassRegistrar() = default;
44 |
45 | // Returns the singleton registar instance.
46 | static WindowClassRegistrar* GetInstance() {
47 | if (!instance_) {
48 | instance_ = new WindowClassRegistrar();
49 | }
50 | return instance_;
51 | }
52 |
53 | // Returns the name of the window class, registering the class if it hasn't
54 | // previously been registered.
55 | const wchar_t* GetWindowClass();
56 |
57 | // Unregisters the window class. Should only be called if there are no
58 | // instances of the window.
59 | void UnregisterWindowClass();
60 |
61 | private:
62 | WindowClassRegistrar() = default;
63 |
64 | static WindowClassRegistrar* instance_;
65 |
66 | bool class_registered_ = false;
67 | };
68 |
69 | WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;
70 |
71 | const wchar_t* WindowClassRegistrar::GetWindowClass() {
72 | if (!class_registered_) {
73 | WNDCLASS window_class{};
74 | window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
75 | window_class.lpszClassName = kWindowClassName;
76 | window_class.style = CS_HREDRAW | CS_VREDRAW;
77 | window_class.cbClsExtra = 0;
78 | window_class.cbWndExtra = 0;
79 | window_class.hInstance = GetModuleHandle(nullptr);
80 | window_class.hIcon =
81 | LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
82 | window_class.hbrBackground = 0;
83 | window_class.lpszMenuName = nullptr;
84 | window_class.lpfnWndProc = Win32Window::WndProc;
85 | RegisterClass(&window_class);
86 | class_registered_ = true;
87 | }
88 | return kWindowClassName;
89 | }
90 |
91 | void WindowClassRegistrar::UnregisterWindowClass() {
92 | UnregisterClass(kWindowClassName, nullptr);
93 | class_registered_ = false;
94 | }
95 |
96 | Win32Window::Win32Window() {
97 | ++g_active_window_count;
98 | }
99 |
100 | Win32Window::~Win32Window() {
101 | --g_active_window_count;
102 | Destroy();
103 | }
104 |
105 | bool Win32Window::CreateAndShow(const std::wstring& title,
106 | const Point& origin,
107 | const Size& size) {
108 | Destroy();
109 |
110 | const wchar_t* window_class =
111 | WindowClassRegistrar::GetInstance()->GetWindowClass();
112 |
113 | const POINT target_point = {static_cast(origin.x),
114 | static_cast(origin.y)};
115 | HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
116 | UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
117 | double scale_factor = dpi / 96.0;
118 |
119 | HWND window = CreateWindow(
120 | window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
121 | Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
122 | Scale(size.width, scale_factor), Scale(size.height, scale_factor),
123 | nullptr, nullptr, GetModuleHandle(nullptr), this);
124 |
125 | if (!window) {
126 | return false;
127 | }
128 |
129 | return OnCreate();
130 | }
131 |
132 | // static
133 | LRESULT CALLBACK Win32Window::WndProc(HWND const window,
134 | UINT const message,
135 | WPARAM const wparam,
136 | LPARAM const lparam) noexcept {
137 | if (message == WM_NCCREATE) {
138 | auto window_struct = reinterpret_cast(lparam);
139 | SetWindowLongPtr(window, GWLP_USERDATA,
140 | reinterpret_cast(window_struct->lpCreateParams));
141 |
142 | auto that = static_cast(window_struct->lpCreateParams);
143 | EnableFullDpiSupportIfAvailable(window);
144 | that->window_handle_ = window;
145 | } else if (Win32Window* that = GetThisFromHandle(window)) {
146 | return that->MessageHandler(window, message, wparam, lparam);
147 | }
148 |
149 | return DefWindowProc(window, message, wparam, lparam);
150 | }
151 |
152 | LRESULT
153 | Win32Window::MessageHandler(HWND hwnd,
154 | UINT const message,
155 | WPARAM const wparam,
156 | LPARAM const lparam) noexcept {
157 | switch (message) {
158 | case WM_DESTROY:
159 | window_handle_ = nullptr;
160 | Destroy();
161 | if (quit_on_close_) {
162 | PostQuitMessage(0);
163 | }
164 | return 0;
165 |
166 | case WM_DPICHANGED: {
167 | auto newRectSize = reinterpret_cast(lparam);
168 | LONG newWidth = newRectSize->right - newRectSize->left;
169 | LONG newHeight = newRectSize->bottom - newRectSize->top;
170 |
171 | SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,
172 | newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
173 |
174 | return 0;
175 | }
176 | case WM_SIZE: {
177 | RECT rect = GetClientArea();
178 | if (child_content_ != nullptr) {
179 | // Size and position the child window.
180 | MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,
181 | rect.bottom - rect.top, TRUE);
182 | }
183 | return 0;
184 | }
185 |
186 | case WM_ACTIVATE:
187 | if (child_content_ != nullptr) {
188 | SetFocus(child_content_);
189 | }
190 | return 0;
191 | }
192 |
193 | return DefWindowProc(window_handle_, message, wparam, lparam);
194 | }
195 |
196 | void Win32Window::Destroy() {
197 | OnDestroy();
198 |
199 | if (window_handle_) {
200 | DestroyWindow(window_handle_);
201 | window_handle_ = nullptr;
202 | }
203 | if (g_active_window_count == 0) {
204 | WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
205 | }
206 | }
207 |
208 | Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
209 | return reinterpret_cast(
210 | GetWindowLongPtr(window, GWLP_USERDATA));
211 | }
212 |
213 | void Win32Window::SetChildContent(HWND content) {
214 | child_content_ = content;
215 | SetParent(content, window_handle_);
216 | RECT frame = GetClientArea();
217 |
218 | MoveWindow(content, frame.left, frame.top, frame.right - frame.left,
219 | frame.bottom - frame.top, true);
220 |
221 | SetFocus(child_content_);
222 | }
223 |
224 | RECT Win32Window::GetClientArea() {
225 | RECT frame;
226 | GetClientRect(window_handle_, &frame);
227 | return frame;
228 | }
229 |
230 | HWND Win32Window::GetHandle() {
231 | return window_handle_;
232 | }
233 |
234 | void Win32Window::SetQuitOnClose(bool quit_on_close) {
235 | quit_on_close_ = quit_on_close;
236 | }
237 |
238 | bool Win32Window::OnCreate() {
239 | // No-op; provided for subclasses.
240 | return true;
241 | }
242 |
243 | void Win32Window::OnDestroy() {
244 | // No-op; provided for subclasses.
245 | }
246 |
--------------------------------------------------------------------------------
/example/windows/runner/win32_window.h:
--------------------------------------------------------------------------------
1 | #ifndef RUNNER_WIN32_WINDOW_H_
2 | #define RUNNER_WIN32_WINDOW_H_
3 |
4 | #include
5 |
6 | #include
7 | #include
8 | #include
9 |
10 | // A class abstraction for a high DPI-aware Win32 Window. Intended to be
11 | // inherited from by classes that wish to specialize with custom
12 | // rendering and input handling
13 | class Win32Window {
14 | public:
15 | struct Point {
16 | unsigned int x;
17 | unsigned int y;
18 | Point(unsigned int x, unsigned int y) : x(x), y(y) {}
19 | };
20 |
21 | struct Size {
22 | unsigned int width;
23 | unsigned int height;
24 | Size(unsigned int width, unsigned int height)
25 | : width(width), height(height) {}
26 | };
27 |
28 | Win32Window();
29 | virtual ~Win32Window();
30 |
31 | // Creates and shows a win32 window with |title| and position and size using
32 | // |origin| and |size|. New windows are created on the default monitor. Window
33 | // sizes are specified to the OS in physical pixels, hence to ensure a
34 | // consistent size to will treat the width height passed in to this function
35 | // as logical pixels and scale to appropriate for the default monitor. Returns
36 | // true if the window was created successfully.
37 | bool CreateAndShow(const std::wstring& title,
38 | const Point& origin,
39 | const Size& size);
40 |
41 | // Release OS resources associated with window.
42 | void Destroy();
43 |
44 | // Inserts |content| into the window tree.
45 | void SetChildContent(HWND content);
46 |
47 | // Returns the backing Window handle to enable clients to set icon and other
48 | // window properties. Returns nullptr if the window has been destroyed.
49 | HWND GetHandle();
50 |
51 | // If true, closing this window will quit the application.
52 | void SetQuitOnClose(bool quit_on_close);
53 |
54 | // Return a RECT representing the bounds of the current client area.
55 | RECT GetClientArea();
56 |
57 | protected:
58 | // Processes and route salient window messages for mouse handling,
59 | // size change and DPI. Delegates handling of these to member overloads that
60 | // inheriting classes can handle.
61 | virtual LRESULT MessageHandler(HWND window,
62 | UINT const message,
63 | WPARAM const wparam,
64 | LPARAM const lparam) noexcept;
65 |
66 | // Called when CreateAndShow is called, allowing subclass window-related
67 | // setup. Subclasses should return false if setup fails.
68 | virtual bool OnCreate();
69 |
70 | // Called when Destroy is called.
71 | virtual void OnDestroy();
72 |
73 | private:
74 | friend class WindowClassRegistrar;
75 |
76 | // OS callback called by message pump. Handles the WM_NCCREATE message which
77 | // is passed when the non-client area is being created and enables automatic
78 | // non-client DPI scaling so that the non-client area automatically
79 | // responsponds to changes in DPI. All other messages are handled by
80 | // MessageHandler.
81 | static LRESULT CALLBACK WndProc(HWND const window,
82 | UINT const message,
83 | WPARAM const wparam,
84 | LPARAM const lparam) noexcept;
85 |
86 | // Retrieves a class instance pointer for |window|
87 | static Win32Window* GetThisFromHandle(HWND const window) noexcept;
88 |
89 | bool quit_on_close_ = false;
90 |
91 | // window handle for top level window.
92 | HWND window_handle_ = nullptr;
93 |
94 | // window handle for hosted content.
95 | HWND child_content_ = nullptr;
96 | };
97 |
98 | #endif // RUNNER_WIN32_WINDOW_H_
99 |
--------------------------------------------------------------------------------
/ios/Flutter/flutter_export_environment.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # This is a generated file; do not edit or check into version control.
3 | export "FLUTTER_ROOT=D:\flutter"
4 | export "FLUTTER_APPLICATION_PATH=D:\Code\GitHub\cool_ui"
5 | export "COCOAPODS_PARALLEL_CODE_SIGN=true"
6 | export "FLUTTER_TARGET=lib\main.dart"
7 | export "FLUTTER_BUILD_DIR=build"
8 | export "FLUTTER_BUILD_NAME=1.2.0"
9 | export "FLUTTER_BUILD_NUMBER=1.2.0"
10 | export "DART_OBFUSCATION=false"
11 | export "TRACK_WIDGET_CREATION=true"
12 | export "TREE_SHAKE_ICONS=false"
13 | export "PACKAGE_CONFIG=.dart_tool/package_config.json"
14 |
--------------------------------------------------------------------------------
/ios/Runner/GeneratedPluginRegistrant.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | // clang-format off
6 |
7 | #ifndef GeneratedPluginRegistrant_h
8 | #define GeneratedPluginRegistrant_h
9 |
10 | #import
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | @interface GeneratedPluginRegistrant : NSObject
15 | + (void)registerWithRegistry:(NSObject*)registry;
16 | @end
17 |
18 | NS_ASSUME_NONNULL_END
19 | #endif /* GeneratedPluginRegistrant_h */
20 |
--------------------------------------------------------------------------------
/ios/Runner/GeneratedPluginRegistrant.m:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | // clang-format off
6 |
7 | #import "GeneratedPluginRegistrant.h"
8 |
9 | @implementation GeneratedPluginRegistrant
10 |
11 | + (void)registerWithRegistry:(NSObject*)registry {
12 | }
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/lib/cool_ui.dart:
--------------------------------------------------------------------------------
1 | library cool_ui;
2 |
3 | import 'dart:async';
4 | import 'dart:ui' as ui;
5 | import 'dart:core';
6 |
7 | import 'package:back_button_interceptor/back_button_interceptor.dart';
8 | import 'package:flutter/foundation.dart';
9 | import 'package:flutter/material.dart';
10 | import 'package:flutter/scheduler.dart';
11 | import 'package:flutter/services.dart';
12 | import 'package:flutter/rendering.dart';
13 |
14 | part 'utils/widget_util.dart';
15 | part 'utils/screen_util.dart';
16 | part 'utils/scroll_utils.dart';
17 |
18 | part 'icons/cool_ui_icons.dart';
19 |
20 | part 'widgets/popover/cupertino_popover.dart';
21 | part 'widgets/popover/cupertino_popover_menu_item.dart';
22 |
23 | part 'widgets/utils/paint_event.dart';
24 |
25 | part 'dialogs/weui_toast.dart';
26 |
27 | part 'keyboards/mocks/mock_binding.dart';
28 | part 'keyboards/mocks/mock_binary_messenger.dart';
29 | part 'keyboards/keyboard_manager.dart';
30 | part 'keyboards/number_keyboard.dart';
31 | part 'keyboards/keyboard_controller.dart';
32 | part 'keyboards/keyboard_media_query.dart';
33 | part 'keyboards/keyboard_root.dart';
34 |
35 |
36 | part 'widgets/tables/table.dart';
--------------------------------------------------------------------------------
/lib/dialogs/weui_toast.dart:
--------------------------------------------------------------------------------
1 | part of cool_ui;
2 |
3 | typedef HideCallback = Future Function();
4 |
5 | class WeuiToastWidget extends StatelessWidget {
6 | const WeuiToastWidget({
7 | Key? key,
8 | required this.stopEvent,
9 | required this.alignment,
10 | required this.icon,
11 | required this.message,
12 | }) : super(key: key);
13 |
14 | final bool stopEvent;
15 | final Alignment alignment;
16 | final Widget icon;
17 | final Widget message;
18 |
19 | @override
20 | Widget build(BuildContext context) {
21 | var widget = Material(
22 | color: Colors.transparent,
23 | child: Align(
24 | alignment: this.alignment,
25 | child: IntrinsicHeight(
26 | child: Container(
27 | width: 122.0,
28 | decoration: BoxDecoration(
29 | color: Color.fromRGBO(17, 17, 17, 0.7),
30 | borderRadius: BorderRadius.circular(5.0)),
31 | constraints: BoxConstraints(
32 | minHeight: 122.0,
33 | ),
34 | child: Column(
35 | children: [
36 | Container(
37 | margin: EdgeInsets.only(top: 22.0),
38 | constraints: BoxConstraints(minHeight: 55.0),
39 | child: IconTheme(
40 | data: IconThemeData(color: Colors.white, size: 55.0),
41 | child: icon),
42 | ),
43 | DefaultTextStyle(
44 | style: TextStyle(color: Colors.white, fontSize: 16.0),
45 | child: message,
46 | ),
47 | ],
48 | ),
49 | ),
50 | ),
51 | ),
52 | );
53 | return IgnorePointer(
54 | ignoring: !stopEvent,
55 | child: widget,
56 | );
57 | }
58 | }
59 |
60 | class WeuiLoadingIcon extends StatefulWidget {
61 | final double size;
62 |
63 | WeuiLoadingIcon({this.size = 50.0});
64 |
65 | @override
66 | State createState() => WeuiLoadingIconState();
67 | }
68 |
69 | class WeuiLoadingIconState extends State
70 | with SingleTickerProviderStateMixin {
71 | AnimationController? _controller ;
72 | Animation? _doubleAnimation;
73 |
74 | @override
75 | void initState() {
76 | super.initState();
77 | _controller = AnimationController(
78 | vsync: this, duration: Duration(milliseconds: 1000))
79 | ..repeat();
80 | _doubleAnimation = Tween(begin: 0.0, end: 360.0).animate(_controller!)
81 | ..addListener(() {
82 | setState(() {});
83 | });
84 | }
85 |
86 | @override
87 | void dispose() {
88 | // TODO: implement dispose
89 | _controller!.dispose();
90 | super.dispose();
91 | }
92 |
93 | @override
94 | Widget build(BuildContext context) {
95 | return Transform.rotate(
96 | angle: _doubleAnimation!.value ~/ 30 * 30.0 * 0.0174533,
97 | child: Image.asset("assets/images/loading.png",
98 | package: "cool_ui", width: widget.size, height: widget.size));
99 | }
100 | }
101 |
102 | @immutable
103 | class WeuiToastConfigData{
104 | final String successText;
105 | final Duration successDuration;
106 | final bool successBackButtonClose;
107 | final String loadingText;
108 | final bool loadingBackButtonClose;
109 | final Alignment toastAlignment;
110 |
111 | const WeuiToastConfigData({this.successText = '成功',
112 | this.successDuration = const Duration(seconds: 3),
113 | this.successBackButtonClose = true,
114 | this.loadingText = '加载中',
115 | this.loadingBackButtonClose = false,
116 | this.toastAlignment = const Alignment(0.0, -0.2)});
117 |
118 | copyWith({String? successText, Duration? successDuration, String? loadingText, Alignment? toastAlignment}){
119 | return WeuiToastConfigData(
120 | successText: successText ?? this.successText,
121 | successDuration: successDuration ?? this.successDuration,
122 | loadingText: loadingText ?? this.loadingText,
123 | toastAlignment: toastAlignment ?? this.toastAlignment
124 | );
125 | }
126 | }
127 |
128 | class WeuiToastConfig extends InheritedWidget{
129 | final WeuiToastConfigData? data;
130 | WeuiToastConfig({required Widget child,this.data}): super(child:child);
131 |
132 | @override
133 | bool updateShouldNotify(WeuiToastConfig oldWidget) {
134 | // TODO: implement updateShouldNotify
135 | return data != oldWidget.data;
136 | }
137 |
138 |
139 | static WeuiToastConfigData of(BuildContext context) {
140 | var widget = context.dependOnInheritedWidgetOfExactType();
141 | if(widget is WeuiToastConfig){
142 | return widget.data ?? WeuiToastConfigData();
143 | }
144 | return WeuiToastConfigData();
145 | }
146 | }
147 |
148 | Future showWeuiSuccessToast(
149 | {required BuildContext context,
150 | Widget? message,
151 | stopEvent = false,
152 | bool? backButtonClose,
153 | Alignment? alignment,
154 | Duration? closeDuration}) {
155 |
156 | var config = WeuiToastConfig.of(context);
157 | message = message?? Text(config.successText);
158 | closeDuration = closeDuration?? config.successDuration;
159 | backButtonClose = backButtonClose ?? config.successBackButtonClose;
160 | var hide = showWeuiToast(
161 | context: context,
162 | alignment: alignment,
163 | message: message,
164 | stopEvent: stopEvent,
165 | backButtonClose: backButtonClose,
166 | icon: Icon(CoolUIIcons.success_no_circle));
167 |
168 | return Future.delayed(closeDuration, () {
169 | hide();
170 | });
171 | }
172 |
173 | HideCallback showWeuiLoadingToast(
174 | {required BuildContext context,
175 | Widget? message,
176 | stopEvent = true,
177 | bool? backButtonClose,
178 | Alignment? alignment}) {
179 | var config = WeuiToastConfig.of(context);
180 | message = message?? Text(config.loadingText);
181 | backButtonClose = backButtonClose ?? config.loadingBackButtonClose;
182 |
183 | return showWeuiToast(
184 | context: context,
185 | alignment: alignment,
186 | message: message,
187 | stopEvent: stopEvent,
188 | icon: WeuiLoadingIcon(),
189 | backButtonClose: backButtonClose);
190 | }
191 |
192 | int backButtonIndex = 2;
193 |
194 | HideCallback showWeuiToast(
195 | {required BuildContext context,
196 | required Widget message,
197 | required Widget icon,
198 | bool stopEvent = false,
199 | Alignment? alignment,
200 | bool backButtonClose = false}) {
201 |
202 | var config = WeuiToastConfig.of(context);
203 | alignment = alignment ?? config.toastAlignment;
204 |
205 | Completer result = Completer();
206 | var backButtonName = 'CoolUI_WeuiToast$backButtonIndex';
207 | BackButtonInterceptor.add((stopDefaultButtonEvent, routeInfo){
208 | if(backButtonClose){
209 | result.future.then((hide){
210 | hide();
211 | });
212 | }
213 | return true;
214 | }, zIndex: backButtonIndex, name: backButtonName);
215 | backButtonIndex++;
216 |
217 | OverlayEntry? overlay = OverlayEntry(
218 | maintainState: true,
219 | builder: (_) => WillPopScope(
220 | onWillPop: () async {
221 | var hide = await result.future;
222 | hide();
223 | return false;
224 | },
225 | child: WeuiToastWidget(
226 | alignment: alignment!,
227 | icon: icon,
228 | message: message,
229 | stopEvent: stopEvent,
230 | ),
231 | ));
232 | result.complete((){
233 | if(overlay == null){
234 | return;
235 | }
236 | overlay!.remove();
237 | overlay = null;
238 | BackButtonInterceptor.removeByName(backButtonName);
239 | });
240 | Overlay.of(context)!.insert(overlay!);
241 |
242 |
243 | return () async {
244 | var hide = await result.future;
245 | hide();
246 | };
247 | }
248 |
--------------------------------------------------------------------------------
/lib/icons/cool_ui_icons.dart:
--------------------------------------------------------------------------------
1 | /// Flutter icons CoolUI
2 | /// Copyright (C) 2018 by original authors @ fluttericon.com, fontello.com
3 | /// This font was generated by FlutterIcon.com, which is derived from Fontello.
4 | ///
5 | /// To use this font, place it in your fonts/ directory and include the
6 | /// following in your pubspec.yaml
7 | ///
8 | /// flutter:
9 | /// fonts:
10 | /// - family: CoolUI
11 | /// fonts:
12 | /// - asset: fonts/CoolUI.ttf
13 | ///
14 | ///
15 | ///
16 | part of cool_ui;
17 |
18 | class CoolUIIcons {
19 | CoolUIIcons._();
20 |
21 | static const _kFontFam = 'CoolUIIcons';
22 |
23 | static const IconData success_no_circle = const IconData(0xea08, fontFamily: _kFontFam,fontPackage: "cool_ui");
24 | }
25 |
--------------------------------------------------------------------------------
/lib/keyboards/keyboard_controller.dart:
--------------------------------------------------------------------------------
1 | part of cool_ui;
2 |
3 | class KeyboardController extends ValueNotifier{
4 | final InputClient client;
5 |
6 | KeyboardController({TextEditingValue? value,required this.client})
7 | : super(value == null ? TextEditingValue.empty : value);
8 |
9 |
10 | /// The current string the user is editing.
11 | String get text => value.text;
12 | /// Setting this will notify all the listeners of this [TextEditingController]
13 | /// that they need to update (it calls [notifyListeners]). For this reason,
14 | /// this value should only be set between frames, e.g. in response to user
15 | /// actions, not during the build, layout, or paint phases.
16 | set text(String newText) {
17 | value = value.copyWith(text: newText,
18 | selection: const TextSelection.collapsed(offset: -1),
19 | composing: TextRange.empty);
20 | }
21 |
22 | /// The currently selected [text].
23 | ///
24 | /// If the selection is collapsed, then this property gives the offset of the
25 | /// cursor within the text.
26 | TextSelection get selection => value.selection;
27 | /// Setting this will notify all the listeners of this [TextEditingController]
28 | /// that they need to update (it calls [notifyListeners]). For this reason,
29 | /// this value should only be set between frames, e.g. in response to user
30 | /// actions, not during the build, layout, or paint phases.
31 | set selection(TextSelection newSelection) {
32 | if (newSelection.start > text.length || newSelection.end > text.length)
33 | throw FlutterError('invalid text selection: $newSelection');
34 | value = value.copyWith(selection: newSelection, composing: TextRange.empty);
35 | }
36 |
37 | set value(TextEditingValue newValue) {
38 |
39 | newValue = newValue.copyWith( // 修正由于默认值导致的Bug
40 | composing: TextRange(
41 | start: newValue.composing.start < 0 ? 0: newValue.composing.start,
42 | end: newValue.composing.end < 0 ? 0: newValue.composing.end
43 | ),
44 | selection: newValue.selection.copyWith(
45 | baseOffset: newValue.selection.baseOffset < 0 ? 0: newValue.selection.baseOffset,
46 | extentOffset: newValue.selection.extentOffset < 0 ? 0: newValue.selection.extentOffset
47 | )
48 | );
49 |
50 | super.value = newValue;
51 | }
52 |
53 | /// Set the [value] to empty.
54 | ///
55 | /// After calling this function, [text] will be the empty string and the
56 | /// selection will be invalid.
57 | ///
58 | /// Calling this will notify all the listeners of this [TextEditingController]
59 | /// that they need to update (it calls [notifyListeners]). For this reason,
60 | /// this method should only be called between frames, e.g. in response to user
61 | /// actions, not during the build, layout, or paint phases.
62 | void clear() {
63 | value = TextEditingValue.empty;
64 | }
65 |
66 | /// Set the composing region to an empty range.
67 | ///
68 | /// The composing region is the range of text that is still being composed.
69 | /// Calling this function indicates that the user is done composing that
70 | /// region.
71 | ///
72 | /// Calling this will notify all the listeners of this [TextEditingController]
73 | /// that they need to update (it calls [notifyListeners]). For this reason,
74 | /// this method should only be called between frames, e.g. in response to user
75 | /// actions, not during the build, layout, or paint phases.
76 | clearComposing() {
77 | value = value.copyWith(composing: TextRange.empty);
78 | }
79 | ///删除一个字符,一般用于键盘的删除键
80 | deleteOne(){
81 | if(selection.baseOffset == 0)
82 | return;
83 | String newText = '';
84 | if(selection.baseOffset != selection.extentOffset)
85 | {
86 | newText = selection.textBefore(text) + selection.textAfter(text);
87 | value = TextEditingValue(
88 | text: newText,
89 | selection: selection.copyWith(
90 | baseOffset:selection.baseOffset,
91 | extentOffset: selection.baseOffset)
92 | );
93 | }else{
94 | newText = text.substring(0,selection.baseOffset - 1) + selection.textAfter(text);
95 | value = TextEditingValue(
96 | text: newText,
97 | selection: selection.copyWith(
98 | baseOffset:selection.baseOffset - 1,
99 | extentOffset: selection.baseOffset - 1)
100 | );
101 | }
102 | }
103 |
104 | /// 在光标位置添加文字,一般用于键盘输入
105 | addText(String insertText){
106 | String newText = selection.textBefore(text) + insertText + selection.textAfter(text);
107 | value = TextEditingValue(
108 | text: newText,
109 | selection: selection.copyWith(
110 | baseOffset:selection.baseOffset + insertText.length,
111 | extentOffset: selection.baseOffset + insertText.length)
112 | );
113 | }
114 |
115 | /// 完成
116 | doneAction(){
117 | CoolKeyboard.sendPerformAction(TextInputAction.done);
118 | }
119 |
120 | /// 下一个
121 | nextAction(){
122 | CoolKeyboard.sendPerformAction(TextInputAction.next);
123 | }
124 |
125 | /// 换行
126 | newLineAction(){
127 | CoolKeyboard.sendPerformAction(TextInputAction.newline);
128 | }
129 |
130 | ///发送其他Action
131 | sendPerformAction(TextInputAction action){
132 | CoolKeyboard.sendPerformAction(action);
133 | }
134 | }
--------------------------------------------------------------------------------
/lib/keyboards/keyboard_manager.dart:
--------------------------------------------------------------------------------
1 | part of cool_ui;
2 |
3 | typedef GetKeyboardHeight = double Function(BuildContext context);
4 | typedef KeyboardBuilder = Widget Function(
5 | BuildContext context, KeyboardController controller, String? param);
6 |
7 | class CoolKeyboard {
8 | static JSONMethodCodec _codec = const JSONMethodCodec();
9 | static KeyboardConfig? _currentKeyboard;
10 | static Map _keyboards = {};
11 | static KeyboardRootState? _root;
12 | static BuildContext? _context;
13 | static KeyboardController? _keyboardController;
14 | static GlobalKey? _pageKey;
15 | static bool isInterceptor = false;
16 |
17 | static ValueNotifier _keyboardHeightNotifier = ValueNotifier(0)
18 | ..addListener(updateKeyboardHeight);
19 |
20 | static String? _keyboardParam;
21 |
22 | static Timer? clearTask;
23 |
24 | static init(KeyboardRootState root, BuildContext context) {
25 | _root = root;
26 | _context = context;
27 | interceptorInput();
28 | }
29 |
30 | static interceptorInput() {
31 | if (isInterceptor) return;
32 | if (!(ServicesBinding.instance is MockBinding)) {
33 | throw Exception('CoolKeyboard can only be used in MockBinding');
34 | }
35 | var mockBinding = ServicesBinding.instance as MockBinding;
36 | var mockBinaryMessenger =
37 | mockBinding.defaultBinaryMessenger as MockBinaryMessenger;
38 | mockBinaryMessenger.setMockMessageHandler(
39 | "flutter/textinput", _textInputHanlde);
40 | isInterceptor = true;
41 | }
42 |
43 | static Future _textInputHanlde(ByteData? data) async {
44 | var methodCall = _codec.decodeMethodCall(data);
45 | switch (methodCall.method) {
46 | case 'TextInput.show':
47 | if (_currentKeyboard != null) {
48 | if (clearTask != null) {
49 | clearTask!.cancel();
50 | clearTask = null;
51 | }
52 | openKeyboard();
53 | return _codec.encodeSuccessEnvelope(null);
54 | } else {
55 | if (data != null) {
56 | return await _sendPlatformMessage("flutter/textinput", data);
57 | }
58 | }
59 | break;
60 | case 'TextInput.hide':
61 | if (_currentKeyboard != null) {
62 | if (clearTask == null) {
63 | clearTask = new Timer(Duration(milliseconds: 16),
64 | () => hideKeyboard(animation: true));
65 | }
66 | return _codec.encodeSuccessEnvelope(null);
67 | } else {
68 | if (data != null) {
69 | return await _sendPlatformMessage("flutter/textinput", data);
70 | }
71 | }
72 | break;
73 | case 'TextInput.setEditingState':
74 | var editingState = TextEditingValue.fromJSON(methodCall.arguments);
75 | if (_keyboardController != null) {
76 | _keyboardController!.value = editingState;
77 | return _codec.encodeSuccessEnvelope(null);
78 | }
79 | break;
80 | case 'TextInput.clearClient':
81 | var isShow = _currentKeyboard != null;
82 | if (clearTask == null) {
83 | clearTask = new Timer(
84 | Duration(milliseconds: 16), () => hideKeyboard(animation: true));
85 | }
86 | clearKeyboard();
87 | if (isShow) {
88 | return _codec.encodeSuccessEnvelope(null);
89 | }
90 | break;
91 | case 'TextInput.setClient':
92 | var setInputType = methodCall.arguments[1]['inputType'];
93 | InputClient? client;
94 | _keyboards.forEach((inputType, keyboardConfig) {
95 | if (inputType.name == setInputType['name']) {
96 | client = InputClient.fromJSON(methodCall.arguments);
97 |
98 | _keyboardParam =
99 | (client!.configuration.inputType as CKTextInputType).params;
100 |
101 | clearKeyboard();
102 | _currentKeyboard = keyboardConfig;
103 | _keyboardController = KeyboardController(client: client!)
104 | ..addListener(_updateEditingState);
105 | if (_pageKey != null) {
106 | _pageKey!.currentState?.update();
107 | }
108 | }
109 | });
110 |
111 | if (client != null) {
112 | await _sendPlatformMessage("flutter/textinput",
113 | _codec.encodeMethodCall(MethodCall('TextInput.hide')));
114 | return _codec.encodeSuccessEnvelope(null);
115 | } else {
116 | if (clearTask == null) {
117 | hideKeyboard(animation: false);
118 | }
119 | clearKeyboard();
120 | }
121 | // break;
122 | }
123 | if (data != null) {
124 | ByteData? response =
125 | await _sendPlatformMessage("flutter/textinput", data);
126 | return response;
127 | }
128 | return null;
129 | }
130 |
131 | static void _updateEditingState() {
132 | var callbackMethodCall = MethodCall("TextInputClient.updateEditingState", [
133 | _keyboardController!.client.connectionId,
134 | _keyboardController!.value.toJSON()
135 | ]);
136 | WidgetsBinding.instance.defaultBinaryMessenger.handlePlatformMessage(
137 | "flutter/textinput",
138 | _codec.encodeMethodCall(callbackMethodCall),
139 | (data) {});
140 | }
141 |
142 | static Future _sendPlatformMessage(
143 | String channel, ByteData message) {
144 | final Completer completer = Completer();
145 | ui.window.sendPlatformMessage(channel, message, (ByteData? reply) {
146 | try {
147 | completer.complete(reply);
148 | } catch (exception, stack) {
149 | FlutterError.reportError(FlutterErrorDetails(
150 | exception: exception,
151 | stack: stack,
152 | library: 'services library',
153 | context:
154 | ErrorDescription('during a platform message response callback'),
155 | ));
156 | }
157 | });
158 | return completer.future;
159 | }
160 |
161 | static addKeyboard(CKTextInputType inputType, KeyboardConfig config) {
162 | _keyboards[inputType] = config;
163 | }
164 |
165 | static openKeyboard() {
166 | var keyboardHeight = _currentKeyboard!.getHeight(_context!);
167 | _keyboardHeightNotifier.value = keyboardHeight;
168 | if (_root!.hasKeyboard && _pageKey != null) return;
169 | _pageKey = GlobalKey();
170 | // KeyboardMediaQueryState queryState = _context
171 | // .ancestorStateOfType(const TypeMatcher())
172 | // as KeyboardMediaQueryState;
173 | // queryState.update();
174 |
175 | var tempKey = _pageKey;
176 | var isUpdate = false;
177 | _root!.setKeyboard((ctx) {
178 | if (_currentKeyboard != null && _keyboardHeightNotifier.value != 0) {
179 | if (!isUpdate) {
180 | isUpdate = true;
181 | // WidgetsBinding.instance.addPostFrameCallback((_) {
182 | // _keyboardController!.addText('1');
183 | // });
184 | }
185 | return KeyboardPage(
186 | key: tempKey,
187 | builder: (ctx) {
188 | return _currentKeyboard?.builder(
189 | ctx, _keyboardController!, _keyboardParam);
190 | },
191 | height: _keyboardHeightNotifier.value);
192 | } else {
193 | return Container();
194 | }
195 | });
196 |
197 | BackButtonInterceptor.add((_, __) {
198 | CoolKeyboard.sendPerformAction(TextInputAction.done);
199 | return true;
200 | }, zIndex: 1, name: 'CustomKeyboard');
201 | }
202 |
203 | static hideKeyboard({bool animation = true}) {
204 | if (clearTask != null) {
205 | if (clearTask!.isActive) {
206 | clearTask!.cancel();
207 | }
208 | clearTask = null;
209 | }
210 | BackButtonInterceptor.removeByName('CustomKeyboard');
211 | if (_root!.hasKeyboard && _pageKey != null) {
212 | // _pageKey.currentState.animationController
213 | // .addStatusListener((AnimationStatus status) {
214 | // if (status == AnimationStatus.dismissed ||
215 | // status == AnimationStatus.completed) {
216 | // if (_root.hasKeyboard) {
217 | // _keyboardEntry.remove();
218 | // _keyboardEntry = null;
219 | // }
220 | // }
221 | // });
222 | if (animation) {
223 | _pageKey!.currentState?.exitKeyboard();
224 | Future.delayed(Duration(milliseconds: 116)).then((_) {
225 | _root!.clearKeyboard();
226 | });
227 | } else {
228 | _root!.clearKeyboard();
229 | }
230 | }
231 | _pageKey = null;
232 | _keyboardHeightNotifier.value = 0;
233 | try {
234 | // KeyboardMediaQueryState queryState = _context
235 | // .ancestorStateOfType(const TypeMatcher())
236 | // as KeyboardMediaQueryState;
237 | // queryState.update();
238 | } catch (_) {}
239 | }
240 |
241 | static clearKeyboard() {
242 | _currentKeyboard = null;
243 | if (_keyboardController != null) {
244 | _keyboardController!.dispose();
245 | _keyboardController = null;
246 | }
247 | }
248 |
249 | static sendPerformAction(TextInputAction action) {
250 | var callbackMethodCall = MethodCall("TextInputClient.performAction",
251 | [_keyboardController!.client.connectionId, action.toString()]);
252 | WidgetsBinding.instance.defaultBinaryMessenger.handlePlatformMessage(
253 | "flutter/textinput",
254 | _codec.encodeMethodCall(callbackMethodCall),
255 | (data) {});
256 | }
257 |
258 | static updateKeyboardHeight() {
259 | if (_pageKey != null &&
260 | _pageKey!.currentState != null &&
261 | clearTask == null) {
262 | _pageKey!.currentState!.updateHeight(_keyboardHeightNotifier.value);
263 | }
264 | }
265 | }
266 |
267 | class KeyboardConfig {
268 | final KeyboardBuilder builder;
269 | final GetKeyboardHeight getHeight;
270 | const KeyboardConfig({required this.builder, required this.getHeight});
271 | }
272 |
273 | class InputClient {
274 | final int connectionId;
275 | final TextInputConfiguration configuration;
276 | const InputClient({required this.connectionId, required this.configuration});
277 |
278 | factory InputClient.fromJSON(List encoded) {
279 | return InputClient(
280 | connectionId: encoded[0],
281 | configuration: TextInputConfiguration(
282 | inputType: CKTextInputType.fromJSON(encoded[1]['inputType']),
283 | obscureText: encoded[1]['obscureText'],
284 | autocorrect: encoded[1]['autocorrect'],
285 | actionLabel: encoded[1]['actionLabel'],
286 | inputAction: _toTextInputAction(encoded[1]['inputAction']),
287 | textCapitalization:
288 | _toTextCapitalization(encoded[1]['textCapitalization']),
289 | keyboardAppearance:
290 | _toBrightness(encoded[1]['keyboardAppearance'])));
291 | }
292 |
293 | static TextInputAction _toTextInputAction(String action) {
294 | switch (action) {
295 | case 'TextInputAction.none':
296 | return TextInputAction.none;
297 | case 'TextInputAction.unspecified':
298 | return TextInputAction.unspecified;
299 | case 'TextInputAction.go':
300 | return TextInputAction.go;
301 | case 'TextInputAction.search':
302 | return TextInputAction.search;
303 | case 'TextInputAction.send':
304 | return TextInputAction.send;
305 | case 'TextInputAction.next':
306 | return TextInputAction.next;
307 | case 'TextInputAction.previuos':
308 | return TextInputAction.previous;
309 | case 'TextInputAction.continue_action':
310 | return TextInputAction.continueAction;
311 | case 'TextInputAction.join':
312 | return TextInputAction.join;
313 | case 'TextInputAction.route':
314 | return TextInputAction.route;
315 | case 'TextInputAction.emergencyCall':
316 | return TextInputAction.emergencyCall;
317 | case 'TextInputAction.done':
318 | return TextInputAction.done;
319 | case 'TextInputAction.newline':
320 | return TextInputAction.newline;
321 | }
322 | throw FlutterError('Unknown text input action: $action');
323 | }
324 |
325 | static TextCapitalization _toTextCapitalization(String capitalization) {
326 | switch (capitalization) {
327 | case 'TextCapitalization.none':
328 | return TextCapitalization.none;
329 | case 'TextCapitalization.characters':
330 | return TextCapitalization.characters;
331 | case 'TextCapitalization.sentences':
332 | return TextCapitalization.sentences;
333 | case 'TextCapitalization.words':
334 | return TextCapitalization.words;
335 | }
336 |
337 | throw FlutterError('Unknown text capitalization: $capitalization');
338 | }
339 |
340 | static Brightness _toBrightness(String brightness) {
341 | switch (brightness) {
342 | case 'Brightness.dark':
343 | return Brightness.dark;
344 | case 'Brightness.light':
345 | return Brightness.light;
346 | }
347 |
348 | throw FlutterError('Unknown Brightness: $brightness');
349 | }
350 | }
351 |
352 | class CKTextInputType extends TextInputType {
353 | final String name;
354 | final String? params;
355 |
356 | const CKTextInputType(
357 | {required this.name, bool? signed, bool? decimal, this.params})
358 | : super.numberWithOptions(signed: signed, decimal: decimal);
359 |
360 | @override
361 | Map toJson() {
362 | return {
363 | 'name': name,
364 | 'signed': signed,
365 | 'decimal': decimal,
366 | 'params': params
367 | };
368 | }
369 |
370 | @override
371 | String toString() {
372 | return '$runtimeType('
373 | 'name: $name, '
374 | 'signed: $signed, '
375 | 'decimal: $decimal)';
376 | }
377 |
378 | bool operator ==(Object target) {
379 | if (target is CKTextInputType) {
380 | if (this.name == target.toString()) {
381 | return true;
382 | }
383 | }
384 | return false;
385 | }
386 |
387 | @override
388 | int get hashCode => this.toString().hashCode;
389 |
390 | factory CKTextInputType.fromJSON(Map encoded) {
391 | return CKTextInputType(
392 | name: encoded['name'],
393 | signed: encoded['signed'],
394 | decimal: encoded['decimal'],
395 | params: encoded['params']);
396 | }
397 | }
398 |
399 | class KeyboardPage extends StatefulWidget {
400 | final Widget? Function(BuildContext context) builder;
401 | final double height;
402 | const KeyboardPage({required this.builder, this.height = 0, Key? key})
403 | : super(key: key);
404 |
405 | @override
406 | State createState() => KeyboardPageState();
407 | }
408 |
409 | class KeyboardPageState extends State {
410 | Widget? _lastBuildWidget;
411 | bool isClose = false;
412 | double _height = 0;
413 |
414 | @override
415 | void initState() {
416 | // TODO: implement initState
417 | super.initState();
418 |
419 | WidgetsBinding.instance.addPostFrameCallback((_) {
420 | _height = widget.height;
421 | setState(() => {});
422 | });
423 | }
424 |
425 | @override
426 | Widget build(BuildContext context) {
427 | return AnimatedPositioned(
428 | child: IntrinsicHeight(child: Builder(
429 | builder: (ctx) {
430 | var result = widget.builder(ctx);
431 | if (result != null) {
432 | _lastBuildWidget = result;
433 | }
434 | return ConstrainedBox(
435 | constraints: BoxConstraints(
436 | minHeight: 0,
437 | minWidth: 0,
438 | maxHeight: _height,
439 | maxWidth: _ScreenUtil.getScreenW(context)),
440 | child: _lastBuildWidget,
441 | );
442 | },
443 | )),
444 | left: 0,
445 | width: _ScreenUtil.getScreenW(context),
446 | bottom: _height * (isClose ? -1 : 0),
447 | height: _height,
448 | duration: Duration(milliseconds: 100),
449 | );
450 | }
451 |
452 | @override
453 | void dispose() {
454 | // if (animationController.status == AnimationStatus.forward ||
455 | // animationController.status == AnimationStatus.reverse) {
456 | // animationController.notifyStatusListeners(AnimationStatus.dismissed);
457 | // }
458 | // animationController.dispose();
459 | super.dispose();
460 | }
461 |
462 | exitKeyboard() {
463 | isClose = true;
464 | }
465 |
466 | update() {
467 | WidgetsBinding.instance.addPostFrameCallback((_) {
468 | setState(() => {});
469 | });
470 | }
471 |
472 | updateHeight(double height) {
473 | WidgetsBinding.instance.addPostFrameCallback((_) {
474 | this._height = height;
475 | setState(() => {});
476 | });
477 | }
478 | }
479 |
--------------------------------------------------------------------------------
/lib/keyboards/keyboard_media_query.dart:
--------------------------------------------------------------------------------
1 | part of cool_ui;
2 |
3 | class KeyboardMediaQuery extends StatefulWidget {
4 | final Widget child;
5 |
6 | KeyboardMediaQuery({required this.child});
7 |
8 | @override
9 | State createState() => KeyboardMediaQueryState();
10 | }
11 |
12 | class KeyboardMediaQueryState extends State {
13 | double keyboardHeight = 0;
14 | ValueNotifier keyboardHeightNotifier =
15 | CoolKeyboard._keyboardHeightNotifier;
16 |
17 | @override
18 | void initState() {
19 | super.initState();
20 | CoolKeyboard._keyboardHeightNotifier.addListener(onUpdateHeight);
21 | }
22 |
23 | @override
24 | Widget build(BuildContext context) {
25 | // TODO: implement build
26 | var data = MediaQuery.maybeOf(context);
27 | if (data == null) {
28 | data = MediaQueryData.fromWindow(WidgetsBinding.instance.window);
29 | }
30 | var bottom = CoolKeyboard._keyboardHeightNotifier.value != 0
31 | ? CoolKeyboard._keyboardHeightNotifier.value
32 | : data.viewInsets.bottom;
33 | // TODO: implement build
34 | return MediaQuery(
35 | child: widget.child,
36 | data: data.copyWith(
37 | viewInsets: data.viewInsets.copyWith(bottom: bottom)));
38 | }
39 |
40 | onUpdateHeight() {
41 | SchedulerBinding.instance.addPostFrameCallback((_) {
42 | setState(() => {});
43 | SchedulerBinding.instance.addPostFrameCallback((_) {
44 | WidgetsBinding.instance.handleMetricsChanged();
45 | });
46 | });
47 | }
48 |
49 | @override
50 | void dispose() {
51 | super.dispose();
52 | CoolKeyboard._keyboardHeightNotifier.removeListener(onUpdateHeight);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/lib/keyboards/keyboard_root.dart:
--------------------------------------------------------------------------------
1 | part of cool_ui;
2 |
3 | class KeyboardRootWidget extends StatefulWidget {
4 | final Widget child;
5 |
6 | /// The text direction for this subtree.
7 | final TextDirection textDirection;
8 |
9 | const KeyboardRootWidget(
10 | {Key? key, required this.child, this.textDirection = TextDirection.ltr})
11 | : super(key: key);
12 |
13 | @override
14 | State createState() {
15 | // TODO: implement createState
16 | return KeyboardRootState();
17 | }
18 | }
19 |
20 | class KeyboardRootState extends State {
21 | WidgetBuilder? _keyboardbuilder;
22 |
23 | bool get hasKeyboard => _keyboardbuilder != null;
24 | // List _initialEntries = [];
25 |
26 | @override
27 | void initState() {
28 | super.initState();
29 | // _initialEntries.add(this.initChild());
30 | }
31 |
32 | @override
33 | Widget build(BuildContext context) {
34 | // TODO: implement build
35 | return KeyboardMediaQuery(child: Builder(builder: (context) {
36 | CoolKeyboard.init(this, context);
37 |
38 | List children = [widget.child];
39 | if (_keyboardbuilder != null) {
40 | children.add(Builder(
41 | builder: _keyboardbuilder!,
42 | ));
43 | }
44 | return Directionality(
45 | textDirection: widget.textDirection,
46 | child: Stack(
47 | children: children,
48 | ));
49 | }));
50 | }
51 |
52 | setKeyboard(WidgetBuilder keyboardbuilder) {
53 | this._keyboardbuilder = keyboardbuilder;
54 | setState(() {});
55 | }
56 |
57 | clearKeyboard() {
58 | if (this._keyboardbuilder != null) {
59 | this._keyboardbuilder = null;
60 | setState(() {});
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/lib/keyboards/mocks/mock_binary_messenger.dart:
--------------------------------------------------------------------------------
1 | part of cool_ui;
2 |
3 | //Copy By TestDefaultBinaryMessenger
4 |
5 | class MockBinaryMessenger extends BinaryMessenger {
6 | /// Creates a [MockBinaryMessenger] instance.
7 | ///
8 | MockBinaryMessenger(this.mockBinding);
9 |
10 | /// 用于获取delegate
11 | final MockBinding mockBinding;
12 |
13 | // The handlers for messages from the engine (including fake
14 | // messages sent by handlePlatformMessage).
15 | final Map _inboundHandlers =
16 | {};
17 |
18 | /// Send a mock message to the framework as if it came from the platform.
19 | ///
20 | /// If a listener has been set using [setMessageHandler], that listener is
21 | /// invoked to handle the message, and this method returns a future that
22 | /// completes with that handler's result.
23 | ///
24 | /// {@template flutter.flutter_test.MockBinaryMessenger.handlePlatformMessage.asyncHandlers}
25 | /// It is strongly recommended that all handlers used with this API be
26 | /// synchronous (not requiring any microtasks to complete), because
27 | /// [testWidgets] tests run in a [FakeAsync] zone in which microtasks do not
28 | /// progress except when time is explicitly advanced (e.g. with
29 | /// [WidgetTester.pump]), which means that `await`ing a [Future] will result
30 | /// in the test hanging.
31 | /// {@endtemplate}
32 | ///
33 | /// If no listener is configured, this method returns right away with null.
34 | ///
35 | /// The `callback` argument, if non-null, will be called just before this
36 | /// method's future completes, either with the result of the listener
37 | /// registered with [setMessageHandler], or with null if no listener has
38 | /// been registered.
39 | ///
40 | /// Messages can also be sent via [ChannelBuffers.push] (see
41 | /// [ServicesBinding.channelBuffers]); the effect is the same, though that API
42 | /// will not wait for a response.
43 | // TODO(ianh): When the superclass `handlePlatformMessage` is removed,
44 | // remove this @override (but leave the method).
45 | @override
46 | Future handlePlatformMessage(
47 | String channel,
48 | ByteData? data,
49 | ui.PlatformMessageResponseCallback? callback,
50 | ) {
51 | Future? result;
52 | if (_inboundHandlers.containsKey(channel))
53 | result = _inboundHandlers[channel]!(data);
54 | result ??= Future.value(null);
55 | if (callback != null)
56 | result = result.then((ByteData? result) {
57 | callback(result);
58 | return result;
59 | });
60 | return result;
61 | }
62 |
63 | @override
64 | void setMessageHandler(String channel, MessageHandler? handler) {
65 | if (handler == null) {
66 | _inboundHandlers.remove(channel);
67 | mockBinding._superDefaultBinaryMessenger.setMessageHandler(channel, null);
68 | } else {
69 | _inboundHandlers[channel] =
70 | handler; // used to handle fake messages sent via handlePlatformMessage
71 | mockBinding._superDefaultBinaryMessenger.setMessageHandler(
72 | channel, handler); // used to handle real messages from the engine
73 | }
74 | }
75 |
76 | final List> _pendingMessages = >[];
77 |
78 | /// The number of incomplete/pending calls sent to the platform channels.
79 | int get pendingMessageCount => _pendingMessages.length;
80 |
81 | // Handlers that intercept and respond to outgoing messages,
82 | // pretending to be the platform.
83 | final Map _outboundHandlers =
84 | {};
85 |
86 | // The outbound callbacks that were actually registered, so that we
87 | // can implement the [checkMockMessageHandler] method.
88 | final Map _outboundHandlerIdentities = {};
89 |
90 | @override
91 | Future? send(String channel, ByteData? message) {
92 | final Future? resultFuture;
93 | final MessageHandler? handler = _outboundHandlers[channel];
94 | if (handler != null) {
95 | resultFuture = handler(message);
96 | } else {
97 | resultFuture =
98 | mockBinding._superDefaultBinaryMessenger.send(channel, message);
99 | }
100 | if (resultFuture != null) {
101 | _pendingMessages.add(resultFuture);
102 | resultFuture.catchError((Object error) {
103 | /* errors are the responsibility of the caller */
104 | }).whenComplete(() => _pendingMessages.remove(resultFuture));
105 | }
106 | return resultFuture;
107 | }
108 |
109 | /// Returns a Future that completes after all the platform calls are finished.
110 | ///
111 | /// If a new platform message is sent after this method is called, this new
112 | /// message is not tracked. Use with [pendingMessageCount] to guarantee no
113 | /// pending message calls.
114 | Future get platformMessagesFinished {
115 | return Future.wait(_pendingMessages);
116 | }
117 |
118 | /// Set a callback for intercepting messages sent to the platform on
119 | /// the given channel, without decoding them.
120 | ///
121 | /// Intercepted messages are not forwarded to the platform.
122 | ///
123 | /// The given callback will replace the currently registered
124 | /// callback for that channel, if any. To stop intercepting messages
125 | /// at all, pass null as the handler.
126 | ///
127 | /// The handler's return value, if non-null, is used as a response,
128 | /// unencoded.
129 | ///
130 | /// {@macro flutter.flutter_test.MockBinaryMessenger.handlePlatformMessage.asyncHandlers}
131 | ///
132 | /// The `identity` argument, if non-null, is used to identify the
133 | /// callback when checked by [checkMockMessageHandler]. If null, the
134 | /// `handler` is used instead. (This allows closures to be passed as
135 | /// the `handler` with an alias used as the `identity` so that a
136 | /// reference to the closure need not be used. In practice, this is
137 | /// used by [setMockDecodedMessageHandler] and
138 | /// [setMockMethodCallHandler] to allow [checkMockMessageHandler] to
139 | /// recognize the closures that were passed to those methods even
140 | /// though those methods wrap those closures when passing them to
141 | /// this method.)
142 | ///
143 | /// Registered callbacks are cleared after each test.
144 | ///
145 | /// See also:
146 | ///
147 | /// * [checkMockMessageHandler], which can verify if a handler is still
148 | /// registered, which is useful in tests to ensure that no unexpected
149 | /// handlers are being registered.
150 | ///
151 | /// * [setMockDecodedMessageHandler], which wraps this method but
152 | /// decodes the messages using a [MessageCodec].
153 | ///
154 | /// * [setMockMethodCallHandler], which wraps this method but decodes
155 | /// the messages using a [MethodCodec].
156 | void setMockMessageHandler(String channel, MessageHandler? handler,
157 | [Object? identity]) {
158 | if (handler == null) {
159 | _outboundHandlers.remove(channel);
160 | _outboundHandlerIdentities.remove(channel);
161 | } else {
162 | identity ??= handler;
163 | _outboundHandlers[channel] = handler;
164 | _outboundHandlerIdentities[channel] = identity;
165 | }
166 | }
167 |
168 | /// Set a callback for intercepting messages sent to the platform on
169 | /// the given channel.
170 | ///
171 | /// Intercepted messages are not forwarded to the platform.
172 | ///
173 | /// The given callback will replace the currently registered
174 | /// callback for that channel, if any. To stop intercepting messages
175 | /// at all, pass null as the handler.
176 | ///
177 | /// Messages are decoded using the codec of the channel.
178 | ///
179 | /// The handler's return value, if non-null, is used as a response,
180 | /// after encoding it using the channel's codec.
181 | ///
182 | /// {@macro flutter.flutter_test.MockBinaryMessenger.handlePlatformMessage.asyncHandlers}
183 | ///
184 | /// Registered callbacks are cleared after each test.
185 | ///
186 | /// See also:
187 | ///
188 | /// * [checkMockMessageHandler], which can verify if a handler is still
189 | /// registered, which is useful in tests to ensure that no unexpected
190 | /// handlers are being registered.
191 | ///
192 | /// * [setMockMessageHandler], which is similar but provides raw
193 | /// access to the underlying bytes.
194 | ///
195 | /// * [setMockMethodCallHandler], which is similar but decodes
196 | /// the messages using a [MethodCodec].
197 | void setMockDecodedMessageHandler(
198 | BasicMessageChannel channel, Future Function(T? message)? handler) {
199 | if (handler == null) {
200 | setMockMessageHandler(channel.name, null);
201 | return;
202 | }
203 | setMockMessageHandler(channel.name, (ByteData? message) async {
204 | return channel.codec
205 | .encodeMessage(await handler(channel.codec.decodeMessage(message)));
206 | }, handler);
207 | }
208 |
209 | /// Set a callback for intercepting method calls sent to the
210 | /// platform on the given channel.
211 | ///
212 | /// Intercepted method calls are not forwarded to the platform.
213 | ///
214 | /// The given callback will replace the currently registered
215 | /// callback for that channel, if any. To stop intercepting messages
216 | /// at all, pass null as the handler.
217 | ///
218 | /// Methods are decoded using the codec of the channel.
219 | ///
220 | /// The handler's return value, if non-null, is used as a response,
221 | /// after re-encoding it using the channel's codec.
222 | ///
223 | /// To send an error, throw a [PlatformException] in the handler.
224 | /// Other exceptions are not caught.
225 | ///
226 | /// {@macro flutter.flutter_test.MockBinaryMessenger.handlePlatformMessage.asyncHandlers}
227 | ///
228 | /// Registered callbacks are cleared after each test.
229 | ///
230 | /// See also:
231 | ///
232 | /// * [checkMockMessageHandler], which can verify if a handler is still
233 | /// registered, which is useful in tests to ensure that no unexpected
234 | /// handlers are being registered.
235 | ///
236 | /// * [setMockMessageHandler], which is similar but provides raw
237 | /// access to the underlying bytes.
238 | ///
239 | /// * [setMockDecodedMessageHandler], which is similar but decodes
240 | /// the messages using a [MessageCodec].
241 | void setMockMethodCallHandler(MethodChannel channel,
242 | Future