diff --git a/.gitignore b/.gitignore index 9213b3353..fe733f8be 100644 --- a/.gitignore +++ b/.gitignore @@ -29,7 +29,13 @@ .pub-cache/ .pub/ /build/ +/android/app/.cxx/ /lib/tools/ /lib/res/constant/github_client_config.dart # Exceptions to above rules. !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages + +/build_tools +/libs +/pubspec_overrides.yaml +modules/game_system diff --git a/README-EN.md b/README-EN.md new file mode 100644 index 000000000..13cf46805 --- /dev/null +++ b/README-EN.md @@ -0,0 +1,239 @@ +

+ + FlutterUnit🔖
+
+ ⭐️ All Platform Flutter Experience App ⭐️
+

+ +

+FlutterUnit is a cross-platform experience app, Here, you can fully explore the creativity that Flutter offers. +

+ +

+ + +License: GPL-3.0 +

+ +

+ Download v3.0.0 : + [Android] • + [iOS] • + [MacOS] • + [Windows] • + [Web] +

+ +

FlutterUnit App

+ +--- + + +### Env and Build + +#### Flutter Version + +``` +·]>> flutter --version +Flutter 3.35.1 • channel stable • https://github.com/flutter/flutter.git +Framework • revision 20f8274939 (6 days ago) • 2025-08-14 10:53:09 -0700 +Engine • hash 6cd51c08a88e7bbe848a762c20ad3ecb8b063c0e (revision 1e9a811bf8) (7 days ago) • 2025-08-13 23:35:25.000Z +Tools • Dart 3.9.0 • DevTools 2.48.0 +``` + +#### Build Application + +``` +·]>> git clone https://github.com/toly1994328/FlutterUnit.git +·]>> cd FlutterUnit + +Build Android: +·]>> flutter build apk --target-platform --split-per-abi +Build iOS: +·]>> flutter build ios +Build Windows: +·]>> flutter build windows +Build Linux: +·]>> flutter build linux +Build web: +·]>> flutter build web +``` + + +#### My Flutter Books +- 🔥 [免费] [掘金小册 -《Flutter 入门教程》](https://juejin.cn/book/7212822723330834487) +- 🔥 [掘金小册 -《Flutter 语言基础 - 梦始之地》](https://juejin.cn/book/6844733827617652750) +- 🔥 [掘金小册 -《Flutter 绘制指南 - 妙笔生花》](https://juejin.im/book/6844733827265331214) +- 🔥 [掘金小册 -《Flutter 手势探索 - 执掌天下》](https://juejin.cn/book/6896378716427911181) +- 🔥 [掘金小册 -《Flutter 动画探索 - 流光幻影》](https://juejin.cn/book/6965102582473687071) +- 🔥 [掘金小册 -《Flutter 滑动探索 - 珠联璧合》](https://juejin.cn/book/6984685333312962573) +- 🔥 [掘金小册 -《Flutter 布局探索 - 薪火相传》](https://juejin.cn/book/7075958265250578469) +- 🔥 [掘金小册 -《Flutter 渲染机制 - 聚沙成塔》](https://juejin.cn/book/6965102582473687071) + +--- + +- [Flutter环境配置](https://github.com/toly1994328/FlutterUnit/issues/22) +- [Flutter实用插件集录 ](https://github.com/toly1994328/FlutterUnit/issues/41) +- [Flutter要点集录 ](https://github.com/toly1994328/FlutterUnit/labels/point) + + +--- + + +#### MacOS 桌面版本组件界面 + +![](./doc/screens/macos-2.webp) + +#### Windows 桌面版本组件界面 + +![](./doc/screens/windows-1.png) + + +> 开源不易,请我喝咖啡 ~ + +![](./doc/ewm/coffee1.png) + + + +#### Star History + +[![Star History Chart](https://api.star-history.com/svg?repos=toly1994328/FlutterUnit&type=Date)](https://star-history.com/#toly1994328/FlutterUnit&Date) + +### 一、组件的展示页面 + +#### 1. `300+组件收录` + +> Flutter源码中的可用的组件一共350个左右,纷繁复杂,也没有明确的分类标准 +FlutterUnit 对`大大小小,常用不常用`的组件能收的尽量收录。并`根据个人感觉进行评星 ` +`目前收录组件306个`,每个都有至少一个演示展现和代码展示。 + +| . | . | . | +|------|------------|------------| +| ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6acf7b8a1d~tplv-t2oaga2asx-zoom-1.image)| ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6ad06db455~tplv-t2oaga2asx-image.image)| ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6ad085632b~tplv-t2oaga2asx-image.image) | + +--- + +#### 2. 组件详情页 + +> `213个组件`全部都有详情页。对于重要的组件会详细展现 +一般都会有某个演示对应的组件和属性,尽量做到细致,如果有需要补充,欢迎联系我。 +`最重要的是: 所有的演示展现都是Flutter的组件形成的,而非图片,这就意味着可操作性更高。` + +| . | . | . | +|------|------------|------------| +| ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6ad8ba98f1~tplv-t2oaga2asx-image.image)| ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6afb3841c4~tplv-t2oaga2asx-image.image)| ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6afb590185~tplv-t2oaga2asx-image.image) | +| ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6b0ad26b14~tplv-t2oaga2asx-image.image) | ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6b13d3fb5b~tplv-t2oaga2asx-image.image) |![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6b15efec19~tplv-t2oaga2asx-image.image)| + +--- + +#### 3. 组件的可操作性 + +> 对一些操作交互的组件或有可操作性的某些组件,`提供操作演示` + +| . | . | . | +|------|------------|------------| +| ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6b177c5b67~tplv-t2oaga2asx-image.image)| ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6b21cc116a~tplv-t2oaga2asx-image.image)| ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6b2486b5a5~tplv-t2oaga2asx-image.image)| +| ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6b34887a94~tplv-t2oaga2asx-image.image) | ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6b3ca09b47~tplv-t2oaga2asx-image.image) |![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6b3d4e0253~tplv-t2oaga2asx-image.image)| + +--- + +#### 4. 相关组件的关联切换 +> `相关组件通过link to 可以进行切换, 满足你的探索欲。` +如果有的关联未加入,欢迎联系我,对我来说,加个数字就行了。 + +| . | . | . | +|------|------------|------------| +|![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6b4a4d6005~tplv-t2oaga2asx-image.image)|![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6b5066fbf0~tplv-t2oaga2asx-image.image)| ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6b5359b695~tplv-t2oaga2asx-image.image)| + + +--- + +#### 5. 代码的查看和分享 +> 激动人心的是,你可以通过右侧的图标`展开/隐藏 实现下面效果的代码` +并且`支持分享`,如果你想亲自体验,so,easy ! 而且`代码高亮样式可以自定义`。 + + +| . | . | . | +|------|------------|------------| +| ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6b6badc1bb~tplv-t2oaga2asx-image.image)| ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6b6e75653c~tplv-t2oaga2asx-image.image)| ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6b72cdd78f~tplv-t2oaga2asx-image.image)| + +--- + + +### 二、全局配置 + +#### 1. 颜色主题 +> 只提供八种颜色,可在`右滑菜单页`的`我的主题`配置,`可以拓展` + +| . | . | . | +|------|------------|------------| +| ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6c2e937170~tplv-t2oaga2asx-image.image)| ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6c3253c4ec~tplv-t2oaga2asx-image.image) | ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6c4f97f74d~tplv-t2oaga2asx-image.image) | + +--- + + +#### 2.字体配置 + +> 支持全局字体设置,`可以拓展` + +| . | . | . | +|------|------------|------------| +| ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6c5448cb6c~tplv-t2oaga2asx-image.image)| ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6c55542837~tplv-t2oaga2asx-image.image) | ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6c5bec6c56~tplv-t2oaga2asx-image.image)| +--- + + +#### 3.item样式设置 + +> 支持item样式设置,`可以拓展,支持征集`,详见`Flutter Unit 1.0 征集方案` + +| . | . | . | +|------|------------|------------| +|![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6c7d4b5988~tplv-t2oaga2asx-image.image)| ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6c8935dfe1~tplv-t2oaga2asx-image.image)| ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6c8f90d6ec~tplv-t2oaga2asx-image.image)| +--- + +#### 4.代码面板风格设置 + +> 支持代码风格设置,`可以拓展,支持征集`,详见`Flutter Unit 1.0 征集方案` + +| . | . | +|------|------------| +|![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6cac86d591~tplv-t2oaga2asx-image.image)| ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6cac7d5bc7~tplv-t2oaga2asx-image.image)| +![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6cb75e5450~tplv-t2oaga2asx-image.image)|![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6cd88c7ff9~tplv-t2oaga2asx-image.image)| + +--- + + + +### 三、搜索与收藏功能 + + +#### 1.搜索功能 + +> 由于Flutter中Widget比较杂乱,不太好分类,所以搜索是非常重要的 +另外可以根据星级进行过滤,支持多选。目前正在考虑根据功能分类,之后会有所完善。 + + +| . | . | . | +|------|------------|------------| +| ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6c1a355ad3~tplv-t2oaga2asx-image.image)| ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6c211dfc99~tplv-t2oaga2asx-image.image)| ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6c28562ec5~tplv-t2oaga2asx-image.image) | + +--- + +#### 2.收藏功能 + +| 添加收藏集 | 修改收藏集 | 删除收藏集 | +|------|------------|------------| +| ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6b7979f4ae~tplv-t2oaga2asx-image.image) | ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6b97f00113~tplv-t2oaga2asx-image.image) | ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6ba47f3fd2~tplv-t2oaga2asx-image.image) | + +| 长按右菜单滑页 | 长按左菜单滑页 | 详情内长按展示收藏菜单 | +|------|------------|------------| +| ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6beb370b8b~tplv-t2oaga2asx-image.image) |![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6befe43cd2~tplv-t2oaga2asx-image.image) | ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6bfd3cd42f~tplv-t2oaga2asx-image.image) | + +| 删除与数据同步 | 组件加入收藏集 | 收藏集支持多选 | +|------|------------|------------| +| ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6ba47ab64c~tplv-t2oaga2asx-image.image) | ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6badf6ee28~tplv-t2oaga2asx-image.image) | ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/3/171dae6bb380c7f5~tplv-t2oaga2asx-image.image) | + +> `FlutterUnit 2.0 `目前基本就是这么多功能,可以在Github中下载打包后的apk玩玩 +希望能对你的Flutter学习有所帮助。 + +--- diff --git a/README.md b/README.md index 1feb59939..0881234c8 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,62 @@ +

+ + FlutterUnit
+
+ ⭐️ 全平台 Flutter 探索应用 ⭐️
+

+ +

+FlutterUnit 是一个全平台体验应用,你可以在这里尽情体验 Flutter 带来的创造力。 +

+ +

+ + +License: GPL-3.0 +

+ +

+ 下载 App: + [Android] • + [iOS] • + [MacOS] • + [Windows] • + [Web] +

+ +

FlutterUnit App

-### FlutterUnit 全平台下载体验: +--- + +### 环境与构建 + +#### Flutter 版本 -| 平台类型 | 下载地址 | 项目分支地址 | 相关文章 | -|------|------------|------|------------| -| Android版 | [FlutterUnit.apk](https://github.com/toly1994328/FlutterUnit/releases/download/v2.9.3/FlutterUnit.apk) |[flutter_unit](https://github.com/toly1994328/FlutterUnit)| [《FlutterUnit食用指南》](https://juejin.im/post/6844904147045597191)| -| iOS版 |[FlutterUnit in AppStore](https://apps.apple.com/cn/app/flutter-unit/id6450545123) |[flutter_unit](https://github.com/toly1994328/FlutterUnit)| [《FlutterUnit 食用指南》](https://juejin.im/post/6844904147045597191)| -| MacOS版 | [FlutterUnitMac.zip](https://github.com/toly1994328/FlutterUnit/releases/download/v2.9.3/FlutterUnitMac.zip) |[flutter_unit](https://github.com/toly1994328/FlutterUnit/tree/flutter_unit)| [《mac版闪亮登场》](https://juejin.im/post/6844904147817332743)| -| Windows版 |[FlutterUnitWin.zip](https://github.com/toly1994328/FlutterUnit/releases/download/v2.9.3/FlutterUnitWin.zip) | [flutter_unit](https://github.com/toly1994328/FlutterUnit/tree/flutter_unit) | [《win版闪亮登场》](https://juejin.im/post/6847902222626488327)| -| Web版 | http://toly1994328.gitee.io/flutter_web | [ flutter_unit_web ](https://github.com/toly1994328/FlutterUnit/tree/flutter_unit_web) | [《web版闪亮登场》](https://juejin.im/post/6859888713980182541)| +``` +·]>> flutter --version +Flutter 3.35.1 • channel stable • https://github.com/flutter/flutter.git +Framework • revision 20f8274939 (6 days ago) • 2025-08-14 10:53:09 -0700 +Engine • hash 6cd51c08a88e7bbe848a762c20ad3ecb8b063c0e (revision 1e9a811bf8) (7 days ago) • 2025-08-13 23:35:25.000Z +Tools • Dart 3.9.0 • DevTools 2.48.0 +``` +#### 构建应用 + +``` +·]>> git clone https://github.com/toly1994328/FlutterUnit.git +·]>> cd FlutterUnit + +Build Android: +·]>> flutter build apk --target-platform --split-per-abi +Build iOS: +·]>> flutter build ios +Build Windows: +·]>> flutter build windows +Build Linux: +·]>> flutter build linux +Build web: +·]>> flutter build web +``` #### Flutter Unit 周边 - 🔥 [免费] [掘金小册 -《Flutter 入门教程》](https://juejin.cn/book/7212822723330834487) @@ -26,27 +74,6 @@ - [Flutter实用插件集录 ](https://github.com/toly1994328/FlutterUnit/issues/41) - [Flutter要点集录 ](https://github.com/toly1994328/FlutterUnit/labels/point) - ---- - -#### 当前Flutter 版本 - -``` -Flutter 3.13.3 • channel stable • https://github.com/flutter/flutter.git -Framework • revision 2524052335 (3 days ago) • 2023-09-06 14:32:31 -0700 -Engine • revision b8d35810e9 -Tools • Dart 3.1.1 • DevTools 2.25.0 -``` - -#### 构建命令,产出应用 - -> Android 分架构打包: flutter build apk --target-platform android-arm64 --split-per-abi -> iOS 打包应用: flutter build ios -> Windows 打包应用: flutter build windows -> Macos 打包应用: flutter build macos -> Linux 打包应用: flutter build linux -> web 打包应用: flutter build web - --- #### MacOS 桌面版本组件界面 @@ -60,7 +87,8 @@ Tools • Dart 3.1.1 • DevTools 2.25.0 > 开源不易,请我喝咖啡 ~ -![](./doc/ewm/coffee.webp) +![](./doc/ewm/coffee1.png) + #### Star History diff --git a/analysis_options.yaml b/analysis_options.yaml index 2fdeaeb06..d54e87f36 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -10,21 +10,13 @@ include: package:flutter_lints/flutter.yaml linter: - # The lint rules applied to this project can be customized in the - # section below to disable rules from the `package:flutter_lints/flutter.yaml` - # included above or to enable additional rules. A list of all available lints - # and their documentation is published at - # https://dart-lang.github.io/linter/lints/index.html. - # - # Instead of disabling a lint rule for the entire project in the - # section below, it can also be suppressed for a single line of code - # or a specific dart file by using the `// ignore: name_of_lint` and - # `// ignore_for_file: name_of_lint` syntax on the line or in the file - # producing the lint. rules: avoid_print: false # Uncomment to disable the `avoid_print` rule file_names: false - # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options +analyzer: +# exclude: +# - modules/widget_system/widgets/** + + + diff --git a/android/app/build.gradle b/android/app/build.gradle deleted file mode 100644 index a3efbb853..000000000 --- a/android/app/build.gradle +++ /dev/null @@ -1,74 +0,0 @@ -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - -android { - compileSdkVersion 33 - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = '1.8' - } - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - - defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.toly1994.flutter_unit" - minSdkVersion 19 - targetSdkVersion 30 - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - multiDexEnabled true - archivesBaseName = "FlutterUnit.apk" - ndk { -// abiFilters 'arm64-v8a' -// abiFilters 'armeabi', 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a' - } - } - - buildTypes { - release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug - } - } -} - -flutter { - source '../..' -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -} diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts new file mode 100644 index 000000000..d73f8c2d9 --- /dev/null +++ b/android/app/build.gradle.kts @@ -0,0 +1,53 @@ +plugins { + id("com.android.application") + id("kotlin-android") + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id("dev.flutter.flutter-gradle-plugin") +} + +android { + namespace = "com.toly1994.flutter_unit" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_11.toString() + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "com.toly1994.flutter_unit" + // You can update the following values to match your application needs. + // For more information, see: https://flutter.dev/to/review-gradle-config. + minSdk = flutter.minSdkVersion + targetSdk = flutter.targetSdkVersion + versionCode = flutter.versionCode + versionName = flutter.versionName + } + + buildTypes { + getByName("release") { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig = signingConfigs.getByName("debug") + isShrinkResources = true // 移除未使用的资源 + isMinifyEnabled = true // 启用 R8 代码压缩 + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + ndk { + debugSymbolLevel = "none" + } + } + } +} + +flutter { + source = "../.." +} diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro new file mode 100644 index 000000000..e9ffa01ad --- /dev/null +++ b/android/app/proguard-rules.pro @@ -0,0 +1,2 @@ +-dontwarn javax.annotation.** +-keep class javax.annotation.** { *; } \ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 932ba74d6..678734418 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,8 @@ - + + + + @@ -28,6 +30,7 @@ android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" + android:exported="true" android:windowSoftInputMode="adjustResize"> 上\nclass Bottom2TopRouter extends PageRouteBuilder {\n final Widget child;\n final int duration;\n final Curve curve;\n\n Bottom2TopRouter(\n {this.child, this.duration = 500, this.curve = Curves.fastOutSlowIn})\n : super(\n transitionDuration: Duration(milliseconds: duration),\n pageBuilder: (ctx, a1, a2) {\n return child;\n },\n transitionsBuilder: (\n ctx,\n a1,\n a2,\n Widget child,\n ) => SlideTransition(\n position: Tween(\n begin: Offset(0.0, 1.0),\n end: Offset(0.0, 0.0),\n ).animate(CurvedAnimation(parent: a1, curve: curve)),\n child: child));\n}\n"},{"id":null,"widgetId":174,"name":"PopupMenuDivider基本使用","priority":1,"subtitle":" \n【height】 : 高度 【double】","code":"import 'package:flutter/material.dart';\nimport '../../../dialogs/dialog_about.dart';\nclass CustomPopupMenuDivider extends StatelessWidget {\n final map = {\n \"关于\": Icons.info_outline,\n \"帮助\": Icons.help_outline,\n \"问题反馈\": Icons.add_comment,\n };\n\n @override\n Widget build(BuildContext context) {\n return Container(\n child: Column(\n children: [\n _buildPopupMenuButton(context),\n PopupMenuDivider(),\n ],\n ),\n );\n }\n\n PopupMenuButton _buildPopupMenuButton(BuildContext context) {\n return PopupMenuButton(\n itemBuilder: (context) => [\n ...buildItems().sublist(0, 2),\n PopupMenuDivider(),\n ...buildItems().sublist(2, 3)\n ],\n offset: Offset(0, 50),\n color: Color(0xffF4FFFA),\n elevation: 1,\n shape: RoundedRectangleBorder(\n borderRadius: BorderRadius.only(\n topLeft: Radius.circular(20),\n bottomRight: Radius.circular(20),\n topRight: Radius.circular(5),\n bottomLeft: Radius.circular(5),\n )),\n onSelected: (e) {\n print(e);\n if (e == '关于') {\n DialogAbout.show(context);\n }\n },\n onCanceled: () => print('onCanceled'),\n );\n }\n\n List> buildItems() {\n return map.keys\n .toList()\n .map((e) => PopupMenuItem(\n value: e,\n child: Wrap(\n spacing: 10,\n children: [\n Icon(\n map[e],\n color: Colors.blue,\n ),\n Text(e),\n ],\n )))\n .toList();\n }\n}\n"},{"id":null,"widgetId":195,"name":"CupertinoScrollbar基本使用","priority":1,"subtitle":" \n【child】 : 子组件 【Widget】\n【controller】 : 控制器 【ScrollController】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass CustomCupertinoScrollbar extends StatelessWidget {\n final data = [\n Colors.purple[50],\n Colors.purple[100],\n Colors.purple[200],\n Colors.purple[300],\n Colors.purple[400],\n Colors.purple[500],\n Colors.purple[600],\n Colors.purple[700],\n Colors.purple[800],\n Colors.purple[900],\n ];\n\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 200,\n child: CupertinoScrollbar(\n child: ListView(\n padding: EdgeInsets.symmetric(horizontal: 5),\n children: data\n .map((color) => Container(\n alignment: Alignment.center,\n width: 100,\n height: 50,\n color: color,\n child: Text(\n colorString(color),\n style: TextStyle(color: Colors.white, shadows: [\n Shadow(\n color: Colors.black,\n offset: Offset(.5, .5),\n blurRadius: 2)\n ]),\n ),\n ))\n .toList(),\n ),\n ),\n );\n }\n\n String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n}"},{"id":null,"widgetId":172,"name":"FutureBuilder基本使用","priority":1,"subtitle":" \n【builder】 : 子组件 【AsyncWidgetBuilder】\n【initialData】 : 初始数据 【T】\n【future】 : 异步任务 【Future】","code":"import 'package:flutter/material.dart';\nclass CustomFutureBuilder extends StatefulWidget {\n @override\n _CustomFutureBuilderState createState() => _CustomFutureBuilderState();\n}\n\nclass _CustomFutureBuilderState extends State {\n Future _future;\n\n @override\n void initState() {\n _future = loadData();\n super.initState();\n }\n\n @override\n Widget build(BuildContext context) {\n return Container(\n child: FutureBuilder(\n initialData: 'Load',\n future: _future,\n builder: (ctx, snap) {\n if (snap.connectionState == ConnectionState.done) {\n return Text(snap.data);\n }\n if (snap.connectionState == ConnectionState.waiting) {\n return CircularProgressIndicator();\n }\n if (snap.hasError) {\n return Text('Error');\n }\n return Container();\n }),\n );\n }\n\n Future loadData() async {\n await Future.delayed(Duration(seconds: 2));\n return 'LoadeSuccess';\n }\n}\n"},{"id":null,"widgetId":182,"name":"Overlay基本使用","priority":1,"subtitle":" \n Overlay.of(context).insert插入全局组件","code":"import 'package:flutter/material.dart';\nclass CustomOverlay extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Wrap(\n children: [\n Container(\n height: 50,\n child: RawMaterialButton(\n elevation: 2,\n shape: CircleBorder(\n side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)),\n ),\n fillColor: Colors.blue,\n splashColor: Colors.orange,\n textStyle: TextStyle(color: Colors.white),\n child: Icon(Icons.add),\n onPressed: ()=>showFloating(context),\n ),\n ),\n Container(\n height: 50,\n child: RawMaterialButton(\n elevation: 2,\n shape: CircleBorder(\n side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)),\n ),\n fillColor: Colors.red,\n splashColor: Colors.orange,\n textStyle: TextStyle(color: Colors.white),\n child: Icon(Icons.remove),\n onPressed: hideFloating,\n ),\n ),\n ],\n );\n }\n}\n"},{"id":null,"widgetId":139,"name":"CupertinoPicker基本使用","priority":1,"subtitle":" \n【children】 : 子组件列表 【List】\n【offAxisFraction】 : 轴偏移率 【double】\n【squeeze】 : 挤压率 【double】\n【diameterRatio】 : 高与圆柱直径比率 【double】\n【itemExtent】 : 间距 【double】\n【backgroundColor】 : 背景色 【Color】\n【onSelectedItemChanged】 : 选中事件 【Function(int)】","code":"import 'package:flutter/cupertino.dart';\nclass CustomCupertinoPicker extends StatelessWidget {\n final names = [\n 'Java',\n 'Kotlin',\n 'Dart',\n 'Swift',\n 'C++',\n 'Python',\n \"JavaScript\",\n \"PHP\",\n \"Go\",\n \"Object-c\"\n ];\n\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 150,\n child: CupertinoPicker(\n backgroundColor: CupertinoColors.systemGrey.withAlpha(33),\n diameterRatio: 1,\n offAxisFraction: 0.4,\n squeeze: 1.5,\n itemExtent: 40,\n onSelectedItemChanged: (position) {\n print('当前条目 ${names[position]}');\n },\n children: names.map((e) => Center(child: Text(e))).toList()),\n );\n }\n}\n"},{"id":null,"widgetId":118,"name":"AnimatedOpacity基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【duration】 : 动画时长 【Duration】\n【onEnd】 : 动画结束回调 【Function()】\n【curve】 : 动画曲线 【Duration】\n【opacity】 : 透明度 【double】","code":"import 'package:flutter/material.dart';\nclass CustomAnimatedOpacity extends StatefulWidget {\n @override\n _CustomAnimatedOpacityState createState() => _CustomAnimatedOpacityState();\n}\n\nclass _CustomAnimatedOpacityState extends State {\n double _opacity = 1.0;\n\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n Switch(\n value: _opacity == 0,\n onChanged: (v) {\n setState(() {\n _opacity = v ? 0 : 1.0;\n });\n }),\n Container(\n color: Colors.grey.withAlpha(22),\n width: 200,\n height: 100,\n child: AnimatedOpacity(\n duration: Duration(seconds: 1),\n curve: Curves.fastOutSlowIn,\n opacity: _opacity,\n onEnd: () => print('End'),\n child: Icon(Icons.android, color: Colors.green, size: 60),\n ),\n ),\n ],\n );\n }\n}\n"},{"id":null,"widgetId":55,"name":"DropdownButton的样式指定","priority":2,"subtitle":" \n【isDense】 : 是否紧排 【bool】\n【iconSize】 : 图标大小 【double】\n【hint】 : 提示组件 【Widget】\n【iconEnabledColor】 : 图标颜色 【Color】","code":"import 'package:flutter/material.dart';\nclass StyleDropDownButton extends StatefulWidget {\n @override\n _StyleDropDownButtonState createState() => _StyleDropDownButtonState();\n}\n\nclass _StyleDropDownButtonState extends State {\n Color _color = Colors.red ;\n final _colors = [Colors.red, Colors.yellow, Colors.blue, Colors.green];\n final _info = [\"红色\", \"黄色\", \"蓝色\", \"绿色\"];\n\n @override\n Widget build(BuildContext context) {\n return Wrap(\n crossAxisAlignment: WrapCrossAlignment.center,\n children: [\n Container(\n margin: EdgeInsets.symmetric(horizontal: 20),\n width: 50,\n height: 50,\n color: _color??Colors.transparent,\n ),\n DropdownButton(\n hint: Text('请选择'),\n isDense: true,\n iconSize:20,\n iconEnabledColor:_color??Colors.orange,\n value: _color,\n items: _buildItems(),\n onChanged: (v) => setState(() => _color = v)),\n ],\n );\n }\n\n List> _buildItems() => _colors\n .map((e) => DropdownMenuItem(\n value: e,\n child: Text(\n _info[_colors.indexOf(e)],\n style: TextStyle(color: e),\n )))\n .toList();\n}\n"},{"id":null,"widgetId":55,"name":"DropdownButton基本用法","priority":1,"subtitle":" \n【value】 : 当前值 【T】\n【items】 : 下拉选框 【List>】\n【icon】 : 图标 【Widget】\n【elevation】 : 影深 【double】\n【onChanged】 : 选择条目事件 【Function(T)】\n【backgroundColor】 : 背景色 【Color】","code":"import 'package:flutter/material.dart';\nclass CustomDropDownButton extends StatefulWidget {\n @override\n _CustomDropDownButtonState createState() => _CustomDropDownButtonState();\n}\n\nclass _CustomDropDownButtonState extends State {\n Color _color = Colors.red;\n final _colors = [Colors.red, Colors.yellow, Colors.blue, Colors.green];\n final _info = [\"红色\", \"黄色\", \"蓝色\", \"绿色\"];\n\n @override\n Widget build(BuildContext context) {\n return Wrap(\n children: [\n Container(\n margin: EdgeInsets.symmetric(horizontal: 20),\n width: 50,\n height: 50,\n color: _color,\n ),\n DropdownButton(\n value: _color,\n elevation: 1,\n icon: Icon(\n Icons.expand_more,\n size: 20,\n color: _color,\n ),\n items: _buildItems(),\n onChanged: (v) => setState(() => _color = v)),\n ],\n );\n }\n\n List> _buildItems() => _colors\n .map((e) => DropdownMenuItem(\n value: e,\n child: Text(\n _info[_colors.indexOf(e)],\n style: TextStyle(color: e),\n )))\n .toList();\n}"},{"id":null,"widgetId":56,"name":"PopupMenuButton基本使用","priority":1,"subtitle":" \n【itemBuilder】 : 构造器 【PopupMenuItemBuilder】\n【offset】 : 偏移 【Offset】\n【color】 : 背景颜色 【Color】\n【shape】 : 形状 【ShapeBorder】\n【elevation】 : 影深 【double】\n【onCanceled】 : 取消事件 【Function()】\n【onSelected】 : 选择事件 【Function(T)】","code":"import 'package:flutter/material.dart';\nimport '../../../dialogs/dialog_about.dart';\nclass CustomPopupMenuButton extends StatefulWidget {\n @override\n _CustomPopupMenuButtonState createState() => _CustomPopupMenuButtonState();\n}\n\nclass _CustomPopupMenuButtonState extends State {\n final map = {\n \"关于\": Icons.info_outline,\n \"帮助\": Icons.help_outline,\n \"问题反馈\": Icons.add_comment,\n };\n\n @override\n Widget build(BuildContext context) {\n return PopupMenuButton(\n itemBuilder: (context) => buildItems(),\n offset: Offset(0, 50),\n color: Color(0xffF4FFFA),\n elevation: 1,\n shape: RoundedRectangleBorder(\n borderRadius: BorderRadius.only(\n topLeft: Radius.circular(20),\n bottomRight: Radius.circular(20),\n topRight: Radius.circular(5),\n bottomLeft: Radius.circular(5),\n )),\n onSelected: (e) {\n print(e);\n if (e == '关于') {\n DialogAbout.show(context);\n }\n },\n onCanceled: () => print('onCanceled'),\n );\n }\n\n List> buildItems() {\n return map.keys\n .toList()\n .map((e) => PopupMenuItem(\n value: e,\n child: Wrap(\n spacing: 10,\n children: [\n Icon(\n map[e],\n color: Colors.blue,\n ),\n Text(e),\n ],\n )))\n .toList();\n }\n}\n"},{"id":null,"widgetId":160,"name":"Material的shape属性","priority":2,"subtitle":" \n【shape】 : 形状 【ShapeBorder】,","code":"import 'package:flutter/material.dart';\nclass ShapeMaterial extends StatelessWidget {\n\n final shapeMap = {\n 'BorderDirectional': BorderDirectional(\n top: BorderSide(\n color: Colors.white,\n ),\n start: BorderSide(color: Colors.black, width: 15),\n bottom: BorderSide(\n color: Colors.white,\n )),\n 'Border': Border(\n top: BorderSide(width: 5.0, color: Color(0xFFFFDFDFDF)),\n left: BorderSide(width: 5.0, color: Color(0xFFFFDFDFDF)),\n right: BorderSide(width: 5.0, color: Color(0xFFFF7F7F7F)),\n bottom: BorderSide(width: 5.0, color: Color(0xFFFF7F7F7F)),\n ),\n 'Circle': CircleBorder(\n side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)),\n ),\n 'RoundedRectangleBorder': RoundedRectangleBorder(\n side: BorderSide(width: 1.0, color: Colors.black),\n borderRadius: BorderRadius.all(Radius.circular(15))),\n 'ContinuousRectangleBorder': ContinuousRectangleBorder(\n side: BorderSide.none,\n borderRadius: BorderRadius.circular(40.0),\n )\n };\n\n @override\n Widget build(BuildContext context) {\n return Wrap(\n spacing: 10,\n runSpacing: 10,\n children: shapeMap.keys.map((e) => _buildMaterial(e)).toList());\n }\n\n Material _buildMaterial(String type) => Material(\n shadowColor: Colors.blue,\n shape: shapeMap[type],\n color: Colors.orange,\n elevation: 3,\n textStyle: TextStyle(color: Colors.white),\n child: Container(\n alignment: Alignment.center,\n width: 300,\n height: 60,\n child: Text(\n type,\n ),\n ),\n );\n}\n"},{"id":null,"widgetId":160,"name":"Material基本使用","priority":1,"subtitle":" \n【child】 : 子组件 【Widget】\n【type】 : 类型 【MaterialType】\n【elevation】 : 影深 【double】\n【shadowColor】 : 阴影颜色 【Color】\n【color】 : 颜色 【Color】","code":"import 'package:flutter/material.dart';\nclass CustomMaterial extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Wrap(\n spacing: 10,\n runSpacing: 10,\n children: MaterialType.values.map((e) => _buildMaterial(e)).toList());\n }\n\n Material _buildMaterial(MaterialType type) => Material(\n shadowColor: Colors.blue,\n type: type,\n color: Colors.orange,\n elevation: 3,\n child: Container(\n alignment: Alignment.center,\n width: 100,\n height: 60,\n child: Text(\n type.toString().split('.')[1],\n style: TextStyle(color: Colors.black),\n ),\n ),\n );\n}\n"},{"id":null,"widgetId":194,"name":"Scrollbar基本使用","priority":1,"subtitle":" \n【child】 : 子组件 【Widget】\n【controller】 : 控制器 【ScrollController】","code":"import 'package:flutter/material.dart';\nclass CustomScrollbar extends StatelessWidget {\n final data = [\n Colors.purple[50],\n Colors.purple[100],\n Colors.purple[200],\n Colors.purple[300],\n Colors.purple[400],\n Colors.purple[500],\n Colors.purple[600],\n Colors.purple[700],\n Colors.purple[800],\n Colors.purple[900],\n ];\n\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 200,\n child: Scrollbar(\n child: ListView(\n padding: EdgeInsets.symmetric(horizontal: 5),\n children: data\n .map((color) => Container(\n alignment: Alignment.center,\n width: 100,\n height: 50,\n color: color,\n child: Text(\n colorString(color),\n style: TextStyle(color: Colors.white, shadows: [\n Shadow(\n color: Colors.black,\n offset: Offset(.5, .5),\n blurRadius: 2)\n ]),\n ),\n ))\n .toList(),\n ),\n ),\n );\n }\n\n String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n}"},{"id":null,"widgetId":170,"name":"WillPopScope使用","priority":1,"subtitle":" \n【child】 : 子组件 【Widget】\n【onWillPop】 : 返回回调 【WillPopCallback】","code":"import 'package:flutter/material.dart';\nclass CustomWillPopScope extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n child: WillPopScope(child: (BackButton()),\n onWillPop: ()=>_willPop(context)),\n );\n }\n\n Future _willPop(context) async{\n return await showDialog(\n context: context,\n builder: (context) => AlertDialog(\n shape: RoundedRectangleBorder(\n borderRadius: BorderRadius.all(Radius.circular(10))),\n title: Text('提示'),\n content: Text('你确定要离开此页吗?'),\n actions: [\n FlatButton(\n onPressed: () => Navigator.of(context).pop(true),\n child: Text('确定'),\n ),\n FlatButton(\n onPressed: () => Navigator.of(context).pop(false),\n child: Text('取消'),\n ),\n\n ],\n ),\n ) ?? false;\n\n }\n}\n"},{"id":null,"widgetId":151,"name":"TableRowInkWell基本事件","priority":1,"subtitle":" \n【child】 : 子组件 【Widget】\n【onTap】 : 点击事件 【Function()】\n【onDoubleTap】 : 双击事件 【Function()】\n【onLongPress】 : 长按事件 【Function()】\n【onHighlightChanged】 : 高亮变化回调 【Function(bool)】","code":"import 'package:flutter/material.dart';\nclass CustomTableRowInkWell extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n var title = _ItemBean(\"单位称\", \"量纲\", \"单位\", \"单位名称\", \"单位符号\");\n var m = _ItemBean(\"长度\", \"L\", \"1m\", \"米\", \"m\");\n var kg = _ItemBean(\"质量\", \"M\", \"1Kg\", \"千克\", \"Kg\");\n var s = _ItemBean(\"时间\", \"T\", \"1s\", \"秒\", \"s\");\n var a = _ItemBean(\"安培\", \"Ι\", \"1A\", \"安培\", \"A\");\n var k = _ItemBean(\"热力学温度\", \"θ\", \"1K\", \"开尔文\", \"K\");\n var mol = _ItemBean(\"物质的量\", \"N\", \"1mol\", \"摩尔\", \"mol\");\n var cd = _ItemBean(\"发光强度\", \"J\", \"1cd\", \"坎德拉\", \"cd\");\n\n var data = <_ItemBean>[title, m, kg, s, a, k, mol, cd];\n\n return SingleChildScrollView(\n scrollDirection: Axis.horizontal,\n child: Table(\n columnWidths: const {\n 0: FixedColumnWidth(80.0),\n 1: FixedColumnWidth(80.0),\n 2: FixedColumnWidth(80.0),\n 3: FixedColumnWidth(80.0),\n 4: FixedColumnWidth(80.0),\n },\n defaultVerticalAlignment: TableCellVerticalAlignment.middle,\n border: TableBorder.all(\n color: Colors.orangeAccent, width: 1.0, style: BorderStyle.solid),\n children: data\n .map((item) => TableRow(children: [\n TableRowInkWell(\n onTap: () => print('onTap'),\n onDoubleTap: () => print('onDoubleTap'),\n onLongPress: () => print('onLongPress'),\n onHighlightChanged: (v) => print('onHighlightChanged:$v'),\n child: Center(\n child: Text(\n item.name,\n style: TextStyle(color: Colors.blue),\n )),\n ),\n Padding(\n padding: const EdgeInsets.all(8.0),\n child: Center(child: Text(item.symbol)),\n ),\n Padding(\n padding: const EdgeInsets.all(8.0),\n child: Center(child: Text(item.unitSymbol)),\n ),\n Padding(\n padding: const EdgeInsets.all(8.0),\n child: Center(child: Text(item.unitName)),\n ),\n Padding(\n padding: const EdgeInsets.all(8.0),\n child: Center(child: Text(item.unit)),\n ),\n ]))\n .toList(),\n ),\n );\n }\n}\n\nclass _ItemBean {\n String name;\n String symbol;\n String unit;\n String unitName;\n String unitSymbol;\n\n _ItemBean(this.name, this.symbol, this.unit, this.unitName, this.unitSymbol);\n}\n\n"},{"id":null,"widgetId":135,"name":"MonthPicker基本使用","priority":1,"subtitle":" \n【selectedDate】 : 选中日期 【DateTime】\n【firstDate】 : 最前日期限制 【DateTime】\n【lastDate】 : 最后日期限制 【DateTime】\n【onChanged】 : 点击回调 【Function(DateTime)】","code":"import 'package:flutter/material.dart';\nclass CustomMonthPicker extends StatefulWidget {\n @override\n _CustomMonthPickerState createState() => _CustomMonthPickerState();\n}\n\nclass _CustomMonthPickerState extends State {\n DateTime _date = DateTime.now();\n\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 350,\n child: MonthPicker(\n selectedDate: _date,\n onChanged: (date) => setState(() => _date = date),\n firstDate: DateTime(2018),\n lastDate: DateTime(2030),\n ),\n );\n }\n}\n"},{"id":null,"widgetId":143,"name":"CupertinoContextMenu基本使用","priority":1,"subtitle":" \n【child】 : 子组件 【Widget】\n【actions】 : 行为组件集 【List】\n【previewBuilder】 : 动画构造器 【ContextMenuPreviewBuilder】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass CustomCupertinoContextMenu extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n child: _buildCupertinoContextMenu(context),\n );\n }\n\n final info= ['保存图片','立刻呼叫','添加到收藏夹'];\n\n Widget _buildCupertinoContextMenu(context) => Container(\n decoration: BoxDecoration(\n image: DecorationImage(\n image: AssetImage('assets/images/sabar_bar.jpg'),\n fit: BoxFit.cover),\n borderRadius: BorderRadius.all(Radius.circular(50))),\n width: 100,\n height: 100,\n child: CupertinoContextMenu(\n child: Container(\n decoration: BoxDecoration(\n image: DecorationImage(\n image: AssetImage('assets/images/sabar_bar.jpg'),\n fit: BoxFit.cover),\n borderRadius: BorderRadius.all(Radius.circular(50))),\n ),\n actions: info.map((e)=>CupertinoContextMenuAction(\n child: Center(child: Text(e)),\n onPressed: () => Navigator.pop(context),\n )).toList())\n );\n}\n"}] \ No newline at end of file diff --git a/assets/data/web/widget.json b/assets/data/web/widget.json new file mode 100644 index 000000000..8dd52f79c --- /dev/null +++ b/assets/data/web/widget.json @@ -0,0 +1 @@ +[{"id":1,"family":0,"name":"Container","nameCN":"容器组件","lever":5,"linkWidget":"74,85,80,78,70,123","info":"用于容纳单个子组件的容器组件。集成了若干个单子组件的功能,如内外边距、形变、装饰、约束等...","image":"assets/images/widgets/Container.png"},{"id":2,"family":0,"name":"Text","nameCN":"文字组件","lever":5,"linkWidget":"101,324","info":"用于显示文字的组件。拥有的属性非常多,足够满足你的使用需求,核心样式由style属性控制。","image":"assets/images/widgets/Text.png"},{"id":3,"family":0,"name":"Card","nameCN":"卡片组件","lever":3,"linkWidget":"160","info":" 基于Material组件实现,用于将单个组件卡片化。并使其具有投影效果,可加外边距,也可以自定义卡片形状。","image":"assets/images/widgets/Card.png"},{"id":4,"family":0,"name":"FlutterLogo","lever":1,"linkWidget":"","nameCN":"Flutter图标","info":"用于展示Flutter图标组件。可定义颜色、尺寸、展示模式等信息,是一个非常简单的组件。","image":"assets/images/widgets/FlutterLogo.png"},{"id":5,"family":0,"name":"Banner","lever":1,"linkWidget":"","nameCN":"角标组件","info":"用于角标显示的组件。可容纳一个子组件,可选择方位添加角标及信息文字,可设置颜色。","image":"assets/images/widgets/Banner.png"},{"id":6,"family":0,"name":"Icon","lever":2,"linkWidget":"7,30,125","nameCN":"图标组件","info":"用于图标显示的组件。可指定图标资源、大小、颜色。非常简单,但是非常用","image":"assets/images/widgets/Icon.png"},{"id":7,"family":0,"name":"ImageIcon","linkWidget":"6,30,125","nameCN":"容器","lever":1,"info":"用于将一个图片变为纯色的组件。可指定大小、颜色。","image":"assets/images/widgets/ImageIcon.png"},{"id":8,"family":0,"name":"FadeInImage","nameCN":"淡入图片","linkWidget":"38","lever":2,"info":"透明渐变地加载一张图片。可指定占位图片、进退的动画曲线、时间、宽高、fit类型、对齐方式、重复方式等。","image":""},{"id":9,"family":0,"name":"CircleAvatar","nameCN":"圆形组件","linkWidget":"","lever":4,"info":"可将一张图片变成圆形,并且中间可以放置一个组件。可指定半径、前景色、背景色等。","image":""},{"id":10,"family":0,"name":"Visibility","nameCN":"显隐组件","linkWidget":"71","lever":3,"info":"控制一个组件显示或隐藏,可设置隐藏后的占位组件。与其功能相似的由OffStage组件。","image":"assets/images/widgets/Visibility.png"},{"id":11,"family":0,"name":"Chip","nameCN":"小条组件","linkWidget":"12,13,14,15,153","lever":4,"info":"一个横向的圆边小条,可以包含左中右三个组件。可以指定颜色、阴影色和点击事件。","image":"assets/images/widgets/Chip.png"},{"id":12,"family":0,"name":"ChoiceChip","nameCN":"选择小条","lever":3,"linkWidget":"11,13,14,15,153","info":"和Chip组件类似的样式,有一些选择的属性。可以指定选中时的颜色、阴影色和选择事件。","image":"assets/images/widgets/ChoiceChip.png"},{"id":13,"family":0,"name":"ActionChip","nameCN":"事件小条","lever":3,"linkWidget":"11,12,14,15,153","info":"和Chip组件类似的样式,有一些点击的属性。可以指定点击时的阴影深、点击事件。","image":"assets/images/widgets/ActionChip.png"},{"id":14,"family":0,"name":"InputChip","nameCN":"综合小条","linkWidget":"11,12,13,15,153","lever":4,"info":"和Chip组件类似的样式,集成了点击、删除、选择事件为一体。注意:点击事件和选择事件不能同时存在。","image":"assets/images/widgets/InputChip.png"},{"id":15,"family":0,"name":"FilterChip","linkWidget":"11,12,13,14,153","nameCN":"过滤小条","lever":4,"info":"和Chip组件类似的样式,具有选中与否的属性和选中事件。当选中时左侧组件上层会被✔️遮罩。","image":"assets/images/widgets/FilterChip.png"},{"id":16,"family":0,"name":"ListTile","nameCN":"列表瓦片","linkWidget":"162,334","lever":3,"info":"Flutter提供的一个通用列表条目结构,为左中右结构。相应位置可插入组件,可以很方便地应对特定的条目。","image":"assets/images/widgets/ListTile.png"},{"id":17,"family":0,"name":"CheckboxListTile","nameCN":"复选瓦片","linkWidget":"39","lever":3,"info":"Flutter提供的一个通用列表条目结构,为左中结构,尾部是一个CheckBox。相应位置可插入组件,可以很方便地应对特定的条目。","image":"assets/images/widgets/CheckBoxListTile.png"},{"id":18,"family":0,"name":"SwitchListTile","nameCN":"切钮瓦片","linkWidget":"40","lever":3,"info":"Flutter提供的一个通用列表条目结构,为左中结构,尾部是一个Switch。相应位置可插入组件,可以很方便地应对特定的条目。","image":"assets/images/widgets/SwitchListTile.png"},{"id":19,"family":0,"name":"RadioListTile","nameCN":"选钮瓦片","linkWidget":"45","lever":3,"info":"Flutter提供的一个通用列表条目结构,为中右结构,尾部是一个Radio。相应位置可插入组件,可以很方便地应对特定的条目。","image":"assets/images/widgets/RadioListTile.png"},{"id":20,"family":0,"name":"GridTileBar","nameCN":"网格瓦片头","linkWidget":"21","lever":2,"info":"Flutter提供的一个通用头结构,为左中右结构。相应位置可插入组件,可以很方便地应对特定的条目,相比ListTile而言,属性较少。","image":"assets/images/widgets/GridTileBar.png"},{"id":21,"family":0,"name":"GridTile","nameCN":"网格瓦片","linkWidget":"20","lever":3,"info":"Flutter提供的一个通用列表条目结构,可指定头、尾、子组件,常用于网格列表。","image":"assets/images/widgets/GridTile.png"},{"id":22,"family":0,"name":"UserAccountsDrawerHeader","nameCN":"展示头","linkWidget":"154","lever":3.8,"info":"Flutter提供的一个通用展示结构,相应位置可插入组件,可以很方便地应对特定的条目,常用于Drawer中。","image":"assets/images/widgets/UserAccountsDrawerHeader.png"},{"id":23,"family":0,"name":"MaterialButton","nameCN":"材料按钮","linkWidget":"25,26,27,326,175","lever":4,"info":"基于RawMaterialButton实现的通用Material按钮。可盛放一个子组件,能定义颜色、形状等表现,可接收点击和长按事件。","image":""},{"id":24,"family":1,"name":"CupertinoButton","nameCN":"iOS按钮","linkWidget":"23","lever":3,"info":"iOS风格的按钮。可指定颜色、点击时透明度、内边距、圆角等。可接收点击事件。","image":""},{"id":25,"family":0,"name":"FlatButton","nameCN":"平按钮","linkWidget":"24,26,27,175","lever":3,"info":"无阴影的平按钮,基于MaterialButton实现,所有属性和MaterialButton类似。","image":""},{"id":26,"family":0,"name":"RaisedButton","nameCN":"浮起按钮","linkWidget":"24,25,27,175","lever":3,"info":"有阴影的浮起按钮,基于MaterialButton实现,所有属性和MaterialButton类似。","image":""},{"id":27,"family":0,"name":"OutlineButton","nameCN":"线框按钮","linkWidget":"23,24,25,175","lever":3,"info":"边框样式按钮,基于MaterialButton实现,所有属性和MaterialButton类似。","image":""},{"id":28,"family":0,"name":"FloatingActionButton","nameCN":"浮动按钮","linkWidget":"64","lever":4,"info":"浮动按钮,一般用于Scaffold中,可摆放在特定位置。可盛放一个子组件,接收点击、可定义颜色、形状等。","image":""},{"id":29,"family":0,"name":"ButtonBar","nameCN":"按钮栏","linkWidget":"","lever":3,"info":"接收组件列表,常用于盛放若干个按钮。可指定对齐方式、边距等信息。","image":""},{"id":30,"family":0,"name":"IconButton","nameCN":"图标按钮","linkWidget":"6","lever":2,"info":"可点击的图标按钮,可指定图标信息、内边距、大小、颜色等,接收点击事件。","image":""},{"id":31,"family":0,"name":"BackButton","nameCN":"返回按钮","linkWidget":"30","lever":1,"info":"一个具有返回功能的IconButton,返回图标不可更改。在iOS和Android中表现不同","image":""},{"id":32,"family":0,"name":"CloseButton","nameCN":"关闭按钮","linkWidget":"30","lever":1.0,"info":"一个具有关闭功能的IconButton,关闭图标不可更改。","image":""},{"id":33,"family":0,"name":"ToggleButtons","nameCN":"切换按钮组","linkWidget":"332,262","lever":4,"info":"接收组件列表,可指定边线、圆角、颜色等属性。根据具体逻辑,可以实现多个按钮单选或多选的需求。","image":""},{"id":34,"family":0,"name":"Divider","nameCN":"水平分割线","linkWidget":"35,329","lever":2,"info":"水平分割线,可指定颜色、高度、粗细、左右边距信息,常用与列表的item分割线。","image":""},{"id":35,"family":0,"name":"VerticalDivider","nameCN":"竖直分割线","linkWidget":"34,329","lever":2,"info":"竖直分割线,可指定颜色、宽度、粗细、上下边距信息,常用与列表的item分割线。","image":""},{"id":36,"family":0,"name":"Placeholder","nameCN":"占位组件","linkWidget":"","lever":1,"info":"一个矩形和叉叉的占位组件,可指定颜色、线宽、宽高等属性。","image":""},{"id":37,"family":0,"name":"GridPager","nameCN":"网格线组件","linkWidget":"","lever":2,"info":"可容纳一个组件,在其上绘制网格。可指定颜色、线宽、间距等属性。","image":""},{"id":38,"family":1,"name":"Image","nameCN":"图片组件","linkWidget":"8,87","lever":5,"info":"用于显示一张图片,可以从文件、内存、网络、资源里加载。可以指定适应方式、样式、颜色混合模式、重复模式等","image":""},{"id":39,"family":1,"name":"Checkbox","nameCN":"复选框","linkWidget":"17","lever":4,"info":"复选框组件,常用于配置的切换,可指定颜色,接收状态变化回调,也可指定三态。","image":""},{"id":40,"family":1,"name":"Switch","nameCN":"切钮","linkWidget":"41,18","lever":4,"info":"切换选钮,常用于配置的切换,可指定小圆颜色、图片,滑槽颜色等,接收状态变化回调。","image":""},{"id":41,"family":1,"name":"CupertinoSwitch","nameCN":"iOS切钮","linkWidget":"40","lever":3,"info":"iOS风格的切换选钮,常用于配置的切换,可指定颜色,接收状态变化回调。","image":""},{"id":42,"family":1,"name":"Slider","nameCN":"滑块","linkWidget":"43,44,331","lever":4,"info":"滑块组件,可以在指定的最大值和最小值之间拖动选择。可指定颜色、分段数及显示的标签,接收进度变化回调。","image":""},{"id":43,"family":1,"name":"CupertinoSlider","linkWidget":"42","nameCN":"iOS滑块","lever":3,"info":"iOS风格的滑块组件,可以在指定的最大值和最小值之间拖动选择。可指定颜色,接收进度变化回调。","image":""},{"id":44,"family":1,"name":"RangeSlider","nameCN":"范围滑块","linkWidget":"42","lever":4,"info":"范围滑块组件,支持两点拖动,获取之间的范围。可指定颜色、分段数及显示的标签,接收进度变化回调。","image":""},{"id":45,"family":1,"name":"Radio","nameCN":"选钮","linkWidget":"19","lever":4,"info":"由于选中和未选择状态的圆钮,多个Radio根据逻辑可以实现单选或多选的需求。可指定颜色,接收状态变化回调。","image":""},{"id":46,"family":1,"name":"CircularProgressIndicator","nameCN":"圆形进度","linkWidget":"47,48","lever":3,"info":"圆形的进度显示,可指定颜色、线宽、进度等属性。value为null时会不停旋转。","image":""},{"id":47,"family":1,"name":"LinearProgressIndicator","nameCN":"水平进度","linkWidget":"46,48","lever":3,"info":"直线型的进度显示,可指定颜色、进度等属性。value为null时会不停旋转。","image":""},{"id":48,"family":1,"name":"CupertinoActivityIndicator","nameCN":"iOS指示器","linkWidget":"46,47","lever":2,"info":"iOS样式的loading显示组件,可指定半径和是否旋转。","image":""},{"id":49,"family":1,"name":"RefreshIndicator","nameCN":"刷新指示器","linkWidget":"","lever":4,"info":"内部嵌套可滑动区域,下滑时会显示刷新图标,松手后可以执行指定的异步方法。可指定颜色、到顶端距离等属性。","image":""},{"id":50,"family":1,"name":"Tooltip","nameCN":"提示工具","linkWidget":"","lever":3,"info":"由于显示提示信息的组件,长按时显示信息。可指定边距、显示时长、文字样式、装饰灯属性。","image":""},{"id":51,"family":1,"name":"ExpandIcon","nameCN":"展开图标","linkWidget":"66,125","lever":1,"info":"一个展开按钮,点击时会自己执行旋转180的动画。可指定颜色、大小、边距,接收点击事件。","image":""},{"id":52,"family":1,"name":"ExpansionTile","nameCN":"展开瓦片","linkWidget":"178","lever":3,"info":"一个通用的展开栏,可在指定的部位安放组件,点击时会折叠显隐下方组件。接收折叠时事件。","image":""},{"id":53,"family":1,"name":"SelectableText","nameCN":"可选择文字","linkWidget":"2","lever":3,"info":"可选择的文字,可以选择、复制。可指定浮标的颜色、大小、文字样式、对齐方式等。","image":""},{"id":54,"family":1,"name":"TextField","nameCN":"输入框","linkWidget":"199","lever":5,"info":"由于输入的组件,拥有复杂的属性。可指定控制器、文字样式、装饰线、行数限制、游标样式等。接收输入变化、完成输入等事件。","image":""},{"id":55,"family":1,"name":"DropdownButton","nameCN":"下拉按钮","linkWidget":"181","lever":4,"info":"用于下拉选择的按钮,可指定图标、影深、提示等属性,接收选中变化的事件。","image":""},{"id":56,"family":1,"name":"PopupMenuButton","nameCN":"菜单按钮","linkWidget":"174","lever":4,"info":"弹出菜单栏,可指定偏移、颜色、影深、形状等属性。接收item选中的事件和取消选择事件。","image":""},{"id":57,"family":1,"name":"AppBar","nameCN":"应用头栏","linkWidget":"64","lever":4,"info":"一个应用顶部栏的通用结构,可在指定的部位放置相应的组件,常用于Scaffold组件中。","image":""},{"id":58,"family":1,"name":"TabBar","nameCN":"标签栏","linkWidget":"57,59,148","lever":3,"info":"可滑动和点击标签栏,通常用于AppBar的底部,可与TabBarView联用,实现滑页的效果。","image":""},{"id":59,"family":1,"name":"TabBarView","nameCN":"标签页","linkWidget":"58","lever":2,"info":"通常与TabBar联用,实现滑页的效果。一般不单独使用。","image":""},{"id":60,"family":1,"name":"BottomNavigationBar","nameCN":"底部导航","linkWidget":"61","lever":4,"info":"一个底部导航栏,通常用于Scaffold组件的底部,可指定颜色和模式,接受点击回调,可与PageView实现切页效果。","image":""},{"id":61,"family":1,"name":"BottomAppBar","nameCN":"底部导航","linkWidget":"60","lever":4,"info":"一个可凹嵌的底部导航栏,通常用于Scaffold组件的底部,可指定颜色、影深、形状等属性,可与PageView实现切页效果。","image":""},{"id":62,"family":1,"name":"CupertinoNavigationBar","nameCN":"iOS导航","linkWidget":"","lever":3,"info":"一个iOS风格的应用顶部栏的通用结构,可在指定的部位放置相应的组件。可指定背景色、间距、边线等属性。","image":""},{"id":63,"family":1,"name":"CupertinoTabBar","nameCN":"iOS页签","linkWidget":"158","lever":3,"info":"一个iOS风格的TabBar,通常用于CupertinoTabScaffold。可指定颜色、图标大小、边线等数据。接收item的点击事件。","image":""},{"id":64,"family":1,"name":"Scaffold","nameCN":"脚手架","linkWidget":"57,60,61","lever":4,"info":"一个通用app结构,包括上、下、左、右、中、浮动按钮部位,对应位置可盛放组件。","image":""},{"id":65,"family":1,"name":"MaterialApp","nameCN":"Material应用","linkWidget":"64","lever":5,"info":"Material应用的顶级组件,包含路由生成器、主题、语言、主页等属性。","image":""},{"id":66,"family":2,"name":"ClipOval","nameCN":"椭圆裁剪","linkWidget":"67,68,69","lever":3,"info":"可容纳一个子组件,并将其以宽高为长轴和短轴进行椭圆裁切。","image":""},{"id":67,"family":2,"name":"ClipRect","nameCN":"矩形裁剪","linkWidget":"66,68,69","lever":3,"info":"可容纳一个子组件,并将其进行矩形裁切。可借助SizedBox、Align、AspectRadio等限定组件进行定域。","image":""},{"id":68,"family":2,"name":"ClipRRect","nameCN":"圆角矩形裁剪","linkWidget":"66,67,69","lever":3,"info":"可容纳一个子组件,并将其进行圆角矩形裁剪。指定borderRadius作为边角半径。","image":""},{"id":69,"family":2,"name":"ClipPath","nameCN":"路径裁剪","linkWidget":"66,67,68","lever":5,"info":"可容纳一个子组件,并将其按指定路径进行裁剪。可以自定义路径形状,是一个很灵活的裁剪组件。","image":""},{"id":70,"family":2,"name":"DecoratedBox","nameCN":"装饰盒","linkWidget":"1","lever":4,"info":"可容纳一个子组件,可将其进行装饰。核心属性为decoration,可设置边线、渐变、阴影、背景图等。","image":""},{"id":71,"family":2,"name":"Offstage","nameCN":"消失组件","linkWidget":"10","lever":3,"info":"可容纳一个子组件,可更改其的消失与否。offstage属性为true表示隐藏。","image":""},{"id":72,"family":2,"name":"RotatedBox","nameCN":"旋转盒","linkWidget":"90","lever":2,"info":"可容纳一个子组件,将其沿顺时针旋转quarterTurns*90°。","image":""},{"id":73,"family":2,"name":"Opacity","nameCN":"透明化","linkWidget":"89,118","lever":3,"info":"可容纳一个子组件,将其透明度变为opacity值, opacity在0~1之间。","image":""},{"id":74,"family":2,"name":"Padding","nameCN":"边距组件","linkWidget":"1,191","lever":4,"info":"可容纳一个子组件,添加自身内边距来限制孩子组件的占位,核心属性为padding。","image":""},{"id":75,"family":2,"name":"Baseline","nameCN":"基线组件","linkWidget":"2","lever":2,"info":"可容纳一个子组件,通过控制基线高度来控制子组件的位置。一般用于文字组件。","image":""},{"id":76,"family":2,"name":"SizedBox","nameCN":"定尺寸盒","linkWidget":"1","lever":4,"info":"可容纳一个子组件,通过指定宽高限定子组件容身区域。","image":""},{"id":77,"family":2,"name":"AspectRatio","nameCN":"比例盒","linkWidget":"82","lever":3,"info":"可容纳一个子组件,通过指定宽高比aspectRatio,来限定子组件容身区域。","image":""},{"id":78,"family":2,"name":"Transform","nameCN":"变换","linkWidget":"1","lever":4,"info":"可容纳一个子组件,可以通过一个4*4的变换矩阵对子组件进行变换。","image":""},{"id":79,"family":2,"name":"LimitedBox","nameCN":"限制盒","linkWidget":"80","lever":3,"info":"可容纳一个子组件,通过指定最大宽高来限定子组件容身区域。","image":""},{"id":80,"family":2,"name":"ConstrainedBox","nameCN":"约束盒","linkWidget":"1,79,81","lever":3,"info":"可容纳一个子组件,通过指定最大、最小宽高,来限定子组件容身区域。","image":""},{"id":81,"family":2,"name":"UnconstrainedBox","nameCN":"约束盒","linkWidget":"80","lever":3,"info":"可容纳一个子组件,并解除该组件的所有区域约束,展现自我尺寸。","image":""},{"id":82,"family":2,"name":"FractionallySizedBox","nameCN":"分率盒","linkWidget":"77","lever":3,"info":"可容纳一个子组件,指定宽高分率,限定子组件区域为父容器宽高*各分率,及对齐方式alignment。","image":""},{"id":83,"family":2,"name":"OverflowBox","nameCN":"溢出盒","linkWidget":"84","lever":4,"info":"可容纳一个子组件,且子组件允许溢出父组件区域,可以指定宽高的最大最小区域进行限定,拥有对齐属性alignment。","image":""},{"id":84,"family":2,"name":"SizedOverflowBox","nameCN":"尺寸溢出盒","linkWidget":"83","lever":2.8,"info":"可容纳一个子组件,且子组件允许溢出父组件区域,可以通过size属性对子组件进行偏移,拥有对齐属性alignment。","image":""},{"id":85,"family":2,"name":"Align","nameCN":"对齐组件","linkWidget":"1,86,111,120","lever":5,"info":"可容纳一个子组件,可以通过alignment让子组件,定位在父组件宽高的任何指定分率出。","image":""},{"id":86,"family":2,"name":"Center","nameCN":"居中组件","linkWidget":"85","lever":3,"info":"可容纳一个子组件,并使其居中于父组件,是Align组件的一种精简模式。","image":""},{"id":87,"family":2,"name":"FittedBox","nameCN":"适应盒","linkWidget":"38","lever":4,"info":"可容纳一个子组件,使用fit属性决定子组件区域相当于父组件的适应模式,拥有对齐属性alignment。","image":""},{"id":88,"family":2,"name":"ColorFiltered","nameCN":"滤色器","linkWidget":"277,38","lever":5,"info":"可容纳一个子组件,可以并将组件按照29中叠色模式和任意组件混合,强大到我不知道该说什么好。app一键全灰了解一下。","image":""},{"id":89,"family":2,"name":"FadeTransition","nameCN":"透明变换","linkWidget":"73,118","lever":3,"info":"可容纳一个子组件,并使其进行透明度渐变动画,需要提供动画器opacity。","image":""},{"id":90,"family":1,"name":"RotationTransition","nameCN":"旋转变换","linkWidget":"72","lever":3,"info":"可容纳一个子组件,并使其进行旋转动画,需要提供动画器turns,拥有alignment属性。","image":""},{"id":91,"family":1,"name":"ScaleTransition","nameCN":"缩放变换","linkWidget":"","lever":3,"info":"可容纳一个子组件,并使其进行缩放动画,需要提供动画器scale,拥有alignment属性。","image":""},{"id":92,"family":1,"name":"SizeTransition","nameCN":"尺寸变换","linkWidget":"201","lever":3,"info":"可容纳一个子组件,并使其进行尺寸动画,需要提供动画器sizeFactor,可指定尺寸变化轴及轴向的axisAlignment。","image":""},{"id":93,"family":1,"name":"PositionedTransition","nameCN":"位置变换","linkWidget":"97","lever":3,"info":"只能用于Stack中,可容纳一个子组件,让其在两个矩形间进行位置动画,需要提供动画器rect。","image":""},{"id":94,"family":3,"name":"Flex","nameCN":"弹性布局","linkWidget":"95,96,106,107,109","lever":5,"info":"Row和Column的父类,Flutter中最强大的布局方式。可容纳多个组件,可与Spacer、Expended、Flexible组件联用进行灵活布局","image":""},{"id":95,"family":3,"name":"Row","nameCN":"行布局","linkWidget":"94,96","lever":4,"info":"排布方向为横向的Flex布局,可容纳多个组件。其他属性全部一致,详见Flex。","image":""},{"id":96,"family":3,"name":"Column","nameCN":"列布局","linkWidget":"94,95","lever":4,"info":"排布方向为竖向的Flex布局,可容纳多个组件。其他属性全部一致,详见Flex。","image":""},{"id":97,"family":3,"name":"Stack","nameCN":"堆叠布局","linkWidget":"94,95,161","lever":5,"info":"可容纳多个组件,以堆叠的方式摆放子组件,后者居上。拥有alignment属性,可与Positioned组件联合使用,精确定位。","image":""},{"id":98,"family":3,"name":"Wrap","nameCN":"包裹布局","linkWidget":"94,95","lever":5,"info":"可容纳多个组件,按照指定方向依次排布,可以很方便处理孩子的间距,当越界时可以自动换行。拥有主轴和交叉轴的对齐方式,比较灵活。","image":""},{"id":99,"family":3,"name":"Flow","nameCN":"流动布局","linkWidget":"98,94","lever":5,"info":"可容纳多个组件, 需要自己制定排布的代理,可以高强度自定义组件的排布,实现普通布局无法达到的效果。布局王者,当之无愧。","image":""},{"id":100,"family":1,"name":"AnimatedCrossFade","nameCN":"组件切换","linkWidget":"116","lever":5,"info":"将两个组件切换时呈现动画效果,可指定动画曲线、时长、对齐方式等属性。是一个非常有用的组件。","image":""},{"id":101,"family":3,"name":"RichText","nameCN":"富文本","linkWidget":"2","lever":5,"info":"可以容纳多种文字样式或各种组件的富文本组件,应用较为广泛。","image":""},{"id":102,"family":0,"name":"DataTable","nameCN":"数据表格","linkWidget":"110","lever":3,"info":"一个表格组件,可以制订逻辑进行点击、修改、排序等操作。","image":""},{"id":103,"family":1,"name":"Draggable","nameCN":"可拖拽组件","linkWidget":"104,105","lever":4,"info":"可以让组件在界面上任意拖拽,可存放一个泛型T的数据。通常和DragTarget组合使用,来完成拖拽效果。","image":""},{"id":104,"family":1,"name":"DragTarget","nameCN":"拖拽目标","linkWidget":"103,105","lever":4,"info":"一个拖拽的目标区域,可接收Draggable组件的信息。可以获取拖拽时的回调。","image":""},{"id":105,"family":1,"name":"LongPressDraggable","nameCN":"拖拽目标","linkWidget":"103,104","lever":4,"info":"长按时让组件在界面上任意拖拽,可存放一个泛型T的数据。通常和DragTarget组合使用,来完成拖拽效果。","image":""},{"id":106,"family":5,"name":"Expanded","nameCN":"延展组件","linkWidget":"94,109","lever":4,"info":"父类是Flexible,相当于一个fit类型为tight的Flexible组件。可嵌套孩子利用剩余空间对占位空间进行延展。","image":""},{"id":107,"family":0,"name":"Spacer","nameCN":"空间组件","linkWidget":"94","lever":3,"info":"只能用于Row、Column和Flex布局中,可利用剩余空间进行占位,使用flex属性可以给多个Spacer按比例分配空间。","image":""},{"id":108,"family":5,"name":"Positioned","nameCN":"定位组件","linkWidget":"97,159,121","lever":3,"info":"只能用于Stack中,可以指定左上右下的距离对某个组件进行位置精确安放。","image":""},{"id":109,"family":5,"name":"Flexible","nameCN":"灵活组件","linkWidget":"94,106","lever":3,"info":"只能用于只能用于Row、Column和Flex布局中,可嵌套孩子利用剩余空间对占位空间进行延展,也可指定适应类型。","image":""},{"id":110,"family":6,"name":"Table","nameCN":"表格组件","linkWidget":"102","lever":4,"info":"用于展示表格的组件,可指定边线、列宽、文字方向等属性,核心对象类型是TableRow。","image":""},{"id":111,"family":1,"name":"AlignTransition","nameCN":"对齐变换","linkWidget":"85,120","lever":3,"info":"AnimatedWidget的子类,使用Alignment类型的动画器让子组件在两个Alignment对象之间进行过渡动画。","image":""},{"id":112,"family":1,"name":"SlideTransition","nameCN":"滑动变换","linkWidget":"","lever":3,"info":"AnimatedWidget的子类,使用Offset类型的动画器让子组件在两个Offset对象之间进行过渡动画。","image":""},{"id":113,"family":1,"name":"DecoratedBoxTransition","nameCN":"装饰变换","linkWidget":"70","lever":3,"info":"AnimatedWidget的子类,使用Decorated类型的动画器让子组件在两个Decorated对象之间进行过渡动画。","image":""},{"id":114,"family":1,"name":"DefaultTextStyleTransition","nameCN":"文字样式变换","linkWidget":"124,324","lever":3,"info":"AnimatedWidget的子类,使用TextStyle类型的动画器让文字组件在两个TextStyle对象之间进行过渡动画。","image":""},{"id":115,"family":1,"name":"RelativePositionedTransition","nameCN":"矩形位置变换","linkWidget":"70","lever":3,"info":"AnimatedWidget的子类,使用Rect类型的动画器让子组件在两个Rect对象之间进行过渡动画。","image":""},{"id":116,"family":1,"name":"AnimatedSwitcher","nameCN":"动画切换","linkWidget":"100","lever":4,"info":"当子组件变化时执行动画,需要指定子组件的key进行标识。动画方式可以自定义,能指定动画时长、动画曲线等属性。","image":""},{"id":117,"family":1,"name":"AnimatedList","nameCN":"动画列表","linkWidget":"162","lever":3,"info":"强化版的ListView,可以对item进行动画处理。比如在添加、删除是item的动画。","image":""},{"id":118,"family":1,"name":"AnimatedOpacity","nameCN":"透明动画","linkWidget":"89,73","lever":3,"info":"能让子组件进行Opacity(透明度)动画,可指定时长和曲线,有动画结束事件。","image":""},{"id":119,"family":1,"name":"AnimatedPadding","nameCN":"边距动画","linkWidget":"74","lever":3,"info":"能让子组件进行Padding(内边距)动画,可指定时长和曲线,有动画结束事件。","image":""},{"id":120,"family":1,"name":"AnimatedAlign","nameCN":"对齐动画","linkWidget":"85,111","lever":3,"info":"能让子组件进行Align(对齐)动画,可指定时长和曲线,有动画结束事件。","image":""},{"id":121,"family":1,"name":"AnimatedPositioned","nameCN":"定位动画","linkWidget":"108,93,122","lever":3,"info":"能让子组件进行Positioned(定位)动画,可指定时长和曲线,有动画结束事件。只能用于Stack之中。","image":""},{"id":122,"family":1,"name":"AnimatedPositionedDirectional","nameCN":"方向定位动画","linkWidget":"121,159","lever":3,"info":"能让子组件进行PositionedDirectional(方向定位)动画,可指定时长和曲线,有动画结束事件。只能用于Stack之中。","image":""},{"id":123,"family":1,"name":"AnimatedContainer","nameCN":"容器动画","linkWidget":"1","lever":5,"info":"集合alignment、padding、color、decoration、width、height、constraints、margin、transform于一身,这些属性皆可动画,可指定时长和曲线,有动画结束事件。","image":""},{"id":124,"family":1,"name":"AnimatedDefaultTextStyle","nameCN":"容器动画","linkWidget":"114,324","lever":3,"info":"能让子文字组件进行TextStyle(文字样式)动画,可指定时长和曲线,有动画结束事件。","image":""},{"id":125,"family":0,"name":"AnimatedIcon","nameCN":"图标动画","linkWidget":"6","lever":3,"info":"使用AnimatedIcons的图标数据,可以根据一个动画控制器来使图标进行动画效果。可指定图标大小、颜色等。","image":""},{"id":126,"family":0,"name":"Dialog","nameCN":"对话框","linkWidget":"","lever":2,"info":"最简易的对话框面板,包含一个内容组件,可指定影深、背景色、形状等属性。","image":""},{"id":127,"family":0,"name":"AlertDialog","nameCN":"弹出对话框","linkWidget":"129","lever":3,"info":"一个通用的对话框结构,可指定头、中、尾处的组件。拥有标题、内容的文字样式和边距,影深、形状等属性。","image":""},{"id":128,"family":0,"name":"SimpleDialog","nameCN":"简单对话框","linkWidget":"133","lever":3,"info":"一个简单的对话框结构,可指定头、中处的组件。拥有拥有标题、内容的文字样式和边距,影深、形状等属性。常与SimpleDialogOption联用。","image":""},{"id":129,"family":0,"name":"CupertinoAlertDialog","nameCN":"iOS对话框","linkWidget":"127","lever":3,"info":"iOS风格的通用的对话框结构,可指定头、中、尾处的组件。","image":""},{"id":130,"family":0,"name":"AboutDialog","nameCN":"弹出对话框","linkWidget":"193","lever":1,"info":"应用的简介对话框,可指定应用图标、应用名、应用版本号等信息和内部的子组件列表,点击左侧按钮可以跳转到证书页。","image":""},{"id":131,"family":0,"name":"CupertinoActionSheet","nameCN":"iOS行为单","linkWidget":"132","lever":3,"info":"iOS风格的弹出选择结构,可放多的按钮,一般与CupertinoActionSheetAction联用。","image":""},{"id":132,"family":0,"name":"CupertinoActionSheetAction","nameCN":"iOS行为单按键","linkWidget":"131","lever":1,"info":"一个按钮,应用场景很少,通常用于CupertinoActionSheet中,接收点击事件。","image":""},{"id":133,"family":0,"name":"SimpleDialogOption","nameCN":"简单对话框选项","linkWidget":"128","lever":1,"info":"一个按钮,应用场景很少,通常用于SimpleDialog中,接收点击事件。","image":""},{"id":134,"family":0,"name":"DayPicker","nameCN":"日期选择器","linkWidget":"135,136","lever":3,"info":"日期的选择组件,可指定当前日期、选中日期、展示月份等,接收日期选中事件。","image":""},{"id":135,"family":1,"name":"MonthPicker","nameCN":"月份选择器","linkWidget":"134,136","lever":3,"info":"月份的选择组件,自带上下月切换的监听。可指定选择的日期范围、选中日期等,接收日期选中事件。","image":""},{"id":136,"family":1,"name":"YearPicker","nameCN":"年份选择器","linkWidget":"134,135","lever":3,"info":"年份的选择组件,长相比较寒酸。可指定选择的日期范围、选中日期等,接收每份选中事件","image":""},{"id":137,"family":1,"name":"CupertinoDatePicker","nameCN":"iOS日期选择器","linkWidget":"138","lever":3,"info":"高大上的滑滚日期选择器,可指定选择的类型、日期范围等,接收日期选中事件。","image":""},{"id":138,"family":1,"name":"CupertinoTimerPicker","nameCN":"iOS时间选择器","linkWidget":"137","lever":3,"info":"高大上的滑滚时间选择器,可指定选择的类型、初始时间、背景色等,接收时间选中事件。","image":""},{"id":139,"family":1,"name":"CupertinoPicker","nameCN":"iOS选择器","linkWidget":"179","lever":3,"info":"高大上的柱面滑动选择器,精妙十足,可指定很多配置属性,接收滑动时选中事件。","image":""},{"id":140,"family":1,"name":"SnackBar","nameCN":"信息提示条","linkWidget":"141,142","lever":4,"info":"作为组件来说是一个简单的结构组件,可指定形状、影深、背景色等。一般通过ScaffoldState的showSnackBar方法从底部弹出。","image":""},{"id":141,"family":1,"name":"SnackBarAction","nameCN":"信息提示条按钮","linkWidget":"140","lever":1,"info":"一般只用于SnackBar中,接受点击事件。点击一次后该按钮就会被禁用,可以指定颜色和禁用时颜色。","image":""},{"id":142,"family":1,"name":"BottomSheet","nameCN":"底部抽屉","linkWidget":"140","lever":4,"info":"作为组件来说是一个简单的结构组件,可指定形状、影深、背景色、内部组件构造器等。一般通过ScaffoldState的showBottomSheet方法从底部弹出。","image":""},{"id":143,"family":1,"name":"CupertinoContextMenu","nameCN":"ios弹出菜单","linkWidget":"144","lever":5,"info":"一个华丽的iOS风格按钮弹出框,长按时会以动画的形式弹出菜单面板,通常和CupertinoContextMenuAction联用。","image":""},{"id":144,"family":1,"name":"CupertinoContextMenuAction","nameCN":"ios弹出菜单按钮","linkWidget":"143","lever":1,"info":"一般只用于CupertinoContextMenu中的点击按钮。可指定孩子和尾部图标,接收点击事件。","image":""},{"id":145,"family":1,"name":"LicensePage","nameCN":"证书页","linkWidget":"130,193","lever":1,"info":"应用的证书页,可指定应用图标、应用名、应用版本号等信息,其他由Flutter自动生成。","image":""},{"id":146,"family":0,"name":"GestureDetector","nameCN":"手势监听器","linkWidget":"147,150","lever":5,"info":"组件手势事件的检测器,可接受点击、长按、双击,按下、松开、移动等事件,并可以获取触点信息,居家旅行必备组件。","image":""},{"id":147,"family":0,"name":"Listener","nameCN":"事件监听器","linkWidget":"146","lever":3,"info":"组件事件的监听器,可接受按下、松开、移动、取消等事件。较GestureDetector比较原始,可获取的信息也更多。","image":""},{"id":148,"family":0,"name":"Tab","nameCN":"标签","linkWidget":"58","lever":1,"info":"一般用于TabBar中的item,上下结构,可指定图标和一个内容组件。","image":""},{"id":149,"family":1,"name":"InkResponse","nameCN":"水波纹响应","linkWidget":"150,152","lever":1,"info":"水波纹的点击效果,接收点击、双击、长按、取消、高亮变化事件,可指定水波纹颜色、半径、高亮形状等属性。","image":""},{"id":150,"family":1,"name":"InkWell","nameCN":"水波纹","linkWidget":"149,152","lever":4,"info":"InkResponse的子类,基本属性同InkResponse。一个矩形区域的水波纹,可以知道圆角半径,边线形状等。","image":""},{"id":151,"family":1,"name":"TableRowInkWell","nameCN":"表格水波纹","linkWidget":"110","lever":1,"info":"只能用于Table的水波纹,接收点击、双击、长按、高亮变化事件,水波纹会作用于表格的一行。","image":""},{"id":152,"family":1,"name":"Ink","nameCN":"水波","linkWidget":"149,150","lever":3,"info":"使InkWell和InkResponse的水波纹有效,用于绘制图像或其他装饰的Material组件。","image":""},{"id":153,"family":1,"name":"RawChip","nameCN":"原生小条","linkWidget":"11,12,13,14,15","lever":5,"info":"各自Chip组件的始祖,拥有各自Chip表现的能力,支持选中、点击、删除等事件。详见Chip、FilterChip、ActionChip、InputChip、ChoiceChip。","image":""},{"id":154,"family":0,"name":"Drawer","nameCN":"滑页栏","linkWidget":"64,155","lever":2,"info":"一般用于Scaffold中的draw和endDraw属性作为左右的滑页栏,可以容纳一个子组件,能指定影深。","image":""},{"id":155,"family":0,"name":"DrawerHeader","nameCN":"滑页栏","linkWidget":"154","lever":2,"info":"一般用于Drawer中,作为滑页栏的头部。可以指定内外边距、装饰、子组件等属性。","image":""},{"id":156,"family":1,"name":"CupertinoApp","nameCN":"iOS应用","linkWidget":"157,158","lever":4,"info":"iOS风格应用的顶级组件,包含路由生成器、主题、语言、主页等属性。","image":""},{"id":157,"family":1,"name":"CupertinoPageScaffold","nameCN":"iOS页面脚手架","linkWidget":"62","lever":3,"info":"iOS风格的页面布局脚手架结构,可指定顶部的导航栏和页面背景色。","image":""},{"id":158,"family":1,"name":"CupertinoTabScaffold","nameCN":"iOS页签脚手架","linkWidget":"63","lever":3,"info":"iOS风格的页面布局脚手架结构,可指定最底部的导航切换栏可主体内容页。","image":""},{"id":159,"family":0,"name":"PositionedDirectional","nameCN":"方向定位","linkWidget":"108,122","lever":3,"info":"和Positioned组件功能一样,属性名不同。只能用于Stack中,可以指定左上右下的距离对某个组件进行位置精确安放。","image":""},{"id":160,"family":1,"name":"Material","nameCN":"材料组件","linkWidget":"3","lever":5,"info":"Material风格组件的领军人物,灵魂核心。可指定颜色、影深、类型、阴影颜色、形状等属性。","image":""},{"id":161,"family":3,"name":"IndexedStack","nameCN":"索引堆叠","linkWidget":"97","lever":4,"info":"Stack组件的子类,可以堆叠多个组件,并通过index来指定展示的组件索引,其余的会被隐藏。","image":""},{"id":162,"family":0,"name":"ListView","nameCN":"列表组件","linkWidget":"16,163","lever":5,"info":"列表显示的领军人物,容纳多个子组件,可以通过builder、separated、custom等构造。有内边距、是否反向、滑动控制器等属性。","image":""},{"id":163,"family":0,"name":"GridView","nameCN":"网格组件","linkWidget":"21,162","lever":5,"info":"容纳多个组件,并以网格的方式。可以通过count、extent、custom、builder等构造。有内边距、是否反向、滑动控制器等属性。","image":""},{"id":164,"family":0,"name":"SingleChildScrollView","nameCN":"单子滑动","linkWidget":"","lever":5,"info":"使一个组件具有滑动的效果,可指定滑动的方向、是否反向、滑动控制器等属性。","image":""},{"id":165,"family":0,"name":"PageView","nameCN":"滑页","linkWidget":"","lever":5,"info":"容纳多个组件页面,可对它们进行滑动切换,可指定滑动的方向、是否反向、滑动控制器等属性。","image":""},{"id":166,"family":2,"name":"CustomPaint","nameCN":"绘制组件","linkWidget":"","lever":5,"info":"通过CustomPainter进行绘制,可实现一些复杂的自定义绘制组件,是Flutter中自定义组件的灵魂人物。","image":""},{"id":167,"family":5,"name":"MediaQuery","nameCN":"媒体查询","linkWidget":"","lever":4,"info":"可通过MediaQuery.of来获取屏幕尺寸、设备密度、文字缩放比例、边距等信息。","image":""},{"id":168,"family":0,"name":"Theme","nameCN":"主题","linkWidget":"65,169","lever":4,"info":"可通过Theme.of获取ThemeData对象。也可以指定主题应用于Theme的后代组件。","image":""},{"id":169,"family":0,"name":"CupertinoTheme","nameCN":"iOS主题","linkWidget":"156,168","lever":3,"info":"可通过CupertinoTheme.of获取CupertinoThemeData对象。也可以指定主题应用于CupertinoTheme的后代组件。","image":""},{"id":170,"family":1,"name":"WillPopScope","nameCN":"返回拦截","linkWidget":"","lever":5,"info":"当一个界面中有WillPopScope组件时,在页面返回时会触发回调,决定是否返回。可用于二次确认退出的场景。","image":""},{"id":171,"family":1,"name":"Hero","nameCN":"共享动画","linkWidget":"28","lever":5,"info":"可指定标签名,两个界面跳转时具有相同标签的组件会进行共享动画。一个界面中不能存在两个同名的Hero标签","image":""},{"id":172,"family":1,"name":"FutureBuilder","nameCN":"异步构造器","linkWidget":"173","lever":5,"info":"可指定一个Future对象,能够监听异步执行的状态,并在构造器中根据状态构建不同的界面。注意该Future对象不能和FutureBuilder同时创建,否则可能过渡刷新。","image":""},{"id":173,"family":1,"name":"StreamBuilder","nameCN":"流构造器","linkWidget":"172","lever":5,"info":"可指定一个stream对象,能够监听异步执行的状态,并在构造器中根据状态构建不同的界面。","image":""},{"id":174,"family":1,"name":"PopupMenuDivider","nameCN":"弹出菜单分割线","linkWidget":"56,34","lever":1,"info":"PopupMenuButton的分割线,一般不单独使用,可指定高度。","image":""},{"id":175,"family":1,"name":"RawMaterialButton","nameCN":"原始按钮","linkWidget":"23,25,26,27","lever":5,"info":"原始的Material按钮,按钮界的幕后大佬,可接受点击、长按、高亮变化事件,可指定颜色、形状。影深、内边距等属性。","image":""},{"id":176,"family":1,"name":"Dismissible","nameCN":"滑动消失","linkWidget":"162","lever":4,"info":"滑动时可显示底部组件,可指定滑动的方向和交叉轴的偏移量。接收确认消失和消失时的回调。","image":""},{"id":177,"family":1,"name":"ReorderableListView","nameCN":"可重排序列表","linkWidget":"162","lever":4,"info":"可以进行长按排序的ListView,可指定滑动方向、是否反向、滑动控制器等属性。","image":""},{"id":178,"family":1,"name":"ExpansionPanelList","nameCN":"展开列表","linkWidget":"52","lever":3,"info":"可展开的列表组件,可根据逻辑来实现单展开或多展开。可指定展开动画时长,接收展开回调","image":""},{"id":179,"family":1,"name":"ListWheelScrollView","nameCN":"滚轮列表","linkWidget":"139","lever":4,"info":"高大上的柱面滑动列表,精妙十足,可指定item高度、透视、挤压等属性,接收滑动时选中事件。","image":""},{"id":180,"family":5,"name":"ScrollConfiguration","nameCN":"ios菜单按钮","linkWidget":"162,163,164","lever":3,"info":"需要包裹一个可滑动的组件,并通过behavior属性控制滑动的效果,可以去除滑动的蓝色阴影等。","image":""},{"id":181,"family":5,"name":"DropdownButtonHideUnderline","nameCN":"下拉按钮隐藏线","linkWidget":"55","lever":1,"info":"用于去除DropdownButton的下划线,本身没有什么应用价值。","image":""},{"id":182,"family":1,"name":"Overlay","nameCN":"悬浮组件","linkWidget":"","lever":5,"info":"可以将组件在全应用中进行悬浮显示,能够添加或移除组件,它们有独立管理的栈。","image":""},{"id":183,"family":4,"name":"CustomScrollView","nameCN":"通用滑动视图","linkWidget":"184,185,188","lever":5,"info":"一个通用的滑动结构,可以指定滑动方向、是否反向、滑动控制器等属性。其中包含的子组件们必须是Sliver家族。","image":""},{"id":184,"family":4,"name":"SliverAppBar","nameCN":"Sliver头部栏","linkWidget":"183,196","lever":4,"info":"Sliver家族的顶部栏通用结构,可以指定左中右组件、收缩空间、影深、固定模式、背景色等属性。","image":""},{"id":185,"family":4,"name":"SliverList","nameCN":"Sliver列表","linkWidget":"183,186,187","lever":5,"info":"Sliver家族的列表组件,通过指定delegate构造子组件。通常用于CustomScrollView中。","image":""},{"id":186,"family":4,"name":"SliverFixedExtentList","nameCN":"Sliver固定延展列表","linkWidget":"183,185,187","lever":3,"info":"Sliver家族的列表组件,通过delegate构造子组件,可以指定item的高度。通常用于CustomScrollView中。","image":""},{"id":187,"family":4,"name":"SliverFillViewport","nameCN":"Sliver填充视图列表","linkWidget":"183,185,186","lever":3,"info":"Sliver家族的列表组件,通过delegate构造子组件,item的高度会填空视口,可以指定是否的分率。","image":""},{"id":188,"family":4,"name":"SliverGird","nameCN":"Sliver网格","linkWidget":"183","lever":4,"info":"Sliver家族的网格列表组件,和GirdView类似,通过count和extent构造。通常用于CustomScrollView中。","image":""},{"id":189,"family":4,"name":"SliverToBoxAdapter","nameCN":"Sliver适配器","linkWidget":"183","lever":4,"info":"可以容纳一个普通的组件,并将其转化成Sliver家族组件的适配器。","image":""},{"id":190,"family":4,"name":"SliverPersistentHeader","nameCN":"Sliver存留头","linkWidget":"183","lever":5,"info":"通常用于CustomScrollView中,可以让一个组件在滑动中停留在顶部,不会滑动消失。","image":""},{"id":191,"family":4,"name":"SliverPadding","nameCN":"Sliver内间距","linkWidget":"74","lever":3,"info":"可容纳一个Sliver家族的子组件,添加自身内边距来限制孩子组件的占位,核心属性为padding。","image":""},{"id":192,"family":4,"name":"SliverOpacity","nameCN":"Sliver透明度","linkWidget":"73","lever":3,"info":"可容纳一个Sliver家族的子组件,并通过opacity来指定子组件的透明度。","image":""},{"id":193,"family":0,"name":"AboutListTile","nameCN":"关于应用条目","linkWidget":"130,145","lever":3,"info":"一个点击条目,点击时可以弹出应用相关信息,可指定应用图标、应用名、应用版本号等信息和内部的子组件列表。","image":""},{"id":194,"family":1,"name":"Scrollbar","nameCN":"滑动指示栏","linkWidget":"195,164,162","lever":3,"info":"需要包裹一个可滑动区域,当可滑动时,会显示滑动的bar用于指示。","image":""},{"id":195,"family":1,"name":"CupertinoScrollbar","nameCN":"iOS滑动指示栏","linkWidget":"194,164,162","lever":3,"info":"iOS风格的滑动指示栏,需要包裹一个可滑动区域,当可滑动时,会显示滑动的bar用于指示。","image":""},{"id":196,"family":4,"name":"FlexibleSpaceBar","nameCN":"ios菜单按钮","linkWidget":"184","lever":3,"info":"通常用于SliverAppBar中的可伸展区域,可指定标题、标题间距、背景、折叠模式等。","image":""},{"id":197,"family":6,"name":"ErrorWidget","nameCN":"错误组件","linkWidget":"","lever":1,"info":"用于显示一个错误信息的组件,红底黄字,在开发过程中经常看到,一般不使用。","image":""},{"id":198,"family":1,"name":"Form","nameCN":"表单组件","linkWidget":"199","lever":4,"info":"表单组件,可以接收其下的FormField组件的变化回调,通过onWillPop拦截页面返回,通过FormState可对表单字段进行保存或校验。","image":""},{"id":199,"family":1,"name":"TextFormField","nameCN":"文字表单输入","linkWidget":"54,198","lever":4,"info":"和TextField属性基本一致,在其基础上增加字段的校验和提交的回调,FormState的save会触发onSaved回调。","image":""},{"id":200,"family":1,"name":"Stepper","nameCN":"步骤组件","linkWidget":"","lever":5,"info":"步骤组件,可指定一步步的操作,可以自定义步骤的内容,确认和返回的按钮以及步骤排列的方向。","image":""},{"id":201,"family":1,"name":"AnimatedSize","nameCN":"尺寸动画","linkWidget":"92","lever":3,"info":"子组件大小发生变化是,进行动画渐变,可指定时长、对齐方式、曲线、vsync等属性。","image":""},{"id":202,"family":0,"name":"Builder","nameCN":"构造器","linkWidget":"","lever":2,"info":"一个不影响子组件占位空间,不具有显示性的组件,存在的唯一价值是提供当前组件对应元素的上下文。","image":""},{"id":203,"family":0,"name":"OrientationBuilder","nameCN":"方向构造器","linkWidget":"202","lever":2,"info":"能够回调父组件是横向还是纵向,可以据此来构建不同的子组件。","image":""},{"id":204,"family":0,"name":"PreferredSize","nameCN":"优先尺寸","linkWidget":"57,64","lever":2,"info":"实现了PreferredSizeWidget接口,可容纳一个子组件,设置优先尺寸,不会对其子组件施加任何约束。","image":""},{"id":205,"family":0,"name":"TabPageSelector","nameCN":"页签滑动选择器","linkWidget":"206,59","lever":2,"info":"通常作为指示器与TabBarView联用,共同使用一个TabController。可指定颜色、大小、选中色。","image":""},{"id":206,"family":0,"name":"TabPageSelectorIndicator","nameCN":"页签指示器","linkWidget":"205","lever":2,"info":"一个有边线的圆形组件,可指定大小、颜色、边线色。是TabPageSelector的部分之一,一般不单独使用。","image":""},{"id":208,"family":0,"name":"Title","nameCN":"应用标题","linkWidget":"65","lever":2,"info":"该组件用于描述app在操作系统中的名称,可以在应用栏列表里看到效果。MaterialApp中的title字段效果的根源是该组件。","image":""},{"id":211,"family":0,"name":"MaterialBanner","nameCN":"横幅组件","linkWidget":"","lever":2,"info":"Material风格的横幅组件,支持左中右或左中下结构,可指定边距背景色等","image":""},{"id":214,"family":0,"name":"NavigationToolbar","nameCN":"导航工具条","linkWidget":"57","lever":2,"info":"左中右模式的通用结构组件,可指定中间组件距左侧边距及是否居中。源码在AppBar等导航条结构中有使用它。","image":""},{"id":218,"family":0,"name":"CupertinoNavigationBarBackButton","nameCN":"iOS风格返回按钮","linkWidget":"57","lever":2,"info":"Cupertino风格的导航栏返回按钮,可指定颜色和点击事件,一般不单独使用。","image":""},{"id":231,"family":1,"name":"InputDecorator","nameCN":"输入装饰","linkWidget":"54","lever":2,"info":"在外层包裹输入的装饰,是TextField的底层核心组件之一,一般不单独使用。","image":""},{"id":232,"family":1,"name":"Navigator","nameCN":"导航器","linkWidget":"65","lever":4,"info":"Navigator用堆栈规则管理一组子组件,可以将子组件切入弹出及监听出入栈事件。MaterialApp路由管理的本源就是使用了Navigator。","image":""},{"id":244,"family":1,"name":"EditableText","nameCN":"可编辑文字","linkWidget":"2,54","lever":2,"info":"可以编辑的文字,是TextField的底层最核心组件,一般不单独使用。","image":""},{"id":245,"family":1,"name":"CupertinoTextField","nameCN":"iOS风格输入框","linkWidget":"54","lever":4,"info":"Cupertino风格的输入框,属性和TextField类似,可指定控制器、文字样式、装饰线、行数限制、游标样式等。接收输入变化、完成输入等事件。","image":""},{"id":251,"family":4,"name":"NestedScrollView","nameCN":"嵌套滑动视图","linkWidget":"183","lever":4,"info":"用于多个视图滑动嵌套处理,可以指定头部、滑动控制器、滑动方向等,其中body必须是可滑动类型的组件。","image":""},{"id":253,"family":1,"name":"Scrollable","nameCN":"可滑动组件","linkWidget":"340,349","lever":4,"info":"实现了一个可滚动组件的交互模型,需要viewportBuilder进的viewport的构造。是ScrollView的核心实现组件之一,一般不直接使用。","image":""},{"id":255,"family":1,"name":"ValueListenableBuilder","nameCN":"监听值构造器","linkWidget":"","lever":5,"info":"可以监听一个值,当其变化时通过builder回调能重建界面,避免使用setState刷新。","image":""},{"id":262,"family":1,"name":"CupertinoSegmentedControl","nameCN":"iOS多栏切换","linkWidget":"33","lever":4,"info":"iOS风格的多按钮栏,表现和ToggleButtons类似,可指定内边距。","image":""},{"id":263,"family":2,"name":"FractionalTranslation","nameCN":"分度偏移","linkWidget":"","lever":3,"info":"通过offset属性将子组件进行偏移,偏移量为OffSet横纵*子组件大小。","image":""},{"id":264,"family":2,"name":"RepaintBoundary","nameCN":"重绘边界","linkWidget":"166","lever":5,"info":"为子组件创建一个单独的显示列表,提升性能。源码中在TextField、DrawerController、Scrollbar、Sliver等组件中均有应用","image":""},{"id":277,"family":2,"name":"ShaderMask","nameCN":"着色器遮罩","linkWidget":"88,38","lever":4,"info":"可容纳一个孩子,并通过着色器来对孩子进行着色,可指定混色模式。通常用于组件渐变色处理。","image":""},{"id":278,"family":2,"name":"BackdropFilter","nameCN":"背景滤镜","linkWidget":"88,97,67","lever":4,"info":"可容纳一个孩子,并将背景进行模糊滤镜。可以通过Stack将背景模糊实现组件的模糊效果。","image":""},{"id":279,"family":2,"name":"PhysicalShape","nameCN":"物理形状","linkWidget":"69","lever":4,"info":"可以让子组件按照路径进行剪裁,并且可以指定背景色、影深、阴影颜色、剪切行为。","image":""},{"id":285,"family":2,"name":"CustomSingleChildLayout","nameCN":"通用单子布局","linkWidget":"341","lever":3,"info":"可容纳一个子组件,并指定代理类对子组件进行排布。代理类可获取父容器区域和子组件的区域大小,及区域约束情况。","image":""},{"id":287,"family":2,"name":"LayoutBuilder","nameCN":"布局构造器","linkWidget":"","lever":4,"info":"可以检测到父容器的区域大小,并根据父容器的尺寸信息可以完成自定义布局。是一个非常实用的布局组件。","image":""},{"id":292,"family":2,"name":"IgnorePointer","nameCN":"忽视点击","linkWidget":"295,146,149,150","lever":4,"info":"容纳一个子组件,可以通过指定ignoring属性,来决定孩子是否忽略手势事件,其本身不接受事件。","image":""},{"id":293,"family":1,"name":"MouseRegion","nameCN":"鼠标区域","linkWidget":"","lever":3,"info":"用于鼠标事件监听的组件,通常用于桌面和Web平台,可监听鼠标的移入、移除、移动事件。","image":""},{"id":295,"family":2,"name":"AbsorbPointer","nameCN":"吸收点击","linkWidget":"146,149,150,292","lever":4,"info":"容纳一个子组件,可以通过指定ignoring属性,来决定孩子是否忽略手势事件,其本身接受事件。","image":""},{"id":297,"family":2,"name":"IntrinsicWidth","nameCN":"固有宽","linkWidget":"298","lever":4,"info":"根据子元素的固有宽度度调整其子元素大小的组件,可解决很多布局的疑难杂症,但相对昂贵。","image":""},{"id":298,"family":2,"name":"IntrinsicHeight","nameCN":"固有高","linkWidget":"297","lever":4,"info":"根据子元素的固有高度调整其子元素大小的组件,可解决很多布局的疑难杂症,但相对昂贵。","image":""},{"id":307,"family":4,"name":"SliverOverlapAbsorber","nameCN":"重叠吸收器","linkWidget":"251,308","lever":3,"info":"包裹另一个的sliver,并迫使其布局范围被视为重叠。需要和SliverOverlapInjector联用。","image":""},{"id":308,"family":4,"name":"SliverOverlapInjector","nameCN":"重叠注射器","linkWidget":"251,307","lever":3,"info":"一个sliver,需要和SliverOverlapAbsorber联用,处理视图重叠问题。","image":""},{"id":312,"family":6,"name":"PerformanceOverlay","nameCN":"性能浮层","linkWidget":"65","lever":2,"info":"可以非常方便地开启性能监测的两个柱图,方便查看刷新界面时帧率的变化情况。","image":""},{"id":313,"family":6,"name":"RawImage","nameCN":"原图片","linkWidget":"38","lever":2,"info":"是实现Image组件的核心组件,可以显示ui的Image,基本属性同Image,一般很少单独使用。","image":""},{"id":315,"family":5,"name":"LayoutId","nameCN":"布局Id","linkWidget":"341","lever":2,"info":"只能用于CustomMultiChildLayout组件中,为其子组件标识身份。","image":""},{"id":324,"family":5,"name":"DefaultTextStyle","nameCN":"默认字体样式","linkWidget":"2,114,124","lever":3,"info":"可容纳一个孩子,为后代的文字指定默认样式。常用于多个相同文字的样式统一,避免一一设置。","image":""},{"id":325,"family":5,"name":"IconTheme","nameCN":"图标样式","linkWidget":"6","lever":3,"info":"可容纳一个孩子,为后代的图标指定默认样式。常用于多个相同图标的样式统一,避免一一设置。","image":""},{"id":326,"family":5,"name":"ButtonTheme","nameCN":"按钮样式","linkWidget":"23,25,26,27","lever":3,"info":"主要用于为后代的Button类型组件统一设置默认属性,也可以通过该组件获取默认Button的属性。","image":""},{"id":327,"family":5,"name":"MaterialBannerTheme","nameCN":"横幅样式","linkWidget":"211","lever":2,"info":"主要用于为后代的MaterialBanner组件统一设置默认属性,也可以通过该组件获取默认MaterialBanner的属性。","image":""},{"id":328,"family":5,"name":"ChipTheme","nameCN":"小条样式","linkWidget":"11,153,12,13,14,15","lever":3,"info":"主要用于为后代的Chip类型组件统一设置默认属性,也可以通过该组件获取默认Chip的属性。","image":""},{"id":329,"family":5,"name":"DividerTheme","nameCN":"分割线样式","linkWidget":"34,35","lever":3,"info":"主要用于为后代的Divider类型组件统一设置默认属性,也可以通过该组件获取默认Divider的属性。","image":""},{"id":330,"family":5,"name":"PopupMenuTheme","nameCN":"弹出菜单样式","linkWidget":"56","lever":2,"info":"主要用于为后代的PopupMenuButton组件统一设置默认属性,也可以通过该组件获取默认PopupMenu的属性。","image":""},{"id":331,"family":5,"name":"SliderTheme","nameCN":"滑块样式","linkWidget":"42","lever":3,"info":"可容纳一个孩子,为后代的Slider指定默认样式。常用于Slider的样式统一,避免一一设置,也可以对Slider进行样式定制。","image":""},{"id":332,"family":5,"name":"ToggleButtonsTheme","nameCN":"滑块样式","linkWidget":"33","lever":2,"info":"主要用于为后代的ToggleButtons组件统一设置默认属性,也可以通过该组件获取默认ToggleButtons的属性。","image":""},{"id":333,"family":5,"name":"TooltipTheme","nameCN":"提示主题","linkWidget":"50","lever":2,"info":"主要用于为后代的Tooltip组件统一设置默认属性,也可以通过该组件获取默认TooltipTheme的属性。","image":""},{"id":334,"family":5,"name":"ListTileTheme","nameCN":"ListTile主题","linkWidget":"16","lever":2,"info":"主要用于为后代的ListTile组件统一设置默认属性,也可以通过该组件获取默认ListTile的属性。","image":""},{"id":338,"family":5,"name":"ButtonBarTheme","nameCN":"按钮条主题","linkWidget":"29","lever":2,"info":"主要用于为后代的ButtonBar组件统一设置默认属性,也可以通过该组件获取默认ButtonBarTheme的属性。","image":""},{"id":340,"family":3,"name":"Viewport","nameCN":"视口组件","linkWidget":"253,349","lever":1,"info":"通常用于为滑动视图提供视口,仅构建显示和预加载的部位。可指定预加载的长度、滑动轴向等。是ScrollView的核心实现组件之一,一般不直接使用。","image":""},{"id":341,"family":3,"name":"CustomMultiChildLayout","nameCN":"通用多子布局","linkWidget":"315,285","lever":4,"info":"使用一个代理类对子组件集进行布局控制,子组件必须使用LayoutId组件进行标识。","image":""},{"id":342,"family":3,"name":"ListBody","nameCN":"列表体","linkWidget":"162","lever":1,"info":"将若干子组件按照轴向进行排列,可设置的属性很少,一般很少使用,而选择使用ListView。","image":""},{"id":351,"family":1,"name":"InteractiveViewer","nameCN":"交互视图","linkWidget":"147,146,78","lever":4,"info":"主要对移动、缩放等手势交互进行封装,简化使用,可指定移动边界、缩放比例、手势监听等。","image":""},{"id":352,"family":0,"name":"CupertinoDialogAction","nameCN":"交互视图","linkWidget":"129","lever":1,"info":" 一个简单的按钮,通常用于CupertinoAlertDialog中,一般不单独使用。","image":""}] \ No newline at end of file diff --git a/assets/flutter.db b/assets/flutter.db index 9cdba0a2c..4ca299955 100644 Binary files a/assets/flutter.db and b/assets/flutter.db differ diff --git a/assets/fonts/CHOPS.TTF b/assets/fonts/CHOPS.ttf similarity index 100% rename from assets/fonts/CHOPS.TTF rename to assets/fonts/CHOPS.ttf diff --git a/assets/images/caver.webp b/assets/images/caver.webp index 426ab0ccb..3d4102c05 100644 Binary files a/assets/images/caver.webp and b/assets/images/caver.webp differ diff --git a/assets/images/coffee_wx.webp b/assets/images/coffee_wx.webp new file mode 100644 index 000000000..34298068b Binary files /dev/null and b/assets/images/coffee_wx.webp differ diff --git a/assets/images/coffee_wx_ac.webp b/assets/images/coffee_wx_ac.webp new file mode 100644 index 000000000..1d21f46fe Binary files /dev/null and b/assets/images/coffee_wx_ac.webp differ diff --git a/assets/images/coffee_zfb.webp b/assets/images/coffee_zfb.webp new file mode 100644 index 000000000..79859859c Binary files /dev/null and b/assets/images/coffee_zfb.webp differ diff --git a/assets/images/widgets/Autocomplete.svg b/assets/images/widgets/Autocomplete.svg new file mode 100644 index 000000000..653b4f5a1 --- /dev/null +++ b/assets/images/widgets/Autocomplete.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/images/widgets/Banner.svg b/assets/images/widgets/Banner.svg new file mode 100644 index 000000000..27c390ff1 --- /dev/null +++ b/assets/images/widgets/Banner.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/assets/images/widgets/Card.svg b/assets/images/widgets/Card.svg new file mode 100644 index 000000000..62d2d4b8d --- /dev/null +++ b/assets/images/widgets/Card.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/assets/images/widgets/Card.webp b/assets/images/widgets/Card.webp deleted file mode 100644 index 9f8452a43..000000000 Binary files a/assets/images/widgets/Card.webp and /dev/null differ diff --git a/assets/images/widgets/Chip.svg b/assets/images/widgets/Chip.svg new file mode 100644 index 000000000..a66547522 --- /dev/null +++ b/assets/images/widgets/Chip.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/widgets/Chip.webp b/assets/images/widgets/Chip.webp deleted file mode 100644 index 1be3bfa81..000000000 Binary files a/assets/images/widgets/Chip.webp and /dev/null differ diff --git a/assets/images/widgets/CircleAvatar.svg b/assets/images/widgets/CircleAvatar.svg new file mode 100644 index 000000000..eafafcb46 --- /dev/null +++ b/assets/images/widgets/CircleAvatar.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/assets/images/widgets/Container.svg b/assets/images/widgets/Container.svg new file mode 100644 index 000000000..c1500829f --- /dev/null +++ b/assets/images/widgets/Container.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/widgets/Container.webp b/assets/images/widgets/Container.webp deleted file mode 100644 index 0cc509a9a..000000000 Binary files a/assets/images/widgets/Container.webp and /dev/null differ diff --git a/assets/images/widgets/FilterChip.svg b/assets/images/widgets/FilterChip.svg new file mode 100644 index 000000000..2c0214a26 --- /dev/null +++ b/assets/images/widgets/FilterChip.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/widgets/FloatingActionButton.svg b/assets/images/widgets/FloatingActionButton.svg new file mode 100644 index 000000000..dfe232950 --- /dev/null +++ b/assets/images/widgets/FloatingActionButton.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/widgets/FlutterLogo.svg b/assets/images/widgets/FlutterLogo.svg new file mode 100644 index 000000000..6a82787f7 --- /dev/null +++ b/assets/images/widgets/FlutterLogo.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/images/widgets/GestureDetector.svg b/assets/images/widgets/GestureDetector.svg new file mode 100644 index 000000000..8d3a00012 --- /dev/null +++ b/assets/images/widgets/GestureDetector.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/images/widgets/GridView.svg b/assets/images/widgets/GridView.svg new file mode 100644 index 000000000..2d7f6874a --- /dev/null +++ b/assets/images/widgets/GridView.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/assets/images/widgets/Icon.svg b/assets/images/widgets/Icon.svg new file mode 100644 index 000000000..dd64950cb --- /dev/null +++ b/assets/images/widgets/Icon.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/widgets/Image.svg b/assets/images/widgets/Image.svg new file mode 100644 index 000000000..e69de29bb diff --git a/assets/images/widgets/InputChip.svg b/assets/images/widgets/InputChip.svg new file mode 100644 index 000000000..d50adf71c --- /dev/null +++ b/assets/images/widgets/InputChip.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/widgets/InputChip.webp b/assets/images/widgets/InputChip.webp deleted file mode 100644 index c6bb78834..000000000 Binary files a/assets/images/widgets/InputChip.webp and /dev/null differ diff --git a/assets/images/widgets/ListView.svg b/assets/images/widgets/ListView.svg new file mode 100644 index 000000000..4e10e6580 --- /dev/null +++ b/assets/images/widgets/ListView.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/widgets/MaterialButton.svg b/assets/images/widgets/MaterialButton.svg new file mode 100644 index 000000000..11b59f54b --- /dev/null +++ b/assets/images/widgets/MaterialButton.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/assets/images/widgets/PageView.svg b/assets/images/widgets/PageView.svg new file mode 100644 index 000000000..5dc3a701d --- /dev/null +++ b/assets/images/widgets/PageView.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/images/widgets/RichText.svg b/assets/images/widgets/RichText.svg new file mode 100644 index 000000000..0dd5f661a --- /dev/null +++ b/assets/images/widgets/RichText.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/images/widgets/SingleChildScrollView.svg b/assets/images/widgets/SingleChildScrollView.svg new file mode 100644 index 000000000..baddde13b --- /dev/null +++ b/assets/images/widgets/SingleChildScrollView.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/widgets/Text.png b/assets/images/widgets/Text.png new file mode 100644 index 000000000..5f690258f Binary files /dev/null and b/assets/images/widgets/Text.png differ diff --git a/assets/images/widgets/Text.svg b/assets/images/widgets/Text.svg new file mode 100644 index 000000000..2b327a5de --- /dev/null +++ b/assets/images/widgets/Text.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/images/widgets/Widget.svg b/assets/images/widgets/Widget.svg new file mode 100644 index 000000000..f92990967 --- /dev/null +++ b/assets/images/widgets/Widget.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/images/wxgzh.webp b/assets/images/wxgzh.webp new file mode 100644 index 000000000..0a44c72cc Binary files /dev/null and b/assets/images/wxgzh.webp differ diff --git a/assets/version.json b/assets/version.json index 79dcc07ab..0c311e970 100644 --- a/assets/version.json +++ b/assets/version.json @@ -1,3 +1,3 @@ { - "dbVersion": 2 + "dbVersion": 5 } \ No newline at end of file diff --git a/desiredFileName.txt b/desiredFileName.txt new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/desiredFileName.txt @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/devtools_options.yaml b/devtools_options.yaml new file mode 100644 index 000000000..fa0b357c4 --- /dev/null +++ b/devtools_options.yaml @@ -0,0 +1,3 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: diff --git a/doc/development/architecture.md b/doc/development/architecture.md new file mode 100644 index 000000000..65c14cf2b --- /dev/null +++ b/doc/development/architecture.md @@ -0,0 +1,247 @@ +# FlutterUnit 架构设计文档 + +## 项目概述 + +FlutterUnit 是一个全平台的 Flutter 组件展示和学习应用,支持 Android、iOS、Web、Windows、macOS 和 Linux 平台。项目采用模块化架构,提供了 300+ Flutter 组件的详细展示、代码示例和交互演示。 + +## 技术栈 + +- **框架**: Flutter 3.35.1 +- **状态管理**: flutter_bloc (BLoC 模式) +- **路由管理**: go_router +- **数据库**: SQLite (本地数据存储) +- **网络请求**: dio +- **国际化**: flutter_localizations +- **UI组件**: 自研 TolyUI 组件库 + +## 整体架构 + +### 分层架构 + +``` +┌─────────────────────────────────────┐ +│ Presentation Layer │ ← UI层 (Views/Pages) +├─────────────────────────────────────┤ +│ Business Logic Layer │ ← 业务逻辑层 (BLoC) +├─────────────────────────────────────┤ +│ Repository Layer │ ← 仓储层 (Repository) +├─────────────────────────────────────┤ +│ Data Source Layer │ ← 数据源层 (Database/API) +└─────────────────────────────────────┘ +``` + +### 模块化设计 + +``` +FlutterUnit/ +├── modules/ +│ ├── basic_system/ # 基础系统模块 +│ │ ├── app/ # 应用核心配置 +│ │ ├── app_update/ # 应用更新 +│ │ ├── authentication/ # 用户认证 +│ │ ├── components/ # 通用组件 +│ │ ├── l10n/ # 国际化 +│ │ ├── storage/ # 数据存储 +│ │ ├── toly_ui/ # UI组件库 +│ │ └── utils/ # 工具类 +│ ├── widget_system/ # 组件系统模块 +│ │ ├── widget_module/ # 组件业务逻辑 +│ │ ├── widget_repository/ # 组件数据仓储 +│ │ └── widget_ui/ # 组件UI展示 +│ ├── knowledge_system/ # 知识系统模块 +│ │ ├── algorithm/ # 算法相关 +│ │ ├── artifact/ # 工件管理 +│ │ ├── awesome/ # 精选内容 +│ │ ├── layout/ # 布局相关 +│ │ └── note/ # 笔记功能 +│ ├── painting_system/ # 绘制系统模块 +│ │ └── draw_system/ # 自定义绘制 +│ └── tools_system/ # 工具系统模块 +│ └── treasure_tools/ # 实用工具集 +└── lib/ + ├── src/ + │ ├── navigation/ # 路由导航 + │ ├── l10n/ # 本地化 + │ └── starter/ # 应用启动 + └── main.dart +``` + +## 核心架构组件 + +### 1. 应用启动器 (FxApplication) + +```dart +class FxApplication with FxStarter { + @override + Widget get app => const AppBlocProvider(child: FlutterUnit3()); + + @override + AppStartRepository get repository => const FlutterUnitStartRepo(); +} +``` + +**职责**: +- 应用启动流程管理 +- 全局配置初始化 +- 错误处理和监控 + +### 2. 状态管理 (BLoC 模式) + +``` +Event → BLoC → State → UI + ↑ ↓ + └── User Interaction ┘ +``` + +**主要 BLoC**: +- `AppConfigBloc`: 应用配置管理 +- `WidgetsBloc`: 组件数据管理 +- `CategoryBloc`: 分类管理 +- `LikeWidgetBloc`: 收藏功能 + +### 3. 路由架构 + +```dart +GoRoute( + path: AppRoute.home.path, + routes: [ + if (kAppEnv.isDesktopUI) + ShellRoute( + builder: (_, __, Widget child) => AppDeskNavigation(content: child), + routes: body, + ), + if (!kAppEnv.isDesktopUI) ...body, + ], +) +``` + +**特点**: +- 支持桌面端和移动端不同导航 +- 声明式路由配置 +- 深度链接支持 + +### 4. 数据层架构 + +#### Repository 模式 + +```dart +abstract class WidgetRepository { + Future> loadWidgets(); + Future findWidget(int id); + Future> loadNodeByWidgetId(int widgetId); +} +``` + +**实现层**: +- `WidgetDbRepository`: 数据库实现 +- `MemoryWidgetRepository`: 内存缓存实现 + +#### 数据库设计 + +**核心表**: +- `widget`: Widget基本信息 +- `node`: 示例代码节点 +- `category`: Widget分类 + +详见: [数据表结构总览](../modules/widget_system/widget_repository/doc/tables_overview.md) + +## 平台适配 + +### 平台检测 + +```dart +class kAppEnv { + static bool get isWeb; + static bool get isDesktopUI; + static bool get isMobile; +} +``` + +### 响应式设计 + +- **移动端**: 底部导航 + 侧滑菜单 +- **桌面端**: 侧边栏导航 + 多窗口布局 +- **Web端**: 响应式布局适配 + +## 核心功能 + +### 1. 组件展示系统 + +- 300+ Flutter组件收录 +- 实时代码演示 +- 交互式组件体验 +- 组件关联跳转 + +### 2. 搜索与过滤 + +- 组件名称搜索 +- 星级过滤 +- 分类筛选 + +### 3. 收藏系统 + +- 自定义收藏集 +- 收藏集管理 +- 批量操作 + +### 4. 主题系统 + +- 明暗主题切换 +- 8种预设颜色主题 +- 6种字体选择 +- 自定义代码高亮 + +## 性能优化 + +### 1. 数据缓存 +- 内存缓存: 热点数据常驻内存 +- 数据库缓存: 本地SQLite存储 + +### 2. 懒加载 +- 路由懒加载: 按需加载页面 +- 组件懒加载: 滚动时动态加载 + +### 3. 构建优化 +- 代码分割: 模块化打包 +- 资源优化: 图片压缩、字体子集化 + +## 构建与部署 + +### 多平台构建 + +```bash +# Android +flutter build apk --target-platform --split-per-abi + +# iOS +flutter build ios + +# Web +flutter build web + +# Desktop +flutter build windows +flutter build macos +flutter build linux +``` + +### 发布渠道 + +- Android: APK 直接下载 +- iOS: App Store +- Web: 在线访问 +- Desktop: 可执行文件下载 + +## 开发规范 + +### 代码规范 +- 遵循 Dart 官方代码规范 +- 使用 `flutter_lints` 进行代码检查 + +### 模块规范 +- 每个模块独立的 `pubspec.yaml` +- 清晰的模块边界和依赖关系 + +### 测试规范 +- 单元测试覆盖核心业务逻辑 +- Widget测试验证UI行为 \ No newline at end of file diff --git a/doc/ewm/coffee.webp b/doc/ewm/coffee.webp deleted file mode 100644 index b8ba8add4..000000000 Binary files a/doc/ewm/coffee.webp and /dev/null differ diff --git a/doc/ewm/coffee1.png b/doc/ewm/coffee1.png new file mode 100644 index 000000000..dbbf38bc7 Binary files /dev/null and b/doc/ewm/coffee1.png differ diff --git a/doc/screens/preview.webp b/doc/screens/preview.webp new file mode 100644 index 000000000..79cf8b002 Binary files /dev/null and b/doc/screens/preview.webp differ diff --git a/doc/version/3.1.0.md b/doc/version/3.1.0.md new file mode 100644 index 000000000..d551bad2a --- /dev/null +++ b/doc/version/3.1.0.md @@ -0,0 +1,10 @@ +桌面版: +windows/macos 支持应用内更新,优化更新过程交互 +增加: 知识集锦/布局宝库 +增加 Ctrl+F 全局搜索功能 + +全端: +增加收录组件,目前共 354 个 +支持寻路算法演绎 +优化组件详情展示 +支持复制局部代码 diff --git a/doc/version/3.2.0.md b/doc/version/3.2.0.md new file mode 100644 index 000000000..9afbc2168 --- /dev/null +++ b/doc/version/3.2.0.md @@ -0,0 +1,9 @@ +全端: +增加最新咨询功能 +优化组件列表展示,增加logo设计图 +增加世界留言板 +优化项目结构 +组件数据支持 10 国语言国际化 + +下载失败,可到下面网站下载最新版 +https://gitee.com/toly1994328/FlutterUnit/releases \ No newline at end of file diff --git a/l10n.yaml b/l10n.yaml new file mode 100644 index 000000000..7312142f3 --- /dev/null +++ b/l10n.yaml @@ -0,0 +1,10 @@ +arb-dir: lib/src/l10n/arb +template-arb-file: app_zh.arb +output-localization-file: app_l10n.dart + + +synthetic-package: false +output-dir: lib/src/l10n/gen +output-class: AppL10n +nullable-getter: false +untranslated-messages-file: desiredFileName.txt \ No newline at end of file diff --git a/lib/app/bloc_wrapper.dart b/lib/app/bloc_wrapper.dart deleted file mode 100644 index e81f72b78..000000000 --- a/lib/app/bloc_wrapper.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'package:app/app.dart'; -import 'package:app_update/app_update.dart'; -import 'package:authentication/authentication.dart'; -import 'package:storage/storage.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/painter_system/bloc/gallery_unit/bloc.dart'; -import 'package:widget_module/blocs/blocs.dart'; -import 'package:widget_repository/widget_repository.dart'; - - - -/// create by 张风捷特烈 on 2020/4/28 -/// contact me by email 1981462002@qq.com -/// 说明: Bloc提供器包裹层 - -// final AppStart storage = AppStart(); - -class BlocWrapper extends StatefulWidget { - final Widget child; - - const BlocWrapper({Key? key, required this.child}) : super(key: key); - - @override - _BlocWrapperState createState() => _BlocWrapperState(); -} - -class _BlocWrapperState extends State { - final WidgetRepository repository = const WidgetDbRepository(); - - final CategoryBloc categoryBloc= CategoryBloc(repository: CategoryDbRepository()); - final AuthRepository authRepository = HttpAuthRepository(); - - @override - Widget build(BuildContext context) { - return MultiBlocProvider( - providers: [ - // 全局 bloc : 维护应用存储状态、更新、认证 - BlocProvider(create: (_) => AuthBloc(repository: authRepository)..add(const AppStarted())), - BlocProvider(create: (_) => AppBloc(AppStateRepository())..initApp()), - BlocProvider(create: (_) => UpdateBloc()), - BlocProvider(create: (_) => UserBloc()), - - - BlocProvider(create: (_) => WidgetsBloc(repository: repository)), - BlocProvider(create: (_) => categoryBloc), - BlocProvider(create: (_) => LikeWidgetBloc(repository: repository)), - BlocProvider(create: (_) => CategoryWidgetBloc(categoryBloc: categoryBloc)), - BlocProvider(create: (_) => GalleryUnitBloc()..loadGalleryInfo()), - ], child: widget.child); - } - - @override - void dispose() { - categoryBloc.close(); - FlutterDbStorage.instance.closeDb(); - super.dispose(); - } -} diff --git a/lib/app/flutter_unit.dart b/lib/app/flutter_unit.dart deleted file mode 100644 index 0d0b54e75..000000000 --- a/lib/app/flutter_unit.dart +++ /dev/null @@ -1,48 +0,0 @@ -import 'package:algorithm/algorithm.dart'; -import 'package:app/app.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_localizations/flutter_localizations.dart'; - -import 'package:flutter_unit/app/router/unit_router.dart'; -import 'package:flutter_unit/app/views/splash/standard_unit_splash.dart'; - -/// create by 张风捷特烈 on 2020/4/28 -/// contact me by email 1981462002@qq.com -/// 说明: 主程序 - -class FlutterUnit extends StatelessWidget { - const FlutterUnit({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return BlocBuilder(builder: (_, state) { - return SortStateScope( - notifier: SortState(), - child: DefaultTextStyle( - style: TextStyle(fontFamily: state.fontFamily), - child: MaterialApp( - // routes: , - showPerformanceOverlay: state.showPerformanceOverlay, - title: StrUnit.appName, - debugShowCheckedModeBanner: false, - onGenerateRoute: UnitRouters.generateRoute, - localizationsDelegates: GlobalMaterialLocalizations.delegates, - supportedLocales: const [ - Locale('zh', 'CN'), - ], - // themeMode: ThemeMode.light, - themeMode: state.themeMode, - darkTheme: AppTheme.darkTheme(state), - theme: AppTheme.lightTheme(state), - // theme: ThemeData( - // primarySwatch: state.themeColor, - // fontFamily: state.fontFamily, - // ), - home: const StandardUnitSplash(), - ), - ), - ); - }); - } -} diff --git a/lib/app/navigation/desk_ui/theme_model_switch_icon.dart b/lib/app/navigation/desk_ui/theme_model_switch_icon.dart deleted file mode 100644 index e8237b664..000000000 --- a/lib/app/navigation/desk_ui/theme_model_switch_icon.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:app/app.dart'; -import 'package:components/components.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -class ThemeModelSwitchIcon extends StatelessWidget { - const ThemeModelSwitchIcon({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - ThemeMode model = context.select((bloc)=>bloc.state.themeMode); - bool isDark = Theme.of(context).brightness == Brightness.dark; - return MouseRegion( - cursor: SystemMouseCursors.click, - child: GestureDetector( - onTap: (){ - context.read().changeThemeMode(isDark?ThemeMode.light:ThemeMode.dark); - }, - child: Padding( - padding: const EdgeInsets.only(bottom: 16, top: 16), - child: Icon( - !isDark?TolyIcon.dark:TolyIcon.wb_sunny, - color: Colors.white, - ), - ), - ), - ); - } -} diff --git a/lib/app/navigation/desk_ui/unit_desk_navigation.dart b/lib/app/navigation/desk_ui/unit_desk_navigation.dart deleted file mode 100644 index b69975fbd..000000000 --- a/lib/app/navigation/desk_ui/unit_desk_navigation.dart +++ /dev/null @@ -1,96 +0,0 @@ -import 'package:algorithm/algorithm.dart'; -import 'package:app/app.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_unit/code_gen/code_gen_page.dart'; - -import 'package:flutter_unit/painter_system/gallery_unit.dart'; -import 'package:flutter_unit/widget_ui/desk_ui/widget_panel/widget_panel.dart'; -import 'package:flutter_unit/widget_ui/mobile/category_page/collect_page.dart'; -import 'package:flutter_unit/widget_ui/mobile/category_page/home_right_drawer.dart'; -import '../../../point_system/views/desk_ui/desk_point_page.dart'; -import '../home_drawer.dart'; -import 'unit_rail_navigation.dart'; - -class UnitDeskNavigation extends StatefulWidget { - - const UnitDeskNavigation(); - - @override - _UnitDeskNavigationState createState() => _UnitDeskNavigationState(); - -} - -class _UnitDeskNavigationState extends State { - late PageController _controller; //页面控制器,初始0 - int _currentIndex = 0; - - @override - void initState() { - super.initState(); - _controller = PageController(); - - // ActionUnit.searchAction.onSearch = () { - // Navigator.of(context).pushNamed(UnitRouter.search); - // }; - } - - @override - void dispose() { - _controller.dispose(); //释放控制器 - super.dispose(); - } - - // 构建悬浮按钮工具 - // Widget wrapOverlayTool({required Widget child}) => Builder( - // builder: (ctx) => OverlayToolWrapper( - // child: child, - // )); - - @override - Widget build(BuildContext context) { - return Scaffold( - drawer: const HomeDrawer(), - endDrawer: const HomeRightDrawer(), - body: Row( - children: [ - UnitRailNavigation( - selectedIndex: _currentIndex, - onItemClick: _onItemClick, itemData: const { - "组件集录": TolyIcon.icon_layout, - "收藏集录": TolyIcon.icon_star, - "绘制集录": Icons.palette, - "可视排序": Icons.sort, - "代码生成": TolyIcon.icon_fast, - "要点集录": TolyIcon.icon_bug, - }, - ), - // _buildLeftNav(), - Expanded( - child: PageView( - physics: const NeverScrollableScrollPhysics(), - //使用PageView实现页面的切换 - controller: _controller, - children: const [ - DeskWidgetPanel(), - CollectPageAdapter(), - GalleryUnit(), - DeskSortPage(), - CodeGenPage(), - DeskPointPage(), - ], - ), - ), - ], - ), - ); - } - - void _onItemClick(int value) { - _currentIndex = value; - _controller.jumpToPage(_currentIndex); - setState(() { - - }); - } -} - diff --git a/lib/app/navigation/desk_ui/unit_rail_navigation.dart b/lib/app/navigation/desk_ui/unit_rail_navigation.dart deleted file mode 100644 index c0447677e..000000000 --- a/lib/app/navigation/desk_ui/unit_rail_navigation.dart +++ /dev/null @@ -1,306 +0,0 @@ -import 'package:app/app.dart'; -import 'package:components/toly_ui/toly_ui.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_unit/app/navigation/desk_ui/theme_model_switch_icon.dart'; -import 'package:url_launcher/url_launcher.dart'; - -class UnitRailNavigation extends StatefulWidget { - final ValueChanged onItemClick; - final int selectedIndex; - final Map itemData; - - const UnitRailNavigation( - {Key? key, - required this.onItemClick, - required this.selectedIndex, - required this.itemData}) - : super(key: key); - - @override - State createState() => _UnitRailNavigationState(); -} - -class _UnitRailNavigationState extends State - with TickerProviderStateMixin { - late List _destinationControllers; - late List> _destinationAnimations; - - List get info => widget.itemData.keys.toList(); - - List get icons => widget.itemData.values.toList(); - - @override - void initState() { - super.initState(); - _initControllers(); - } - - void _initControllers() { - _destinationControllers = - List.generate(widget.itemData.length, (int index) { - return AnimationController( - duration: kThemeAnimationDuration, - vsync: this, - )..addListener(_rebuild); - }); - - _destinationAnimations = _destinationControllers - .map((AnimationController controller) => controller.view) - .toList(); - _destinationControllers[widget.selectedIndex].value = 1.0; - } - - void _rebuild() { - setState(() { - // Rebuilding when any of the controllers tick, i.e. when the items are - // animating. - }); - } - - @override - void didUpdateWidget(UnitRailNavigation oldWidget) { - super.didUpdateWidget(oldWidget); - // No animated segue if the length of the items list changes. - if (widget.itemData.length != oldWidget.itemData.length) { - _resetState(); - return; - } - - if (widget.selectedIndex != oldWidget.selectedIndex) { - _destinationControllers[oldWidget.selectedIndex].reverse(); - _destinationControllers[widget.selectedIndex].forward(); - return; - } - } - - void _resetState() { - _disposeControllers(); - _initControllers(); - } - - void _disposeControllers() { - for (final AnimationController controller in _destinationControllers) { - controller.dispose(); - } - } - - @override - Widget build(BuildContext context) { - Color? divColor = Theme.of(context).dividerTheme.color; - return DragToMoveAreaNoDouble( - child: Container( - padding: const EdgeInsets.only(top: 20), - alignment: Alignment.topCenter, - margin: const EdgeInsets.only(right: 1), - width: 130, - decoration: BoxDecoration(color: Color(0xff2C3036), boxShadow: [ - BoxShadow(color: divColor!, offset: Offset(1, 0), blurRadius: 2) - ]), - child: Column( - children: [ - Wrap( - direction: Axis.vertical, - spacing: 10, - crossAxisAlignment: WrapCrossAlignment.center, - children: const [ - CircleImage( - image: AssetImage('assets/images/icon_head.webp'), - size: 60, - ), - Text( - '张风捷特烈', - style: TextStyle(color: Colors.white70), - ) - ], - ), - buildIcons(), - const Divider( - color: Colors.white, - height: 1, - endIndent: 20, - ), -// SizedBox(height: 60,), - Expanded( - flex: 5, - child: Center( - //const Size(120, 35) - child: Column( - mainAxisSize: MainAxisSize.min, - children: info - .asMap() - .keys - .map((int index) => _UnitRailMenu( - animation: _destinationControllers[index], - onTap: () { - widget.onItemClick.call(index); - }, - selected: widget.selectedIndex == index, - width: 130, - height: 46, - activeColor: Theme.of(context).primaryColor, - inactiveColor: Colors.white.withAlpha(33), - icon: icons[index], - label: info[index], - )) - .toList(), - )), - ), - Expanded( - child: Container(), - flex: 1, - ), - const Divider( - indent: 20, - color: Colors.white, - height: 1, - ), - - Wrap( - spacing: 12, - children: [ - const ThemeModelSwitchIcon(), - Builder( - builder: (ctx) => FeedbackWidget( - onPressed: () => Scaffold.of(ctx).openDrawer(), - child: const Padding( - padding: EdgeInsets.only(bottom: 16, top: 16), - child: Icon( - Icons.settings, - color: Colors.white, - ), - ), - ), - ), - ], - ), - ], - ), - ), - ); - ; - } - - Widget buildIcons() { - return Padding( - padding: const EdgeInsets.only(bottom: 16, top: 16), - child: Wrap( - spacing: 8, - children: [ - FeedbackWidget( - onPressed: () => _launchURL("http://blog.toly1994.com"), - child: const Icon( - TolyIcon.icon_item, - color: Colors.white, - ), - ), - FeedbackWidget( - onPressed: () => - _launchURL("https://github.com/toly1994328/FlutterUnit"), - child: const Icon( - TolyIcon.icon_github, - color: Colors.white, - ), - ), - FeedbackWidget( - onPressed: () => - _launchURL("https://juejin.im/user/5b42c0656fb9a04fe727eb37"), - child: const Icon( - TolyIcon.icon_juejin, - color: Colors.white, - ), - ), - ], - ), - ); - } - - _launchURL(String url) async { - if (await canLaunch(url)) { - await launch(url); - } else { - debugPrint('Could not launch $url'); - } - } -} - -class _UnitRailMenu extends StatefulWidget { - final VoidCallback onTap; - final bool selected; - final Color activeColor; - final Color inactiveColor; - final double width; - final double height; - final IconData icon; - final String label; - final Animation animation; - - _UnitRailMenu({ - Key? key, - required this.onTap, - required this.selected, - required this.width, - required this.activeColor, - required this.inactiveColor, - required this.height, - required this.animation, - required this.icon, - required this.label, - }) : super(key: key); - - @override - State<_UnitRailMenu> createState() => _UnitRailMenuState(); -} - -class _UnitRailMenuState extends State<_UnitRailMenu> { - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: widget.onTap, - child: Container( - alignment: Alignment.topLeft, - margin: const EdgeInsets.only(top: 10), - child: AnimatedBuilder( - animation: widget.animation, - builder: (BuildContext context, Widget? child) => _buildItem(), - ), - )); - } - - late ColorTween colorTween = ColorTween(begin: widget.inactiveColor, end: widget.activeColor); - - Widget _buildItem() { - double iconSize = _sizeTween.transform(widget.animation.value); - Color? color = colorTween.transform(widget.animation.value); - return Container( - alignment: Alignment.center, - decoration: BoxDecoration( - color: color, - borderRadius: BorderRadius.only( - topRight: Radius.circular(widget.height / 2), - bottomRight: Radius.circular(widget.height / 2))), - width: _widthTween.transform(widget.animation.value) * widget.width, - height: widget.height, - child: Wrap( - spacing: 6, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Icon( - widget.icon, - size: iconSize, - color: widget.selected ? Colors.white : Colors.white70, - ), - Text( - widget.label, - style: TextStyle( - fontSize: 14, - color: widget.selected ? Colors.white : Colors.white70, - ), - ), - ], - ), - ); - } -} - -final Tween _widthTween = Tween(begin: 0.82, end: 0.95); -final Tween _sizeTween = Tween(begin: 18.0, end: 20.0); diff --git a/lib/app/navigation/pure_bottom_bar.dart b/lib/app/navigation/pure_bottom_bar.dart deleted file mode 100644 index 71757e3c2..000000000 --- a/lib/app/navigation/pure_bottom_bar.dart +++ /dev/null @@ -1,80 +0,0 @@ -import 'package:app/app.dart'; -import 'package:flutter/material.dart'; - -typedef IndexTapCallback = void Function(int); -typedef IndexLongTapCallback = void Function(BuildContext, int); - -class PureBottomBar extends StatefulWidget { - final int initPosition; - - // item 点击事件 - final IndexTapCallback? onItemTap; - - // item 长按事件 - final IndexLongTapCallback? onItemLongTap; - const PureBottomBar( - {Key? key, this.onItemTap, this.onItemLongTap, this.initPosition = 0}) - : super(key: key); - - @override - State createState() => _PureBottomBarState(); -} - -class _PureBottomBarState extends State { - List get bottomBar => const ['组件', '绘制', - '知识', - '收藏', '我的']; - - List get bottomBarIcon => const [ - TolyIcon.icon_layout, - TolyIcon.dingzhi1, - TolyIcon.icon_artifact, - TolyIcon.icon_collect, - TolyIcon.yonghu, - ]; - int _position = 0; - - @override - void initState() { - super.initState(); - _position = widget.initPosition; - } - - @override - Widget build(BuildContext context) { - return Wrap( - children: [ - // Divider(height: 1,), - BottomNavigationBar( - // backgroundColor: Colors.white, - onTap: (position) { - // checkTokenExpires(); - _position = position; - - widget.onItemTap?.call(_position); - setState(() { - // _controller.jumpToPage(_position); - }); - }, - currentIndex: _position, - - elevation: 3, - // fixedColor: themeColor.activeColor, - type: BottomNavigationBarType.fixed, - iconSize: 22, - selectedItemColor: Theme.of(context).primaryColor, - selectedLabelStyle: const TextStyle(fontWeight: FontWeight.bold), - showUnselectedLabels: true, - showSelectedLabels: true, - // backgroundColor: themeColor.itemColor, - items: bottomBar - .asMap() - .keys - .map((index) => BottomNavigationBarItem( - label: bottomBar[index], icon: Icon(bottomBarIcon[index]))) - .toList(), - ), - ], - ); - } -} diff --git a/lib/app/navigation/route/route.dart b/lib/app/navigation/route/route.dart deleted file mode 100644 index 70f18eb0a..000000000 --- a/lib/app/navigation/route/route.dart +++ /dev/null @@ -1,41 +0,0 @@ -import 'dart:io'; - -import 'package:app/app.dart'; -import 'package:flutter/material.dart'; - -import '../../views/setting/code_style_setting.dart'; -import '../../views/setting/font_setting.dart'; -import '../../views/setting/setting_page.dart'; -import '../../views/setting/theme_color_setting.dart'; -import '../unit_navigation.dart'; - -class RoutePath { - - static const String nav = 'nav'; - - static const String themeColorSetting = 'ThemeColorSettingPage'; - static const String codeStyleSetting = 'CodeStyleSettingPage'; - static const String itemStyleSetting = 'ItemStyleSettingPage'; - static const String fontSetting = 'FountSettingPage'; - - Map get routes => - { - themeColorSetting: (ctx) => const ThemeColorSettingPage(), - codeStyleSetting: (ctx) => const CodeStyleSettingPage(), - fontSetting: (ctx) => const FontSettingPage(), - }; - - - static Route? generateRoute(RouteSettings settings) { - switch (settings.name) { - case nav: - if (Platform.isWindows || Platform.isMacOS || Platform.isLinux) { - return ZeroPageRoute(child: UnitNavigation()); - } - return SlidePageRoute(child: UnitNavigation()); - } - - return null; - } - -} \ No newline at end of file diff --git a/lib/app/navigation/unit_drawer_header.dart b/lib/app/navigation/unit_drawer_header.dart deleted file mode 100644 index 0d5572925..000000000 --- a/lib/app/navigation/unit_drawer_header.dart +++ /dev/null @@ -1,87 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-04-22 -/// contact me by email 1981462002@qq.com -/// 说明: - -class UnitDrawerHeader extends StatelessWidget { - final Color color; - - - const UnitDrawerHeader({Key? key, required this.color}) : super(key: key); - - @override - Widget build(BuildContext context) { - return DrawerHeader( - padding: const EdgeInsets.only(top: 10, left: 15), - decoration: const BoxDecoration( - image: DecorationImage( - image: AssetImage('assets/images/wy_300x200_filter.webp'), - fit: BoxFit.cover), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Wrap( - spacing: 10, - crossAxisAlignment: WrapCrossAlignment.center, - children: const [ - FlutterLogo( - size: 35, - ), - Text( - 'Flutter Unit', - style: TextStyle(fontSize: 24, color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(1, 1), - blurRadius: 3) - ]), - ), - ], - ), - const SizedBox( - height: 15, - ), - Text( - 'The Unity Of Flutter, The Unity Of Coder.', - style: TextStyle(fontSize: 15, color: Colors.white, shadows: [ - Shadow(color: color, offset: const Offset(.5, .5), blurRadius: 1) - ]), - ), - const SizedBox( - height: 5, - ), - Text( - 'Flutter的联合,编程者的联合。', - style: TextStyle(fontSize: 15, color: Colors.white, shadows: [ - Shadow(color: color, offset: const Offset(.5, .5), blurRadius: 1) - ]), - ), - const SizedBox( - height: 10, - ), - Row( - children: const [ - Spacer( - flex: 5, - ), - Text( - '—— 张风捷特烈', - style: TextStyle(fontSize: 15, color: Colors.white, shadows: [ - Shadow( - color: Colors.orangeAccent, - offset: Offset(.5, .5), - blurRadius: 1) - ]), - ), - Spacer( - flex: 1, - ), - ], - ), - ], - ), - ); - } -} diff --git a/lib/app/navigation/unit_navigation.dart b/lib/app/navigation/unit_navigation.dart deleted file mode 100644 index b0dd3b762..000000000 --- a/lib/app/navigation/unit_navigation.dart +++ /dev/null @@ -1,133 +0,0 @@ -import 'dart:io'; - -import 'package:app/app.dart'; -import 'package:app_update/app_update.dart'; -import 'package:artifact/artifact.dart'; -import 'package:authentication/authentication.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/painter_system/gallery_unit.dart'; -import 'package:flutter_unit/widget_ui/mobile/category_page/collect_page.dart'; -import 'package:flutter_unit/widget_ui/mobile/category_page/home_right_drawer.dart'; -import 'package:widget_module/blocs/blocs.dart'; -import 'package:flutter_unit/widget_ui/mobile/widget_panel/standard_home_page.dart'; - - -import 'pure_bottom_bar.dart'; -import 'desk_ui/unit_desk_navigation.dart'; - -/// create by 张风捷特烈 on 2020-04-11 -/// contact me by email 1981462002@qq.com -/// 说明: 主题结构 左右滑页 + 底部导航栏 - -class UnitNavigation extends StatelessWidget { - const UnitNavigation({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return BlocBuilder( - builder: (_, state) => LayoutBuilder(builder: (_, c) { - if (c.maxWidth > 500) { - return UnitDeskNavigation(); - } - return UnitPhoneNavigation(); - }), - ); - } -} - -class UnitPhoneNavigation extends StatefulWidget { - const UnitPhoneNavigation({Key? key}) : super(key: key); - - @override - _UnitPhoneNavigationState createState() => _UnitPhoneNavigationState(); -} - -class _UnitPhoneNavigationState extends State { - //页面控制器,初始 0 - final PageController _controller = PageController(); - int position = 0; - - // 禁止 PageView 滑动 - final ScrollPhysics _neverScroll = const NeverScrollableScrollPhysics(); - - @override - void initState() { - super.initState(); - if (Platform.isAndroid||Platform.isIOS) { - BlocProvider.of(context).add(const CheckUpdate(appName: 'FlutterUnit')); - } - } - - @override - void dispose() { - _controller.dispose(); //释放控制器 - super.dispose(); - } - - /// extendBody = true 凹嵌透明,需要处理底部 边距 - @override - Widget build(BuildContext context) { - return Scaffold( - extendBody: true, - endDrawer: const HomeRightDrawer(), - body: PageView( - physics: _neverScroll, - controller: _controller, - children: [ - StandardHomePage(), - GalleryUnit(), - ArtifactPage(), - CollectPageAdapter(), - UserPage(), - ], - ), - bottomNavigationBar: _buildBottomNav(context), - ); - } - - bool get isDark => Theme.of(context).brightness == Brightness.dark; - - - // 由于 bottomNavigationBar 颜色需要随 点击头部栏 状态而改变, - // 使用 BlocBuilder 构建 - Widget _buildBottomNav(BuildContext context) { - return Stack( - children: [ - PureBottomBar( - initPosition: position, - onItemTap: _onTapBottomNav, - onItemLongTap: _onItemLongTap, - ), - const Positioned(right: 26, top: 8, child: UpdateRedPoint()) - ], - ); - } - - // 点击底部按钮事件,切换页面 - void _onTapBottomNav(int index) { - _controller.jumpToPage(index); - position = index; - if (!isDark) { - late Color color; - if (index != 0) { - color = Theme.of(context).primaryColor; - } else { - } - } - - if (index == 3) { - BlocProvider.of(context).add(const EventLoadLikeData()); - } - } - - // 两侧 - void _onItemLongTap(BuildContext context, int index) { - if (index == 0) { - Scaffold.of(context).openDrawer(); - } - if (index == 4) { - Scaffold.of(context).openEndDrawer(); - } - } -} diff --git a/lib/app/router/unit_router.dart b/lib/app/router/unit_router.dart deleted file mode 100644 index 687a8dfeb..000000000 --- a/lib/app/router/unit_router.dart +++ /dev/null @@ -1,148 +0,0 @@ -// ignore_for_file: constant_identifier_names - -import 'dart:io'; - -import 'package:app/app.dart'; -import 'package:authentication/authentication.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_unit/app/navigation/unit_navigation.dart'; -import 'package:flutter_unit/app/views/about/about_app_page.dart'; -import 'package:flutter_unit/app/views/about/about_me_page.dart'; -import 'package:flutter_unit/app/views/about/version_info.dart'; -import 'package:flutter_unit/app/views/data_manage/data_manage_page.dart'; -import 'package:flutter_unit/app/views/setting/code_style_setting.dart'; -import 'package:flutter_unit/app/views/setting/font_setting.dart'; -import 'package:flutter_unit/app/views/setting/item_style_setting.dart'; -import 'package:flutter_unit/app/views/setting/setting_page.dart'; -import 'package:flutter_unit/app/views/setting/theme_color_setting.dart'; -import 'package:flutter_unit/app/views/unit_todo/attr_unit_page.dart'; -import 'package:flutter_unit/app/views/unit_todo/layout_unit_page.dart'; -import 'package:flutter_unit/app/views/unit_todo/point_unit_page.dart'; -import 'package:flutter_unit/point_system/views/issues_point/issues_detail.dart'; -import 'package:flutter_unit/point_system/views/issues_point/issues_point_page.dart'; -import 'package:flutter_unit/widget_ui/desk_ui/widget_detail/widget_detail_page.dart'; -import 'package:flutter_unit/widget_ui/mobile/category_page/category_detail.dart'; -import 'package:flutter_unit/widget_ui/mobile/category_page/collect_page.dart'; -import 'package:flutter_unit/widget_ui/mobile/search_page/search_page.dart'; -import 'package:flutter_unit/widget_ui/mobile/widget_detail/widget_detail_page.dart'; -import 'package:widget_repository/widget_repository.dart'; - - -class UnitRouters { - static const String widget_detail = '/widget_detail'; - - static const String detail = 'detail'; - // static const String search = 'search_bloc'; - - - static const String collect = 'CollectPage'; - static const String point = 'IssuesPointPage'; - static const String point_detail = 'IssuesDetailPage'; - - static const String setting = 'SettingPage'; - static const String font_setting = 'FountSettingPage'; - static const String theme_color_setting = 'ThemeColorSettingPage'; - static const String code_style_setting = 'CodeStyleSettingPage'; - static const String item_style_setting = 'ItemStyleSettingPage'; - static const String version_info = 'VersionInfo'; - static const String login = 'login'; - - static const String category_show = 'CategoryShow'; - static const String issues_point = 'IssuesPointPage'; - - static const String attr = 'AttrUnitPage'; - static const String bug = 'BugUnitPage'; - static const String layout = 'LayoutUnitPage'; - static const String about_me = 'AboutMePage'; - static const String about_app = 'AboutAppPage'; - static const String register = 'register'; - - static const String data_manage = 'DataManagePage'; - - static Route generateRoute(RouteSettings settings) { - switch (settings.name) { - // - case UnitRouter.nav: - if(Platform.isWindows||Platform.isMacOS||Platform.isLinux){ - return ZeroPageRoute( child: UnitNavigation()); - } - return SlidePageRoute(child: UnitNavigation()); - - // 组件详情页 - case widget_detail: - Widget child; - if(Platform.isWindows||Platform.isMacOS||Platform.isLinux){ - child = DeskWidgetDetailPageScope( - model: settings.arguments as WidgetModel, - ); - }else{ - child = WidgetDetailPageScope( - model: settings.arguments as WidgetModel, - ); - } - - return SlidePageRoute(child: child); - - // case search: - // return Right2LeftRouter(child: const SearchPageProvider()); - case collect: - return SlidePageRoute(child: CollectPageAdapter( - canPop: settings.arguments as bool, - )); - - case setting: - return SlidePageRoute(child: const SettingPage()); - // return Right2LeftRouter(builder:(_)=> const SettingPage()); - // return MaterialPageRoute(builder:(_)=> const SettingPage()); - case data_manage: - return SlidePageRoute(child: const DataManagePage()); - case font_setting: - return SlidePageRoute(child: const FontSettingPage()); - case theme_color_setting: - return SlidePageRoute(child: const ThemeColorSettingPage()); - case code_style_setting: - return SlidePageRoute(child: const CodeStyleSettingPage()); - // case item_style_setting: - // return Right2LeftRouter(child: const ItemStyleSettingPage()); - - case version_info: - return SlidePageRoute(child: const VersionInfo()); - - case issues_point: - return SlidePageRoute(child: const IssuesPointScope()); - case login: - return SlidePageRoute(child: const LoginPage()); - - case register: - return SlidePageRoute(child: const RegisterPage()); - - case attr: - return SlidePageRoute(child: const AttrUnitPage()); - case bug: - return SlidePageRoute(child: const BugUnitPage()); - case layout: - return SlidePageRoute(child: const LayoutUnitPage()); - case about_app: - return SlidePageRoute(child: const AboutAppPage()); - case about_me: - return SlidePageRoute(child: const AboutMePage()); - - case point_detail: - return SlidePageRoute(child: const IssuesDetailPage()); - - case category_show: - return SlidePageRoute( - child: CategoryShow( - model: settings.arguments as CategoryModel, - )); - - default: - return MaterialPageRoute( - builder: (_) => Scaffold( - body: Center( - child: Text('No route defined for ${settings.name}'), - ), - )); - } - } -} diff --git a/lib/app/utils/Toast.dart b/lib/app/utils/Toast.dart deleted file mode 100644 index 2f174108e..000000000 --- a/lib/app/utils/Toast.dart +++ /dev/null @@ -1,32 +0,0 @@ -// import 'package:flutter/material.dart'; -// -// class Toast { -// static toast(BuildContext context, String msg, -// {duration = const Duration(milliseconds: 600), -// Color? color, -// SnackBarAction? action}) { -// -// ScaffoldMessenger.of(context).showSnackBar(SnackBar( -// content: Text(msg), -// duration: duration, -// action: action, -// backgroundColor: color??Theme.of(context).primaryColor, -// )); -// } -// -// static void error(BuildContext context,String msg){ -// toast(context,msg, color:Colors.red, ); -// } -// -// static void warning(BuildContext context,String msg){ -// toast(context,msg, color:Colors.orange, ); -// } -// -// static void success(BuildContext context,String msg){ -// toast(context,msg, color:Theme.of(context).primaryColor, ); -// } -// -// static void green(BuildContext context,String msg){ -// toast(context,msg, color:Colors.green, ); -// } -// } diff --git a/lib/app/utils/color_utils.dart b/lib/app/utils/color_utils.dart deleted file mode 100644 index 75170c730..000000000 --- a/lib/app/utils/color_utils.dart +++ /dev/null @@ -1,73 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; - -import 'random_provider.dart'; - - - -class ColorUtils { - static Color randomColor({ - int limitA = 120, - int limitR = 0, - int limitG = 0, - int limitB = 0, - }) { - Random random = RandomProvider.random; - int a = limitA + random.nextInt(256 - limitA); //透明度值 - int r = limitR + random.nextInt(256 - limitR); //红值 - int g = limitG + random.nextInt(256 - limitG); //绿值 - int b = limitB + random.nextInt(256 - limitB); //蓝值 - return Color.fromARGB(a, r, g, b); //生成argb模式的颜色 - } - - - /// 使用方法: - /// var color1=ColorUtils.parse("#33428A43"); - /// var color2=ColorUtils.parse("33428A43"); - /// var color3=ColorUtils.parse("#428A43"); - ///var color4=ColorUtils.parse("428A43"); - /// - static Color parse(String code) { - Color result =Colors.red; - int value = 0 ; - if (code.contains("#")) { - try { - value = int.parse(code.substring(1), radix: 16); - } catch (e) { - print(e); - } - switch (code.length) { - case 7://6位 - result = Color(value + 0xFF000000); - break; - case 9://8位 - result = Color(value); - break; - default: - result =Colors.red; - } - }else { - try { - value = int.parse(code, radix: 16); - } catch (e) { - print(e); - } - switch (code.length) { - case 6: - result = Color(value + 0xFF000000); - break; - case 8: - result = Color(value); - break; - default: - result =Colors.red; - } - } - return result; - } - - static String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} - diff --git a/lib/app/utils/convert.dart b/lib/app/utils/convert.dart deleted file mode 100644 index 9c3c2ec99..000000000 --- a/lib/app/utils/convert.dart +++ /dev/null @@ -1,51 +0,0 @@ - -import 'package:flutter_unit/painter_system/gallery_factory.dart'; -import 'package:widget_repository/widget_repository.dart'; - -/// create by 张风捷特烈 on 2020-03-07 -/// contact me by email 1981462002@qq.com -/// 说明: - -class Convert { - static WidgetFamily toFamily(int id) { - switch (id) { - case 0: - return WidgetFamily.statelessWidget; - case 1: - return WidgetFamily.statefulWidget; - case 2: - return WidgetFamily.singleChildRenderObjectWidget; - case 3: - return WidgetFamily.multiChildRenderObjectWidget; - case 4: - return WidgetFamily.sliver; - case 5: - return WidgetFamily.proxyWidget; - case 6: - return WidgetFamily.other; - default: - return WidgetFamily.statelessWidget; - } - } - - static Map galleryTypeMap = { - GalleryType.base: "基础绘制", - GalleryType.fun: "趣味绘制", - GalleryType.particle: "粒子绘制", - GalleryType.anim: "动画手势", - GalleryType.art: "艺术画廊", - }; - - static String convertFileSize(int size){ - double result = size / 1024.0; - if(result<1024){ - return "${result.toStringAsFixed(2)} Kb"; - }else if(result>1024&&result<1024*1024){ - return "${(result/1024).toStringAsFixed(2)} Mb"; - }else{ - return "${(result/1024/1024).toStringAsFixed(2)} Gb"; - } - } - - -} diff --git a/lib/app/utils/pather.dart b/lib/app/utils/pather.dart deleted file mode 100644 index 6953fdf34..000000000 --- a/lib/app/utils/pather.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/cupertino.dart'; - -class Pather { - Pather._(); - - static Pather create = Pather._(); - - final Path _path = Path(); - - Path nStarPath(int num, double R, double r, {dx = 0, dy = 0}) { - _path.reset();//重置路径 - double perRad = 2 * pi / num;//每份的角度 - double radA = perRad / 2 / 2;//a角 - double radB = 2 * pi / (num - 1) / 2 - radA / 2 + radA;//起始b角 - _path.moveTo(cos(radA) * R + dx, -sin(radA) * R + dy);//移动到起点 - for (int i = 0; i < num; i++) {//循环生成点,路径连至 - _path.lineTo(cos(radA + perRad * i) * R + dx, -sin(radA + perRad * i) * R + dy); - _path.lineTo(cos(radB + perRad * i) * r + dx, -sin(radB + perRad * i) * r + dy); - } - _path.close(); - return _path; - } -} diff --git a/lib/app/utils/toly_utils.dart b/lib/app/utils/toly_utils.dart deleted file mode 100644 index ea48a992e..000000000 --- a/lib/app/utils/toly_utils.dart +++ /dev/null @@ -1,3 +0,0 @@ -library toly_utils; -export 'color_utils.dart'; -export 'random_provider.dart'; diff --git a/lib/app/views/about/version/version_shower.dart b/lib/app/views/about/version/version_shower.dart deleted file mode 100644 index 3c20a2cd9..000000000 --- a/lib/app/views/about/version/version_shower.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:package_info_plus/package_info_plus.dart'; - -class VersionShower extends StatefulWidget { - const VersionShower({Key? key}) : super(key: key); - - @override - _VersionShowerState createState() => _VersionShowerState(); -} - -class _VersionShowerState extends State { - String version = '1.0.0'; - - @override - void initState() { - super.initState(); - _fetchVersion(); - } - - @override - Widget build(BuildContext context) { - return Text('Version $version'); - } - - void _fetchVersion() async{ - PackageInfo packageInfo = await PackageInfo.fromPlatform(); - if(mounted) { - setState(() { - version= packageInfo.version; - }); - } - } -} diff --git a/lib/app/views/data_manage/data_manage_page.dart b/lib/app/views/data_manage/data_manage_page.dart deleted file mode 100644 index 51bca2ca9..000000000 --- a/lib/app/views/data_manage/data_manage_page.dart +++ /dev/null @@ -1,147 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; -import 'package:app/app.dart'; -import 'package:storage/storage.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -import 'package:flutter_unit/point_system/api/category_api.dart'; - -import 'package:authentication/views/authentic_widget.dart'; -import 'package:utils/utils.dart'; -import 'package:widget_module/blocs/blocs.dart'; - -import 'package:path/path.dart' as path; -import 'package:sqflite/sqflite.dart'; -import 'package:widget_repository/widget_repository.dart'; - -/// create by 张风捷特烈 on 2021/2/26 -/// contact me by email 1981462002@qq.com -/// 说明: -/// - -class DataManagePage extends StatelessWidget { - const DataManagePage({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('数据管理'), - ), - body: Builder( - builder: (ctx) => ListView( - children: [ - AuthenticWidget.just( - ListTile( - trailing: Icon( - TolyIcon.upload, - color: Theme.of(context).primaryColor, - ), - title: const Text('备份收藏集数据'), - onTap: () => _doUploadCategoryData(ctx), - ), - ), - AuthenticWidget.just(const Divider()), - AuthenticWidget.just(ListTile( - trailing: Icon( - TolyIcon.download, - color: Theme.of(context).primaryColor, - ), - title: const Text('同步收藏集数据'), - onTap: () => _doSync(ctx), - )), - AuthenticWidget.just(const Divider()), - ListTile( - trailing: Icon( - Icons.refresh, - color: Theme.of(context).primaryColor, - ), - title: const Text('收藏集数据重置'), - // trailing: _nextIcon(context), - onTap: () => _recallDatabase(ctx), - ), - const Divider(), - ], - ), - ), - ); - } - - _recallDatabase(BuildContext context) async { - String databasesPath = await getDatabasesPath(); - String dbPath = path.join(databasesPath, "flutter.db"); - ByteData data = await rootBundle.load(path.join("assets", "flutter.db")); - List bytes = - data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); - await File(dbPath).writeAsBytes(bytes, flush: true); - print("==== debug ===== assets ======拷贝完成===="); - BlocProvider.of(context).add(const EventLoadCategory()); - BlocProvider.of(context).add(const EventLoadLikeData()); - Toast.toast(context, '重置成功!'); - } - - void _doUploadCategoryData(BuildContext context) async { - CategoryRepository rep = BlocProvider.of(context).repository; - List loadCategories = await rep.loadCategoryData(); - - List likeData = await FlutterDbStorage.instance.likeDao.likeWidgetIds(); - - String json = jsonEncode(loadCategories); - String likeJson = jsonEncode(likeData); - - TaskResult result = - await CategoryApi.uploadCategoryData(data: json, likeData: likeJson); - - if (result.success) { - Toast.toast(context, '数据集备份成功!'); - } else { - Toast.toast(context, '数据集备份失败!'); - } - } - - void _doSync(BuildContext context) async { - TaskResult result = await CategoryApi.getCategoryData(); - - if (result.success) { - // 说明请求成功 - if (result.data != null) { - //说明有后台备份数据,进行同步操作 - CategoryRepository repository = - BlocProvider.of(context).repository; - await repository.syncCategoryByData( - result.data!.data, result.data!.likeData); - BlocProvider.of(context).add(const EventLoadCategory()); - BlocProvider.of(context).add(const EventLoadLikeData()); - } else { - // 说明还没有后台数据, - // 这里防止有傻孩子没点备份,就点同步,哥哥好心,给备份一下。 - CategoryRepository rep = - BlocProvider.of(context).repository; - List loadCategories = await rep.loadCategoryData(); - List likeData = await FlutterDbStorage.instance.likeDao.likeWidgetIds(); - - String json = jsonEncode(loadCategories); - String likeJson = jsonEncode(likeData); - await CategoryApi.uploadCategoryData(data: json, likeData: likeJson); - } - Toast.toast(context, '数据同步份成功!'); - } else { - Toast.toast(context, '数据同步份失败!'); - } - } -} - -// class LoadingIndicate extends StatefulWidget { -// Future Function task; -// @override -// _LoadingIndicateState createState() => _LoadingIndicateState(); -// } -// -// class _LoadingIndicateState extends State { -// @override -// Widget build(BuildContext context) { -// return Container(); -// } -// } diff --git a/lib/app/views/setting/setting_page.dart b/lib/app/views/setting/setting_page.dart deleted file mode 100644 index efc22e206..000000000 --- a/lib/app/views/setting/setting_page.dart +++ /dev/null @@ -1,168 +0,0 @@ -import 'package:app/app.dart'; -import 'package:components/components.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/app/navigation/overlay_tool_wrapper.dart'; - - -import 'app_style_setting.dart'; -import 'theme_model_setting.dart'; - -class SettingPage extends StatelessWidget { - - const SettingPage({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - const Widget divider = Divider(height: 1); - - return Scaffold( - appBar: AppBar(title:Text('应用设置')), - body: ListView( - children: [ - Container( height: 15), - ListTile( - leading: Icon( - Icons.style, - color: Theme.of(context).primaryColor, - ), - title: const Text('深色模式', style: TextStyle(fontSize: 16)), - subtitle: BlocBuilder( - builder: (_,state)=>Text( - themeMode2Str[state.themeMode]! - , style: const TextStyle(fontSize: 12,color: Colors.grey) - ), - ), - trailing: _nextIcon(context), - onTap: (){ - Navigator.of(context).push(SlidePageRoute(child: ThemeModelSetting())); - }, - ), - divider, - ListTile( - leading: Icon( - Icons.palette, - color: Theme.of(context).primaryColor, - ), - title: const Text('主题色设置', style: TextStyle(fontSize: 16)), - subtitle: BlocBuilder( - builder: (_,state)=>Text( - Cons.kThemeColorSupport[state.themeColor]!, - style: TextStyle(color: state.themeColor,fontSize: 12), - ), - ), - trailing: _nextIcon(context), - onTap: () => Navigator.of(context).pushNamed(UnitRouter.theme_color_setting), - ), - // divider, - Container( height: 10), - ListTile( - leading: Icon( - Icons.translate, - color: Theme.of(context).primaryColor, - ), - title: const Text('字体设置', style: TextStyle(fontSize: 16)), - subtitle: BlocBuilder( - builder: (_,state)=>Text( - state.fontFamily,style: TextStyle(fontSize: 12), - ), - ), - trailing: _nextIcon(context), - onTap: () => Navigator.of(context).pushNamed(UnitRouter.font_setting), - ), - divider, - ListTile( - leading: Icon( - TolyIcon.icon_code, - color: Theme.of(context).primaryColor, - ), - title: const Text('代码高亮样式', style: TextStyle(fontSize: 16)), - trailing: _nextIcon(context), - onTap: () => Navigator.of(context).pushNamed(UnitRouter.code_style_setting), - ), - // divider, - Container( height: 10,), - _buildShowBg(context), - divider, - _buildShowOver(context), - // divider, - // _buildShowTool(context), - // divider, - Container( height: 10), - ListTile( - leading: Icon( - Icons.info, - color: Theme.of(context).primaryColor, - ), - title: const Text('版本信息', style: TextStyle(fontSize: 16)), - trailing: _nextIcon(context), - onTap: () => Navigator.of(context).pushNamed(UnitRouter.version_info), - ), - ], - ), - ); - } - -//SwitchListTile( -// inactiveTrackColor: Colors.grey.withOpacity(0.3), -// // inactiveThumbColor: Colors.white, -// activeColor: Theme.of(context).primaryColor, -// value: state.showBackGround, -// secondary: Icon( -// TolyIcon.icon_background, -// color: Theme.of(context).primaryColor, -// ), -// title: const Text('显示背景', style: TextStyle(fontSize: 16)), -// onChanged: (show) { -// BlocProvider.of(context).switchShowBg(show); -// }, -// ) - - Widget _buildShowBg(BuildContext context) => - BlocBuilder( - builder: (_, state) => TolySwitchListTile( - secondary:Icon( - TolyIcon.icon_background, - color: Theme.of(context).primaryColor, - ), - title: const Text('显示背景', style: TextStyle(fontSize: 16,fontWeight: FontWeight.bold)) - , value: state.showBackGround, onChanged: (bool value) { - BlocProvider.of(context).switchShowBg(value); - }, - ),); - - Widget _buildShowOver(BuildContext context) => - BlocBuilder( - builder: (_, state) => TolySwitchListTile( - secondary:Icon( - TolyIcon.icon_background, - color: Theme.of(context).primaryColor, - ), - title: const Text('显示性能浮层', style: TextStyle(fontSize: 16,fontWeight: FontWeight.bold)) - , value: state.showPerformanceOverlay, onChanged: (bool value) { - BlocProvider.of(context).switchShowOver(value); - }, - )); - - Widget _buildShowTool(BuildContext context) => - BlocBuilder( - builder: (_, state) => SwitchListTile( - value: state.showOverlayTool, - secondary: Icon( - TolyIcon.icon_layout, - color: Theme.of(context).primaryColor, - ), - title: const Text('显示浮动工具', style: TextStyle(fontSize: 16)), - onChanged: (show) { - if(show){ - OverlayToolWrapper.of(context).showFloating(); - }else{ - OverlayToolWrapper.of(context).hideFloating(); - } - BlocProvider.of(context).switchShowTool(show); - }, - )); - - - Widget _nextIcon(BuildContext context) => Icon(Icons.chevron_right, color: Theme.of(context).primaryColor); -} diff --git a/lib/app/views/setting/theme_color_setting.dart b/lib/app/views/setting/theme_color_setting.dart deleted file mode 100644 index 9ed91dc53..000000000 --- a/lib/app/views/setting/theme_color_setting.dart +++ /dev/null @@ -1,93 +0,0 @@ -import 'package:app/app.dart'; -import 'package:components/components.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:components/toly_ui/toly_ui.dart'; - -/// create by 张风捷特烈 on 2020-04-10 -/// contact me by email 1981462002@qq.com -/// 说明: - -class ThemeColorSettingPage extends StatelessWidget { - const ThemeColorSettingPage({Key? key}) : super(key: key); - - - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: UnitColor.scaffoldBgLight, - appBar: const UnitAppbar(title:'主题色设置'), - body: BlocBuilder( - builder: (_, state) => _buildCell( - context, Cons.kThemeColorSupport.keys.toList(), state.themeColor)), - ); - } - - Widget _buildCell( - BuildContext context, List themeColorSupport, MaterialColor color) { - return GridView.count( - padding: const EdgeInsets.only(top: 20, left: 10, right: 10), - shrinkWrap: true, - crossAxisCount: 2, - mainAxisSpacing: 10, - crossAxisSpacing: 10, - childAspectRatio: 1.5, - children: themeColorSupport - .map((MaterialColor c) => FeedbackWidget( - a: 0.95, - duration: const Duration(milliseconds: 200), - onPressed: () => BlocProvider.of(context).switchThemeColor(c), - child: GridTile( - header: Container( - decoration: BoxDecoration( - borderRadius: const BorderRadius.only(topLeft: Radius.circular(10),topRight: Radius.circular(10)), - color: color == c - ? Colors.blue.withAlpha(88): - Colors.grey.withAlpha(55), - ), - padding: const EdgeInsets.only(left: 10, right: 5), - height: 30, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Spacer(), - Text(colorString(c), - style: const TextStyle( - color: Colors.white, - )), - const Spacer(), - if (color == c) const Padding( - padding: EdgeInsets.only(right:8.0), - child: Circle(color: Colors.white,radius: 7,), - ) - ], - ), - ), - child: Container( - decoration: BoxDecoration( - borderRadius: const BorderRadius.all(Radius.circular(10)), - gradient: LinearGradient(colors: [ - c.shade50, - c.shade100, - c.shade200, - c.shade300, - c.shade400, - c.shade500, - c.shade600, - c.shade700, - c.shade800, - c.shade900, - ])), - alignment: const Alignment(0,0.35), - child: Text( - '${Cons.kThemeColorSupport[c]}', - style: const TextStyle(fontSize: 18,color: Colors.white,fontWeight: FontWeight.bold), - )), - ))) - .toList(), - ); - } - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/lib/app/views/setting/theme_model_setting.dart b/lib/app/views/setting/theme_model_setting.dart deleted file mode 100644 index 40979d373..000000000 --- a/lib/app/views/setting/theme_model_setting.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:app/app.dart'; -import 'package:app/app/style/unit_color.dart'; -import 'package:components/project_ui/unit_app_bar.dart'; -import 'package:components/toly_ui/toly_ui.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -class ThemeModelSetting extends StatelessWidget { - const ThemeModelSetting({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - - ThemeMode mode = context.select((bloc) => bloc.state.themeMode); - Color iconColor = Theme.of(context).primaryColor; - return Scaffold( - appBar: AppBar(title: Text('深色模式')), - body: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( height: 15,), - TolySwitchListTile( - title: const Text('跟随系统', style: TextStyle(fontSize: 16,fontWeight: FontWeight.bold)), - subtitle: Text( - '开启后,将跟随系统打开或关闭深色模式',style: TextStyle(fontSize: 12,color: Colors.grey), - ), value: mode == ThemeMode.system, onChanged: (bool value) { - print("========value:$value=========="); - ThemeMode newModel; - if(value){ - newModel = ThemeMode.system; - }else{ - newModel = ThemeMode.light; - } - context.read().changeThemeMode(newModel); - }, - // trailing: _nextIcon(context), - // : () => Navigator.of(context).pushNamed(UnitRouter.font_setting), - ), - // AppStyle locale = Cons.kAppStyleStringMap.keys.toList()[index]; - // AppStyle style = BlocProvider.of(context).state.appStyle; - // bool checked = style == locale; - // Color color = Theme.of(context).primaryColor; - Padding( - padding: const EdgeInsets.only(left: 10,top: 16,bottom: 6), - child: Text("手动设置"), - ), - ListTile( - title: Text('浅色模式'), - onTap: (){ - context.read().changeThemeMode(ThemeMode.light); - }, - trailing: mode == ThemeMode.light ? Icon(Icons.check, size: 20, color: iconColor) : null, - ), - Divider(), - ListTile( - title: Text('深色模式'), - onTap: (){ - context.read().changeThemeMode(ThemeMode.dark); - }, - trailing: mode == ThemeMode.dark ? Icon(Icons.check, size: 20, color: iconColor) : null, - ) - ], - ), - );; - } -} diff --git a/lib/app/views/splash/standard_unit_splash.dart b/lib/app/views/splash/standard_unit_splash.dart deleted file mode 100644 index bb05ecfb8..000000000 --- a/lib/app/views/splash/standard_unit_splash.dart +++ /dev/null @@ -1,137 +0,0 @@ -import 'dart:math'; - -import 'package:app/app.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:utils/utils.dart'; - -import 'package:widget_module/blocs/blocs.dart'; - -import 'dart:ui' as ui; - -import 'package:widget_repository/widget_repository.dart'; - -import 'flutter_unit_text.dart'; - -/// create by 张风捷特烈 on 2020-03-07 -/// contact me by email 1981462002@qq.com -/// 说明: app 闪屏页 - -class StandardUnitSplash extends StatefulWidget { - const StandardUnitSplash({Key? key}) : super(key: key); - - @override - _StandardUnitSplashState createState() => _StandardUnitSplashState(); -} - -class _StandardUnitSplashState extends State - with TickerProviderStateMixin { - static const int _minCost = 1500; - - int _recorder = 0; - - final Paint paint = Paint() - ..style = PaintingStyle.stroke - ..shader = ui.Gradient.linear( - const Offset(0, 0), - const Offset(22, 0), - [Colors.red, Colors.yellow, Colors.blue, Colors.green], - [1 / 4, 2 / 4, 3 / 4, 1], - TileMode.mirror, - Matrix4.rotationZ(pi / 4).storage, - ); - - @override - void initState() { - super.initState(); - _recorder = DateTime.now().millisecondsSinceEpoch; - } - - @override - Widget build(BuildContext context) { - final Size winSize = MediaQuery.of(context).size; - return AnnotatedRegion( - value: const SystemUiOverlayStyle( - statusBarColor: Colors.transparent, - statusBarIconBrightness: Brightness.dark), - child: Material( - child: BlocListener( - listener: _listenStart, - listenWhen: (p,n)=>p.dbPath.isEmpty&&n.dbPath.isNotEmpty, - child: Column( - children: [ - const Spacer(), - Expanded( - child: Wrap( - direction: Axis.vertical, - alignment: WrapAlignment.center, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Stack( - children: [ - Text( - "U", - style: TextStyle( - fontSize: 26, - height: 1, - fontWeight: FontWeight.bold, - foreground: paint), - ), - const FlutterLogo(size: 60), - ], - ), - const SizedBox( - height: 20, - ), - _buildFlutterUnitText(winSize.height, winSize.width), - ], - )), - Expanded( - child: Stack( - alignment: Alignment.bottomCenter, - children: [ - Positioned( - bottom: 15, - child: Wrap( - direction: Axis.vertical, - alignment: WrapAlignment.center, - crossAxisAlignment: WrapCrossAlignment.center, - children: const [ - Text("Power By 张风捷特烈", - style: UnitTextStyle.splashShadows), - Text("· 2021 · @编程之王 ", - style: UnitTextStyle.splashShadows), - ], - )), - ], - )) - ], - )), - ), - ); - } - - Widget _buildFlutterUnitText(double winH, double winW) { - return FlutterUnitText( - text: StrUnit.appName, - color: Theme.of(context).primaryColor, - ); - } - - // 监听资源加载完毕,启动,触发事件 - void _listenStart(BuildContext context, AppState state) { - HttpUtil.instance.rebase(PathUnit.baseUrl); - int cost = DateTime.now().millisecondsSinceEpoch - _recorder; - BlocProvider.of(context) - .add(const EventTabTap(WidgetFamily.statelessWidget)); - BlocProvider.of(context).add(const EventLoadLikeData()); - BlocProvider.of(context).add(const EventLoadCategory()); - - // 启动耗时小于 _minCost 时,等待 delay 毫秒 - int delay = cost < _minCost ? _minCost - cost : 0; - Future.delayed(Duration(milliseconds: delay)).then((value) { - Navigator.of(context).pushReplacementNamed(UnitRouter.nav); - }); - } -} diff --git a/lib/awesome/listenable/change_notifier_02/main.dart b/lib/awesome/listenable/change_notifier_02/main.dart deleted file mode 100644 index 3d04b86e5..000000000 --- a/lib/awesome/listenable/change_notifier_02/main.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'package:app/app.dart'; -import 'package:flutter/material.dart'; - -import 'notifier/download_data_scope.dart'; -import 'notifier/progress_value_notifier.dart'; -import 'page/home/home_page.dart'; - -void main(){ - WidgetsFlutterBinding.ensureInitialized(); - runApp(const MyApp()); - WindowsAdapter.setSize(); -} - -class MyApp extends StatelessWidget{ - const MyApp({super.key}); - - @override - Widget build(BuildContext context) { - return DownloadDataScope( - notifier: ProgressValueNotifier(), - child: MaterialApp( - debugShowCheckedModeBanner: false, - theme: ThemeData( - colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), - useMaterial3: true, - ), - home: const ChangeNotifierHome02(), - ), - ); - } -} - - - - - - - diff --git a/lib/code_gen/bloc/state.dart b/lib/code_gen/bloc/state.dart deleted file mode 100644 index e7bac1b94..000000000 --- a/lib/code_gen/bloc/state.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/code_gen/model/class.dart'; - -class ClassGenBloc extends Cubit{ - - ClassGenBloc():super(Class(fields: [], name: '')); - - - -} \ No newline at end of file diff --git a/lib/code_gen/desk_widget_top_bar.dart b/lib/code_gen/desk_widget_top_bar.dart deleted file mode 100644 index 8654630c1..000000000 --- a/lib/code_gen/desk_widget_top_bar.dart +++ /dev/null @@ -1,72 +0,0 @@ -import 'package:app/app.dart'; -import 'package:components/toly_ui/toly_ui.dart'; -import 'package:flutter/material.dart'; - - - -class DeskCodeGenTopBar extends StatefulWidget { - final ValueChanged onTabPressed; - final VoidCallback onTapGen; - - const DeskCodeGenTopBar({Key? key,required this.onTabPressed, required this.onTapGen}) : super(key: key); - - @override - State createState() => _DeskCodeGenTopBarState(); -} - -class _DeskCodeGenTopBarState extends State with SingleTickerProviderStateMixin { - late TabController tabController; - - static const List _tabs = ['IconFont', '数据类' , '状态管理', 'Json 解析',]; - - @override - void initState() { - super.initState(); - tabController = TabController(length: _tabs.length, vsync: this); - } - - @override - Widget build(BuildContext context) { - Color themeColor = Theme.of(context).primaryColor; - - bool isDark = Theme.of(context).brightness == Brightness.dark; - return DragToMoveAreaNoDouble( - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 20), - height: 64, - color: isDark?Color(0xff2C3036):Colors.white, - child: Row( - children: [ - SizedBox( - width: 350, - child: TabBar( - onTap: widget.onTabPressed, - indicatorSize: TabBarIndicatorSize.label, - labelPadding: const EdgeInsets.symmetric(horizontal: 6), - isScrollable: false, - indicator: RoundRectTabIndicator( - borderSide: BorderSide(color: themeColor, width: 3), - ), - labelStyle: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), - controller: tabController, - labelColor: themeColor, - indicatorWeight: 3, - unselectedLabelColor: Colors.grey, - indicatorColor: themeColor, - tabs: - _tabs.map((String name) => Tab(text: name)).toList(), - ), - ), - Spacer(), - - const SizedBox(width: 20,), - WindowButtons(), - ], - ), - ), - ); - } -} diff --git a/lib/components/top_bar/desk_simple_top_bar.dart b/lib/components/top_bar/desk_simple_top_bar.dart deleted file mode 100644 index 0cd3080bd..000000000 --- a/lib/components/top_bar/desk_simple_top_bar.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:app/app.dart'; -import 'package:flutter/material.dart'; - -class SimpleDeskTopBar extends StatelessWidget { - final Widget? leading; - - const SimpleDeskTopBar({super.key, this.leading}); - - @override - Widget build(BuildContext context) { - return DragToMoveAreaNoDouble( - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 20), - height: 64, - color: Colors.white, - child: Row( - children: [ - if (leading != null) leading!, - const Spacer(), - const SizedBox( - width: 20, - ), - const WindowButtons(), - ], - ), - ), - ); - } -} diff --git a/lib/main.dart b/lib/main.dart index a0544e0e8..442228576 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,16 +1,3 @@ -import 'package:app/app.dart'; -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; - -import 'app/bloc_wrapper.dart'; -import 'app/flutter_unit.dart'; - - -void main() { - WidgetsFlutterBinding.ensureInitialized(); - //滚动性能优化 1.22.0 - GestureBinding.instance.resamplingEnabled = true; - runApp(const BlocWrapper(child: FlutterUnit())); - WindowsAdapter.setSize(); -} +import 'src/starter/fx_application.dart'; +void main(List args) => const FxApplication().run(args); diff --git a/lib/painter_system/gallery_factory.dart b/lib/painter_system/gallery_factory.dart deleted file mode 100644 index dbdddfcf0..000000000 --- a/lib/painter_system/gallery_factory.dart +++ /dev/null @@ -1,270 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_unit/painter_system/anim/spring_widget.dart'; - -import 'anim/bezier3_player/bezier3_palyer.dart'; -import 'anim/circle_halo.dart'; -import 'anim/curve_shower/curve_anim_shower.dart'; -import 'anim/draw_path.dart'; -import 'anim/dundun_path.dart'; -import 'anim/rotate_by_point/rotate_by_point.dart'; -import 'art/circle_packing.dart'; -import 'art/cubic_disarray.dart'; -import 'art/hypnotic_squares.dart'; -import 'art/joy_division.dart'; -import 'art/piet_mondrian.dart'; -import 'art/tiled_lines.dart'; -import 'art/triangular_mesh.dart'; -import 'art/un_deux_trois.dart'; -import 'base/clock_widget.dart'; -import 'base/digital/digital_shower.dart'; -import 'base/draw_grid_axis.dart'; -import 'base/draw_path_fun.dart'; -import 'base/draw_picture.dart'; - -import 'base/n_side/n_side_page.dart'; -import 'base/polar/polar_painter_widget.dart'; -import 'base/windmill.dart'; -import 'fun/bufeng/bufeng_panel.dart'; -import 'fun/dundun_view.dart'; -import 'fun/random_portrait.dart'; -import 'fun/stemp/stamp_paper.dart'; -import 'particle/random/random_particle.dart'; -import 'particle/split/particle_split.dart'; -import 'particle/split_img/split_image.dart'; -import 'picture_frame.dart'; - -/// create by 张风捷特烈 on 2020/12/5 -/// contact me by email 1981462002@qq.com -/// 说明: -/// - -enum GalleryType { base, anim, particle, fun, art } - -class GalleryFactory { - static List getGalleryByName(GalleryType type) { - switch (type) { - case GalleryType.base: - return const [ - FrameShower( - title: "The Chaos", - author: "张风捷特烈", - srcUrl: "/base/draw_picture.dart", - info: "本样例介绍如何进行图片的绘制:通过加载图片并将图片资源绘制到指定的区域。在上层绘制一批 45° 倾角的栅格线,来练习线条的绘制。", - content: DrawPicture()), - FrameShower( - title: "数字显示管", - author: "张风捷特烈", - srcUrl: "/base/digital", - info: "本样例介绍如何绘制 LED 数字显示管,以此练习对路径 Path 的使用、变换、组合,以及组件封装的知识。是一个非常好的绘制案例。", - content: DigitalShower()), - FrameShower( - title: "旋转风车", - author: "张风捷特烈", - srcUrl: "/base/windmill.dart", - info: "本样例介绍如何进行简单的路径绘制,以及画板的旋转,再结合动画让风车旋转。这是一个非常精简的绘制与动画结合的案例。", - content: WindmillWidget()), - FrameShower( - title: "平面直角坐标系", - author: "张风捷特烈", - srcUrl: "/base/draw_grid_axis.dart", - info: - "本样例介绍如何使用线路径和文字绘制网格坐标系,并将绘制对象进行封装,方便重用。坐标系也会在绘制时提供参考,入门必备。", - content: DrawGridAxis()), - FrameShower( - title: "平面极坐标系", - author: "张风捷特烈", - srcUrl: "/base/polar", - info: - "本样例介绍如何使用绘制平面的极坐标系,并根据函数方程收集极坐标进行绘制。", - content: PolarPainterWidget()), - FrameShower( - title: "曲线拟合", - author: "张风捷特烈", - srcUrl: "/base/draw_path_fun.dart", - info: "本样例介绍如何使用路径对函数曲线进行绘制,通过函数曲线上的少量点通过贝塞尔曲线进行拟合。", - content: DrawPathFun()), - FrameShower( - title: "圆中取形", - author: "张风捷特烈", - srcUrl: "/base/n_side", - info: "本样例介绍如何在圆中收集点位,绘制正多边形,是练习绘制及形成路径的很好案例。\n特殊操作:+、- 修改边数", - content: NSidePage()), - FrameShower( - title: "随机对称图", - author: "张风捷特烈", - srcUrl: '/fun/random_portrait.dart', - info: "本样例介绍绘制矩形及随机数处理。通过点位集合确定矩形位置信息,将其绘制出来。可以练习对数据的控制能力。\n特殊操作:点击随机生成", - content: RandomPortrait()), - FrameShower( - title: "简单时钟", - author: "张风捷特烈", - srcUrl: '/base/clock_widget.dart', - info: "本样例通过时钟的绘制,练习 Flutter 中旋转刻度类型的绘制技巧,并通过动画使表盘指针转动。", - content: ClockWidget()), - ]; - case GalleryType.anim: - return const [ - FrameShower( - title: "手势弹簧", - author: "张风捷特烈", - srcUrl: '/anim/spring_widget.dart', - info: "本样例介绍如何绘制弹簧,通过触点竖直拖拽拉伸、压缩,放手时进行恢复动画,是一个很好的综合小案例。\n特殊操作:上下拖拽伸缩弹簧", - content: SpringWidget()), - FrameShower( - title: "绕定点旋转", - author: "张风捷特烈", - srcUrl: '/anim/rotate_by_point', - info: "本样例介绍如何根据以某个点为中心,进行旋转运动。以此学习两点间的角度在绘制中的应用。\n特殊操作:点击运行", - content: RotateByPointWidget()), - FrameShower( - title: "流光", - author: "张风捷特烈", - srcUrl: '/anim/circle_halo.dart', - info: "本样例介绍如何在绘制中使用着色器和过滤器,并通过动画进行数值变化达到旋转流光效果。", - content: CircleHalo()), - FrameShower( - title: "曲线路径动画", - author: "张风捷特烈", - srcUrl: '/anim/draw_path.dart', - info: "本样例介绍如何使用路径绘制函数曲线,并使用路径测量进行动画", - content: DrawPath()), - FrameShower( - title: "冰墩墩线条动画", - author: "张风捷特烈", - srcUrl: '/anim/dundun_path.dart', - info: "本样例会绘制 2022 年北京冬奥会吉祥物冰墩墩的路径,并使用路径测量进行动画。\n特殊操作:点击运行", - content: DunDunPathPage()), - FrameShower( - title: "Bezier3 演示", - author: "张风捷特烈", - srcUrl: '/anim/bezier3_player', - info: "本样例介绍如何绘制三次贝塞尔曲线,通过触点判断某点是否激活,据此控制点的位置达到拖动控制效果。\n特殊操作:单击绘点,双击清除", - content: Bezier3Player()), - FrameShower( - title: "动画曲线散点图", - author: "张风捷特烈", - srcUrl: '/anim/curve_shower', - info: "本样例通过直观的方式,来查看动画曲线 curve 的作用效果,让大家对动画有更深的理解。\n特殊操作:点击运行", - content: CurveAnimShower()), - - ]; - case GalleryType.particle: - return const [ - FrameShower( - title: "随机粒子生成器", - author: "张风捷特烈", - srcUrl: '/particle/random', - info: "本样例介绍如何创建随机粒子及边界反弹逻辑处理,是学习粒子运动非常好的入门案例。\n特殊操作:单击停止/运行", - content: RandomParticle()), - FrameShower( - title: "粒子分裂", - author: "张风捷特烈", - srcUrl: '/particle/split', - info: "本样例介绍如何对个粒子进行碰撞检测,并分裂处多个粒子,是一个比较有趣的案例。\n特殊操作:单击重置", - content: ParticleSplit()), - FrameShower( - title: "图片粒子分裂", - author: "张风捷特烈", - srcUrl: '/particle/split_img', - info: "本样例介绍将图片使用粒子表示,并对粒子进行动画处理,达到爆炸的效果。\n特殊操作:单击运行", - content: SplitImage()), - ]; - case GalleryType.fun: - return const[ - FrameShower( - title: "Random Portrait", - author: "张风捷特烈", - srcUrl: '/fun/random_portrait.dart', - info: "本样例介绍绘制矩形及随机数处理。通过点位集合确定矩形位置信息,将其绘制出来。可以练习对数据的控制能力。\n特殊操作:点击随机生成", - content: RandomPortrait()), - FrameShower( - title: "冰墩墩", - author: "张风捷特烈", - srcUrl: '/fun/dundun_view.dart', - info: "本样例是绘制 2022 年北京冬奥会吉祥物冰墩墩的形体,从中可以学到路径绘制、渐变色等知识。", - content: DunDunView()), - FrameShower( - title: "蒲丰投针试验", - author: "张风捷特烈", - srcUrl: '/fun/bufeng', - info: "本样实现蒲丰投针试验的测试过程,根据概率来估算圆周率。其中可以学习到一些绘制小技巧已经数据的逻辑处理。", - content: BufengPanel()), - FrameShower( - title: "井字棋", - author: "张风捷特烈", - srcUrl: '/fun/stemp', - info: "本例通过井字棋的绘制与逻辑校验,集合了手势、绘制、动画、校验等重要的技能,是一个非常好的联系案例。\n特殊操作:双击重置", - content: StampPaper()), - ]; - case GalleryType.art: - return const [ - FrameShower( - title: "Tiled Line", - author: "generativeartistry.com", - srcUrl: '/art/tiled_lines.dart', - info: - "本样例根源来自generativeartistry.com的tiled-lines,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", - content: TiledLines(), - ), - FrameShower( - title: "Joy Division", - author: "generativeartistry.com", - srcUrl: '/art/joy_division.dart', - info: - "本样例根源来自generativeartistry.com的joy-division,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", - content: JoyDivision(), - ), - FrameShower( - title: "Cubic Disarray", - author: "generativeartistry.com", - srcUrl: '/art/cubic_disarray.dart', - info: - "本样例根源来自generativeartistry.com的cubic-disarray,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", - content: CubicDisarray(), - ), - FrameShower( - title: "Triangular Mesh", - author: "generativeartistry.com", - srcUrl: '/art/triangular_mesh.dart', - info: - "本样例根源来自generativeartistry.com的triangular-mesh,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", - content: TriangularMesh(), - ), - FrameShower( - title: "Un Deux Trois", - srcUrl: '/art/un_deux_trois.dart', - author: "generativeartistry.com", - info: - "本样例根源来自generativeartistry.com的un-deux-trois,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", - content: UnDeuxTrois(), - ), - FrameShower( - title: "Circle Packing", - author: "generativeartistry.com", - srcUrl: '/art/circle_packing.dart', - info: - "本样例根源来自generativeartistry.com的circle-packing,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", - content: CirclePacking(), - ), - FrameShower( - title: "Hypnotic Squares", - author: "generativeartistry.com", - srcUrl: '/art/hypnotic_squares.dart', - info: - "本样例根源来自generativeartistry.com的hypnotic-squares,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", - content: HypnoticSquares(), - ), - FrameShower( - title: "Piet Mondrian", - author: "generativeartistry.com", - srcUrl: '/art/piet_mondrian.dart', - info: - "本样例根源来自generativeartistry.com的piet-mondrian,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", - content: PietMondrian(), - ) - ]; - default: - return []; - } - } -} diff --git a/lib/point_system/api/issues_api.dart b/lib/point_system/api/issues_api.dart deleted file mode 100644 index 86f51578a..000000000 --- a/lib/point_system/api/issues_api.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'dart:convert'; - -import 'package:dio/dio.dart'; -import 'package:flutter_unit/point_system/github_model/github_model.dart'; - - -/// create by 张风捷特烈 on 2020/6/17 -/// contact me by email 1981462002@qq.com -/// 说明: - -const kBaseUrl = 'http://toly1994.com:8080/api/v1'; - -class IssuesApi { - static Dio dio = Dio(BaseOptions(baseUrl: kBaseUrl)); - - static Future getRepoFlutterUnit() async { - Response rep = await dio.get('/repository/name/FlutterUnit'); - dynamic repoStr = rep.data['data']['repositoryData']; - return Repository.fromJson(json.decode(repoStr)); - } - - static Future> getIssues( - {int page = 1, int pageSize = 100}) async { - List res = (await dio.get('/point', - queryParameters: {"page": page, "pageSize": pageSize})) - .data['data'] as List; - return res.map((e) => Issue.fromJson(json.decode(e['pointData']))).toList(); - } - - static Future> getIssuesComment(int pointId) async { - List res = (await dio.get('/pointComment/$pointId')).data['data'] as List; - return res - .map((e) => IssueComment.fromJson(json.decode(e['pointCommentData']))) - .toList(); - } -} diff --git a/lib/point_system/blocs/point_bloc/point_bloc.dart b/lib/point_system/blocs/point_bloc/point_bloc.dart deleted file mode 100644 index fd4d32815..000000000 --- a/lib/point_system/blocs/point_bloc/point_bloc.dart +++ /dev/null @@ -1,28 +0,0 @@ - -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/point_system/api/issues_api.dart'; - -import 'point_event.dart'; -import 'point_state.dart'; - - -/// create by 张风捷特烈 on 2020-09-03 -/// contact me by email 1981462002@qq.com -/// 说明: - -class PointBloc extends Bloc { - PointBloc() : super(PointLoading()){ - on(_onEventLoadPoint); - } - - void _onEventLoadPoint(PointEvent event,Emitter emit) async{ - emit( PointLoading()); - try { - final issues = await IssuesApi.getIssues(); - emit( PointLoaded(issues)); - } catch (err) { - print(err); - emit( PointLoadFailure(err.toString())); - } - } -} diff --git a/lib/point_system/blocs/point_bloc/point_event.dart b/lib/point_system/blocs/point_bloc/point_event.dart deleted file mode 100644 index 5c686d59c..000000000 --- a/lib/point_system/blocs/point_bloc/point_event.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:equatable/equatable.dart'; - -/// create by 张风捷特烈 on 2020/9/3 -/// contact me by email 1981462002@qq.com -/// 说明: - -abstract class PointEvent extends Equatable { - -} - -class EventLoadPoint extends PointEvent{ - - @override - List get props => []; - -} \ No newline at end of file diff --git a/lib/point_system/blocs/point_bloc/point_state.dart b/lib/point_system/blocs/point_bloc/point_state.dart deleted file mode 100644 index 43b8dc9e8..000000000 --- a/lib/point_system/blocs/point_bloc/point_state.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:equatable/equatable.dart'; -import 'package:flutter_unit/point_system/github_model/github_model.dart'; - - -/// create by 张风捷特烈 on 2020/9/3 -/// contact me by email 1981462002@qq.com -/// 说明: - -abstract class PointState extends Equatable{ - -} - -class PointLoading extends PointState{ - @override - List get props => []; -} - -class PointLoaded extends PointState{ - - final List issues; - - PointLoaded(this.issues); - - @override - List get props => [issues]; -} - - -class PointLoadFailure extends PointState{ - - final String error; - - PointLoadFailure(this.error); - - @override - List get props => [error]; -} \ No newline at end of file diff --git a/lib/point_system/blocs/point_comment_bloc/point_comment_bloc.dart b/lib/point_system/blocs/point_comment_bloc/point_comment_bloc.dart deleted file mode 100644 index 4977bf68e..000000000 --- a/lib/point_system/blocs/point_comment_bloc/point_comment_bloc.dart +++ /dev/null @@ -1,36 +0,0 @@ - -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/point_system/api/issues_api.dart'; -import 'package:flutter_unit/point_system/github_model/github_model.dart'; - - - -import 'point_comment_event.dart'; -import 'point_comment_state.dart'; - - -/// create by 张风捷特烈 on 2020-09-03 -/// contact me by email 1981462002@qq.com -/// 说明: - -class PointCommentBloc extends Bloc { - - PointCommentBloc() : super(PointCommentInitial()){ - on(_onEventLoadPointComment); - } - - void _onEventLoadPointComment(EventLoadPointComment event,Emitter emit) async{ - emit( PointCommentLoading(event.point)); - if(event.point.number==null){ - emit( PointCommentLoadFailure('point_bloc id 为空')); - } - try { - final List comments = await IssuesApi.getIssuesComment(event.point.number!); - comments.sort((a,b)=>a.createdAt!.compareTo(b.createdAt!)); - emit( PointCommentLoaded(event.point,comments)); - } catch (err) { - print(err); - emit( PointCommentLoadFailure(err.toString())); - } - } -} diff --git a/lib/point_system/blocs/point_comment_bloc/point_comment_event.dart b/lib/point_system/blocs/point_comment_bloc/point_comment_event.dart deleted file mode 100644 index 15da9ed50..000000000 --- a/lib/point_system/blocs/point_comment_bloc/point_comment_event.dart +++ /dev/null @@ -1,19 +0,0 @@ - - -import 'package:equatable/equatable.dart'; -import 'package:flutter_unit/point_system/github_model/github_model.dart'; - -/// create by 张风捷特烈 on 2020/9/3 -/// contact me by email 1981462002@qq.com -/// 说明: - -abstract class PointCommentEvent extends Equatable {} - -class EventLoadPointComment extends PointCommentEvent { - final Issue point; - - EventLoadPointComment(this.point); - - @override - List get props => [point]; -} diff --git a/lib/point_system/blocs/point_comment_bloc/point_comment_state.dart b/lib/point_system/blocs/point_comment_bloc/point_comment_state.dart deleted file mode 100644 index 1b53df4e4..000000000 --- a/lib/point_system/blocs/point_comment_bloc/point_comment_state.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'package:equatable/equatable.dart'; -import 'package:flutter_unit/point_system/github_model/github_model.dart'; - - -/// create by 张风捷特烈 on 2020/9/3 -/// contact me by email 1981462002@qq.com -/// 说明: - -abstract class PointCommentState extends Equatable{ - -} - -class PointCommentInitial extends PointCommentState{ - - - @override - List get props => []; -} - -class PointCommentLoading extends PointCommentState{ - final Issue issue; - - PointCommentLoading(this.issue); - - @override - List get props => [issue]; -} - -class PointCommentLoaded extends PointCommentState{ - - final Issue issue; - final List comments; - - PointCommentLoaded(this.issue,this.comments); - - @override - List get props => [issue,comments]; - - @override - String toString() { - return 'PointCommentLoaded{issue: $issue, comments: $comments}'; - } -} - - -class PointCommentLoadFailure extends PointCommentState{ - - final String error; - - PointCommentLoadFailure(this.error); - - @override - List get props => [error]; -} \ No newline at end of file diff --git a/lib/point_system/blocs/point_system_bloc.dart b/lib/point_system/blocs/point_system_bloc.dart deleted file mode 100644 index e9642c718..000000000 --- a/lib/point_system/blocs/point_system_bloc.dart +++ /dev/null @@ -1,9 +0,0 @@ -export './point_comment_bloc/point_comment_bloc.dart'; -export './point_comment_bloc/point_comment_event.dart'; -export './point_comment_bloc/point_comment_state.dart'; - -export './point_bloc/point_bloc.dart'; -export './point_bloc/point_event.dart'; -export './point_bloc/point_state.dart'; - - diff --git a/lib/point_system/views/desk_ui/desk_point_page.dart b/lib/point_system/views/desk_ui/desk_point_page.dart deleted file mode 100644 index 18cd6ad61..000000000 --- a/lib/point_system/views/desk_ui/desk_point_page.dart +++ /dev/null @@ -1,146 +0,0 @@ -import 'package:app/app.dart'; -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/point_system/github_model/github_model.dart'; -import 'package:url_launcher/url_launcher.dart'; - -import '../../blocs/point_bloc/point_bloc.dart'; -import '../../blocs/point_bloc/point_event.dart'; -import '../../github_model/repository.dart'; -import '../../views/issues_point/issues_point_page.dart'; -import 'github_repo_panel.dart'; - -class DeskPointPage extends StatefulWidget { - const DeskPointPage({Key? key}) : super(key: key); - - @override - State createState() => _DeskPointPageState(); -} - -class _DeskPointPageState extends State { - - final Repository _repository = Repository.fromJson({ - 'full_name': 'toly1994328/FlutterUnit', - 'license': {"spdx_id": 'GPL-3.0'}, - 'description': - '【Flutter 集录指南 App】The unity of flutter, The unity of coder.', - 'stargazers_count': 6261, - 'forks_count': 1039, - 'subscribers_count': 126, - 'open_issues_count': 40, - }); - - @override - Widget build(BuildContext context) { - return BlocProvider( - create: (_) => PointBloc()..add(EventLoadPoint()), - child: Scaffold( - body: Column( - children: [ - SimpleDeskTopBar( - leading: Text( - 'Flutter 要点集录', - style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), - ), - ), - Divider(height: 1), - Expanded( - child: Row( - children: [ - Column( - children: [ - GithubRepoPanel( - repository: _repository, - ), - Expanded( - child: SizedBox( - width: 250, - child: IssuesTip()) - ) - ], - ), - VerticalDivider(width: 1,), - Expanded(flex: 2, child: IssuesPointContent()), - ], - )) - ], - ), - ), - ); - } -} - -class IssuesTip extends StatelessWidget { - const IssuesTip({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(8.0), - child: Text.rich( - TextSpan(children: [ - TextSpan( - text: '* 注: ', - style: TextStyle( - color: Colors.red, - fontWeight: FontWeight.bold)), - TextSpan( - text: - '要点集录中的 QA 数据收录在 FlutterUnit 以 point 为标签的 issues 中。如果需要提供数据,在 issues 中问答即可。'), - TextSpan( - text: '点击这里跳转', - mouseCursor: SystemMouseCursors.click, - recognizer: TapGestureRecognizer() - ..onTap = _toUrl, - style: TextStyle( - color: Colors.blue, - decoration: TextDecoration.underline, - fontWeight: FontWeight.bold)), - ]), - style: TextStyle(fontSize: 14), - ), - ); - } - - void _toUrl() async{ - String url = 'https://github.com/toly1994328/FlutterUnit/issues?q=label%3Apoint+'; - if (!await launchUrl(Uri.parse(url))) { - throw Exception('Could not launch $url'); - } - } -} - - -class SimpleDeskTopBar extends StatelessWidget { - final Widget? leading; - final Widget? tail; - final double height; - - const SimpleDeskTopBar({super.key, this.leading,this.tail,this.height=64}); - - @override - Widget build(BuildContext context) { - - bool isDark = Theme.of(context).brightness == Brightness.dark; - - return DragToMoveAreaNoDouble( - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 20), - height: height, - color: isDark? Color(0xff2C3036):Colors.white, - child: Row( - children: [ - if (leading != null) leading!, - const Spacer(), - const SizedBox( - width: 20, - ), - if(tail!=null) tail!, - const WindowButtons(), - ], - ), - ), - ); - } -} diff --git a/lib/point_system/views/issues_point/issue_item.dart b/lib/point_system/views/issues_point/issue_item.dart deleted file mode 100644 index fc097c5ef..000000000 --- a/lib/point_system/views/issues_point/issue_item.dart +++ /dev/null @@ -1,95 +0,0 @@ -import 'dart:ui'; - -import 'package:app/app.dart'; -import 'package:components/toly_ui/toly_ui.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_unit/app/utils/convert_man.dart'; -import 'package:flutter_unit/point_system/github_model/github_model.dart'; - - - -/// create by 张风捷特烈 on 2020/9/3 -/// contact me by email 1981462002@qq.com -/// 说明: - -class IssueItem extends StatelessWidget { - final Issue issue; - - const IssueItem({Key? key, required this.issue}) : super(key: key); - - @override - Widget build(BuildContext context) { - bool isDark = Theme.of(context).brightness == Brightness.dark; - - return Container( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), - decoration: BoxDecoration( - border: Border( - bottom: BorderSide( - color: Theme.of(context).dividerColor, - width: 1 / window.devicePixelRatio))), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildTop(), - Padding( - padding: const EdgeInsets.only(top: 5.0, bottom: 5.0, left: 10), - child: Text( - '${issue.title}', - style: TextStyle(fontSize: 15, color: Colors.grey, shadows: [ - Shadow(color: isDark?Colors.black:Colors.white, offset: Offset(1, .5)) - ]), - ), - ), - Row( - children: [ - const Spacer(), - WrapColor( - color: Colors.greenAccent, - child: Text( - issue.commentNum.toString(), - style: const TextStyle(color: Colors.white), - )), - const SizedBox( - width: 5, - ), - const Icon( - TolyIcon.icon_common, - size: 20, - ), - ], - ) - ], - ), - ); - } - - Widget _buildTop() { - return Row( - children: [ - CircleImage( - image: NetworkImage(issue.user?.avatarUrl??''), - size: 40, - borderSize: 2, - ), - const SizedBox( - width: 10, - ), - WrapColor( - child: Text( - "#${issue.number}", - style: const TextStyle(color: Colors.white), - )), - const SizedBox( - width: 10, - ), - Text( - '${issue.user?.login}', - style: const TextStyle(fontWeight: FontWeight.bold), - ), - const Spacer(), - Text(ConvertMan.time2string(issue.createdAt!)), - ], - ); - } -} \ No newline at end of file diff --git a/lib/point_system/views/issues_point/issues_point_page.dart b/lib/point_system/views/issues_point/issues_point_page.dart deleted file mode 100644 index 44b9ad034..000000000 --- a/lib/point_system/views/issues_point/issues_point_page.dart +++ /dev/null @@ -1,163 +0,0 @@ -import 'package:app/app.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_spinkit/flutter_spinkit.dart'; - -import 'package:flutter_unit/point_system/api/issues_api.dart'; -import 'package:flutter_unit/point_system/blocs/point_system_bloc.dart'; -import 'package:flutter_unit/point_system/github_model/github_model.dart'; - -import 'issue_item.dart'; -import 'issues_detail.dart'; -import 'repo_widget.dart'; - -/// create by 张风捷特烈 on 2020/6/17 -/// contact me by email 1981462002@qq.com -/// 说明: - -class IssuesPointScope extends StatelessWidget { - const IssuesPointScope({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return MultiBlocProvider(providers: [ - BlocProvider(create: (_) => PointBloc()..add(EventLoadPoint())), - ], child: const IssuesPointPage(), - ); - } -} - -class IssuesPointPage extends StatelessWidget { - const IssuesPointPage({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return const Scaffold(body: IssuesPointContent()); - } -} - -class IssuesPointContent extends StatefulWidget { - const IssuesPointContent({Key? key}) : super(key: key); - - @override - _IssuesPointContentState createState() => _IssuesPointContentState(); -} - -class _IssuesPointContentState extends State { - Repository? _repository; - - @override - void initState() { - super.initState(); - _loadRepo(); - } - - @override - Widget build(BuildContext context) { - return BlocBuilder( - builder: (_, state) => RefreshIndicator( - onRefresh: _loadIssues, - child: CustomScrollView(slivers: [ - // _buildSliverAppBar(), - buildContentByState(state) - ]), - )); - } - - Widget buildContentByState(PointState state) { - if (state is PointLoading) { - return const SliverPadding( - padding: EdgeInsets.only(top: 150), - sliver: SliverToBoxAdapter( - child: Center( - child: SpinKitCircle( - color: Colors.blue, - ), - )), - ); - } - - if (state is PointLoaded) { - List issues = state.issues; - return SliverList( - delegate: SliverChildBuilderDelegate( - (ctx, int index) => GestureDetector( - onTap: () { - Navigator.of(context).push(SlidePageRoute( - child: BlocProvider( - create: (_) => PointCommentBloc() - ..add(EventLoadPointComment(issues[index])), - child: const IssuesDetailPage()))); - - // Navigator.pushNamed(ctx, UnitRouter. - // ); - }, - child: IssueItem(issue: issues[index])), - childCount: issues.length),); - } - - if (state is PointLoadFailure) { - return SliverPadding( - padding: const EdgeInsets.only(top: 40), - sliver: SliverToBoxAdapter( - child: Center( - child: Text(state.error), - )), - ); - } - - return const SliverPadding( - padding: EdgeInsets.zero, - ); - } - - Widget _buildSliverAppBar() { - return SliverAppBar( - expandedHeight: 210.0, -// leading: _buildLeading(), - title: const Text('Flutter要点集录'), -// actions: _buildActions(), - elevation: 5, - pinned: true, - actions: [ - IconButton( - icon: const Icon( - Icons.help_outline, - color: Colors.white, - ), - onPressed: () { - Navigator.of(context).pushNamed(UnitRouter.bug); - }) - ], - backgroundColor: Colors.blue, - flexibleSpace: FlexibleSpaceBar( - //伸展处布局 - titlePadding: const EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - background: _repository == null - ? const Center( - child: SpinKitFadingCube( - color: Colors.white, - ), - ) - : RepoWidget( - repository: _repository!, - ), - ), - ); - } - - Future _loadIssues() async { - BlocProvider.of(context).add(EventLoadPoint()); - await Future.delayed(const Duration(milliseconds: 200)); - } - - void _loadRepo() async { - final Repository? result = await IssuesApi.getRepoFlutterUnit(); - setState(() { - if (result != null) { - _repository = result; - } - }); - } -} diff --git a/lib/src/flutter_unit.dart b/lib/src/flutter_unit.dart new file mode 100644 index 000000000..0f3b0ee7d --- /dev/null +++ b/lib/src/flutter_unit.dart @@ -0,0 +1,91 @@ +import 'package:app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; + +import 'package:go_router/go_router.dart'; +import 'package:l10n/gen_l10n/app_localizations.dart'; +import 'package:l10n/l10n.dart'; +import 'package:tolyui/app/toly_ui.dart'; +import 'package:widget_module/widget_module.dart'; +import 'package:widget_module/blocs/blocs.dart'; +import 'package:note/note.dart'; +import 'l10n/gen/app_l10n.dart'; +import 'l10n/locale_provider.dart'; +import 'navigation/router/app_route.dart'; + +/// create by 张风捷特烈 on 2020/4/28 +/// contact me by email 1981462002@qq.com +/// 说明: 应用主程序 + +class FlutterUnit3 extends StatefulWidget { + const FlutterUnit3({super.key}); + + @override + State createState() => _FlutterUnit3State(); +} + +class _FlutterUnit3State extends State with LocalProvider { + final GoRouter _router = GoRouter( + initialLocation: AppRoute.splash.url, + routes: [appRoute], + onException: (BuildContext ctx, GoRouterState state, GoRouter router) { + router.go(AppRoute.globalError.url, extra: state.uri.toString()); + }, + ); + + @override + void initState() { + super.initState(); + _initWeb(); + } + + @override + Widget build(BuildContext context) { + AppConfig state = context.watch().state; + ThemeData dark = darkTheme(state); + ThemeData light = lightTheme(state); + return BlocListener( + listenWhen: (p, n) => p.language != n.language, + listener: _onLocaleChange, + child: DefaultTextStyle( + style: TextStyle(fontFamily: state.fontFamily), + child: TolyUiApp.router( + routerConfig: _router, + showPerformanceOverlay: state.showPerformanceOverlay, + title: StrUnit.appName, + debugShowCheckedModeBanner: false, + localizationsDelegates: localizationsDelegates, + supportedLocales: supportedLocales, + locale: state.language.locale, + themeMode: state.themeMode, + darkTheme: dark, + theme: light, + ), + ), + ); + } + + void _initWeb() { + if (!kAppEnv.isWeb) return; + GoRouter.optionURLReflectsImperativeAPIs = true; + context.initWidgetData(); + } + + void _onLocaleChange(BuildContext context, AppConfig state) { + context.read().changeLocale(state.language.locale); + } + + @override + Iterable? get localizationsDelegates => const [ + AppL10n.delegate, + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + FlutterQuillLocalizations.delegate, + ]; + + @override + List get supportedLocales => l10nLocales; +} diff --git a/lib/src/l10n/arb/app_de.arb b/lib/src/l10n/arb/app_de.arb new file mode 100644 index 000000000..915213a3a --- /dev/null +++ b/lib/src/l10n/arb/app_de.arb @@ -0,0 +1,16 @@ +{ + "deskTabWidgets": "Widget-Sammlung", + "deskTabPainter": "Zeichnungssammlung", + "deskTabKnowledge": "Wissensdatenbank", + "deskTabTools": "Werkzeugkasten", + "deskTabMine": "App-Info", + "messageBoard": "Pinnwand", + "mobileTabWidgets": "Widgets", + "mobileTabPainter": "Malen", + "mobileTabKnowledge": "Wissen", + "mobileTabTools": "Werkzeuge", + "mobileTabMine": "Mein", + "newBoard": "Neu", + "news": "Neuigkeiten", + "moreNews": "Mehr News" +} \ No newline at end of file diff --git a/lib/src/l10n/arb/app_en.arb b/lib/src/l10n/arb/app_en.arb new file mode 100644 index 000000000..9a3d0f4f7 --- /dev/null +++ b/lib/src/l10n/arb/app_en.arb @@ -0,0 +1,16 @@ +{ + "deskTabWidgets": "Widgets", + "deskTabPainter": "Painter", + "deskTabKnowledge": "Knowledge", + "deskTabTools": "Treasure", + "deskTabMine": "About", + "messageBoard": "Message Board", + "mobileTabWidgets": "Widgets", + "mobileTabPainter": "Painter", + "mobileTabKnowledge": "Knowledge", + "mobileTabTools": "Treasure", + "mobileTabMine": "Mine", + "newBoard": "New", + "news": "News", + "moreNews": "More News" +} \ No newline at end of file diff --git a/lib/src/l10n/arb/app_es.arb b/lib/src/l10n/arb/app_es.arb new file mode 100644 index 000000000..ede49fdb7 --- /dev/null +++ b/lib/src/l10n/arb/app_es.arb @@ -0,0 +1,16 @@ +{ + "deskTabWidgets": "Colección de widgets", + "deskTabPainter": "Colección de dibujos", + "deskTabKnowledge": "Centro de conocimiento", + "deskTabTools": "Caja de herramientas", + "deskTabMine": "Info App", + "messageBoard": "Tablero de Mensajes", + "mobileTabWidgets": "Widgets", + "mobileTabPainter": "Dibujo", + "mobileTabKnowledge": "Conocimiento", + "mobileTabTools": "Herramientas", + "mobileTabMine": "Mi", + "newBoard": "Nuevo", + "news": "Noticias", + "moreNews": "Más noticias" +} \ No newline at end of file diff --git a/lib/src/l10n/arb/app_fr.arb b/lib/src/l10n/arb/app_fr.arb new file mode 100644 index 000000000..2e271c2d2 --- /dev/null +++ b/lib/src/l10n/arb/app_fr.arb @@ -0,0 +1,16 @@ +{ + "deskTabWidgets": "Collection de widgets", + "deskTabPainter": "Collection de dessins", + "deskTabKnowledge": "Base de connaissances", + "deskTabTools": "Boîte à outils", + "deskTabMine": "Infos App", + "messageBoard": "Tableau de messages", + "mobileTabWidgets": "Widgets", + "mobileTabPainter": "Dessin", + "mobileTabKnowledge": "Connaissances", + "mobileTabTools": "Outils", + "mobileTabMine": "Moi", + "newBoard": "Nouveau", + "news": "Actualités", + "moreNews": "Plus d'actualités" +} \ No newline at end of file diff --git a/lib/src/l10n/arb/app_it.arb b/lib/src/l10n/arb/app_it.arb new file mode 100644 index 000000000..7cf9050cb --- /dev/null +++ b/lib/src/l10n/arb/app_it.arb @@ -0,0 +1,16 @@ +{ + "deskTabWidgets": "Raccolta widget", + "deskTabPainter": "Raccolta disegni", + "deskTabKnowledge": "Hub conoscitivo", + "deskTabTools": "Cassetta degli attrezzi", + "deskTabMine": "Info App", + "messageBoard": "Bacheca", + "mobileTabWidgets": "Widget", + "mobileTabPainter": "Disegno", + "mobileTabKnowledge": "Conoscenza", + "mobileTabTools": "Strumenti", + "mobileTabMine": "Io", + "newBoard": "Nuovo", + "news": "Notizie", + "moreNews": "Altre notizie" +} \ No newline at end of file diff --git a/lib/src/l10n/arb/app_ja.arb b/lib/src/l10n/arb/app_ja.arb new file mode 100644 index 000000000..dcf1cb0c9 --- /dev/null +++ b/lib/src/l10n/arb/app_ja.arb @@ -0,0 +1,16 @@ +{ + "deskTabWidgets": "ウィジェット集", + "deskTabPainter": "描画集", + "deskTabKnowledge": "ナレッジハブ", + "deskTabTools": "ツールボックス", + "deskTabMine": "アプリ情報", + "messageBoard": "掲示板", + "mobileTabWidgets": "ウィジェット", + "mobileTabPainter": "描画", + "mobileTabKnowledge": "知識", + "mobileTabTools": "ツール", + "mobileTabMine": "マイ", + "newBoard": "新規作成", + "news": "最新情報", + "moreNews": "もっと見る" +} \ No newline at end of file diff --git a/lib/src/l10n/arb/app_ko.arb b/lib/src/l10n/arb/app_ko.arb new file mode 100644 index 000000000..a3952eb9d --- /dev/null +++ b/lib/src/l10n/arb/app_ko.arb @@ -0,0 +1,16 @@ +{ + "deskTabWidgets": "위젯 모음집", + "deskTabPainter": "그림 모음집", + "deskTabKnowledge": "지식 허브", + "deskTabTools": "도구 상자", + "deskTabMine": "앱 정보", + "messageBoard": "게시판", + "mobileTabWidgets": "위젯", + "mobileTabPainter": "그리기", + "mobileTabKnowledge": "지식", + "mobileTabTools": "도구", + "mobileTabMine": "내 정보", + "newBoard": "새 글", + "news": "최신 소식", + "moreNews": "더 보기" +} \ No newline at end of file diff --git a/lib/src/l10n/arb/app_pt.arb b/lib/src/l10n/arb/app_pt.arb new file mode 100644 index 000000000..4b877bfa9 --- /dev/null +++ b/lib/src/l10n/arb/app_pt.arb @@ -0,0 +1,16 @@ +{ + "deskTabWidgets": "Coleção de widgets", + "deskTabPainter": "Coleção de desenhos", + "deskTabKnowledge": "Hub de conhecimento", + "deskTabTools": "Caixa de ferramentas", + "deskTabMine": "Info App", + "messageBoard": "Quadro de Mensagens", + "mobileTabWidgets": "Widgets", + "mobileTabPainter": "Desenho", + "mobileTabKnowledge": "Conhecimento", + "mobileTabTools": "Ferramentas", + "mobileTabMine": "Meu", + "newBoard": "Novo", + "news": "Novidades", + "moreNews": "Mais novidades" +} \ No newline at end of file diff --git a/lib/src/l10n/arb/app_ru.arb b/lib/src/l10n/arb/app_ru.arb new file mode 100644 index 000000000..64b61c2ea --- /dev/null +++ b/lib/src/l10n/arb/app_ru.arb @@ -0,0 +1,16 @@ +{ + "deskTabWidgets": "Коллекция виджетов", + "deskTabPainter": "Коллекция рисунков", + "deskTabKnowledge": "База знаний", + "deskTabTools": "Инструменты", + "deskTabMine": "Информация", + "messageBoard": "Доска сообщений", + "mobileTabWidgets": "Виджеты", + "mobileTabPainter": "Рисование", + "mobileTabKnowledge": "Знания", + "mobileTabTools": "Инструменты", + "mobileTabMine": "Мои", + "newBoard": "Создать", + "news": "Новости", + "moreNews": "Больше новостей" +} \ No newline at end of file diff --git a/lib/src/l10n/arb/app_zh.arb b/lib/src/l10n/arb/app_zh.arb new file mode 100644 index 000000000..5b4d2f39a --- /dev/null +++ b/lib/src/l10n/arb/app_zh.arb @@ -0,0 +1,16 @@ +{ + "deskTabWidgets": "组件集录", + "deskTabPainter": "绘制集录", + "deskTabKnowledge": "知识集锦", + "deskTabTools": "工具宝箱", + "deskTabMine": "应用信息", + "messageBoard": "留言板", + "mobileTabWidgets": "组件", + "mobileTabPainter": "绘制", + "mobileTabKnowledge": "知识", + "mobileTabTools": "工具", + "mobileTabMine": "我的", + "newBoard": "新建 ", + "news": "最新资讯", + "moreNews": "查看更多" +} \ No newline at end of file diff --git a/lib/src/l10n/gen/app_l10n.dart b/lib/src/l10n/gen/app_l10n.dart new file mode 100644 index 000000000..39cd6ac38 --- /dev/null +++ b/lib/src/l10n/gen/app_l10n.dart @@ -0,0 +1,257 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:intl/intl.dart' as intl; + +import 'app_l10n_de.dart'; +import 'app_l10n_en.dart'; +import 'app_l10n_es.dart'; +import 'app_l10n_fr.dart'; +import 'app_l10n_it.dart'; +import 'app_l10n_ja.dart'; +import 'app_l10n_ko.dart'; +import 'app_l10n_pt.dart'; +import 'app_l10n_ru.dart'; +import 'app_l10n_zh.dart'; + +// ignore_for_file: type=lint + +/// Callers can lookup localized strings with an instance of AppL10n +/// returned by `AppL10n.of(context)`. +/// +/// Applications need to include `AppL10n.delegate()` in their app's +/// `localizationDelegates` list, and the locales they support in the app's +/// `supportedLocales` list. For example: +/// +/// ```dart +/// import 'gen/app_l10n.dart'; +/// +/// return MaterialApp( +/// localizationsDelegates: AppL10n.localizationsDelegates, +/// supportedLocales: AppL10n.supportedLocales, +/// home: MyApplicationHome(), +/// ); +/// ``` +/// +/// ## Update pubspec.yaml +/// +/// Please make sure to update your pubspec.yaml to include the following +/// packages: +/// +/// ```yaml +/// dependencies: +/// # Internationalization support. +/// flutter_localizations: +/// sdk: flutter +/// intl: any # Use the pinned version from flutter_localizations +/// +/// # Rest of dependencies +/// ``` +/// +/// ## iOS Applications +/// +/// iOS applications define key application metadata, including supported +/// locales, in an Info.plist file that is built into the application bundle. +/// To configure the locales supported by your app, you’ll need to edit this +/// file. +/// +/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file. +/// Then, in the Project Navigator, open the Info.plist file under the Runner +/// project’s Runner folder. +/// +/// Next, select the Information Property List item, select Add Item from the +/// Editor menu, then select Localizations from the pop-up menu. +/// +/// Select and expand the newly-created Localizations item then, for each +/// locale your application supports, add a new item and select the locale +/// you wish to add from the pop-up menu in the Value field. This list should +/// be consistent with the languages listed in the AppL10n.supportedLocales +/// property. +abstract class AppL10n { + AppL10n(String locale) + : localeName = intl.Intl.canonicalizedLocale(locale.toString()); + + final String localeName; + + static AppL10n of(BuildContext context) { + return Localizations.of(context, AppL10n)!; + } + + static const LocalizationsDelegate delegate = _AppL10nDelegate(); + + /// A list of this localizations delegate along with the default localizations + /// delegates. + /// + /// Returns a list of localizations delegates containing this delegate along with + /// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, + /// and GlobalWidgetsLocalizations.delegate. + /// + /// Additional delegates can be added by appending to this list in + /// MaterialApp. This list does not have to be used at all if a custom list + /// of delegates is preferred or required. + static const List> localizationsDelegates = + >[ + delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ]; + + /// A list of this localizations delegate's supported locales. + static const List supportedLocales = [ + Locale('de'), + Locale('en'), + Locale('es'), + Locale('fr'), + Locale('it'), + Locale('ja'), + Locale('ko'), + Locale('pt'), + Locale('ru'), + Locale('zh') + ]; + + /// No description provided for @deskTabWidgets. + /// + /// In zh, this message translates to: + /// **'组件集录'** + String get deskTabWidgets; + + /// No description provided for @deskTabPainter. + /// + /// In zh, this message translates to: + /// **'绘制集录'** + String get deskTabPainter; + + /// No description provided for @deskTabKnowledge. + /// + /// In zh, this message translates to: + /// **'知识集锦'** + String get deskTabKnowledge; + + /// No description provided for @deskTabTools. + /// + /// In zh, this message translates to: + /// **'工具宝箱'** + String get deskTabTools; + + /// No description provided for @deskTabMine. + /// + /// In zh, this message translates to: + /// **'应用信息'** + String get deskTabMine; + + /// No description provided for @messageBoard. + /// + /// In zh, this message translates to: + /// **'留言板'** + String get messageBoard; + + /// No description provided for @mobileTabWidgets. + /// + /// In zh, this message translates to: + /// **'组件'** + String get mobileTabWidgets; + + /// No description provided for @mobileTabPainter. + /// + /// In zh, this message translates to: + /// **'绘制'** + String get mobileTabPainter; + + /// No description provided for @mobileTabKnowledge. + /// + /// In zh, this message translates to: + /// **'知识'** + String get mobileTabKnowledge; + + /// No description provided for @mobileTabTools. + /// + /// In zh, this message translates to: + /// **'工具'** + String get mobileTabTools; + + /// No description provided for @mobileTabMine. + /// + /// In zh, this message translates to: + /// **'我的'** + String get mobileTabMine; + + /// No description provided for @newBoard. + /// + /// In zh, this message translates to: + /// **'新建 '** + String get newBoard; + + /// No description provided for @news. + /// + /// In zh, this message translates to: + /// **'最新资讯'** + String get news; + + /// No description provided for @moreNews. + /// + /// In zh, this message translates to: + /// **'查看更多'** + String get moreNews; +} + +class _AppL10nDelegate extends LocalizationsDelegate { + const _AppL10nDelegate(); + + @override + Future load(Locale locale) { + return SynchronousFuture(lookupAppL10n(locale)); + } + + @override + bool isSupported(Locale locale) => [ + 'de', + 'en', + 'es', + 'fr', + 'it', + 'ja', + 'ko', + 'pt', + 'ru', + 'zh' + ].contains(locale.languageCode); + + @override + bool shouldReload(_AppL10nDelegate old) => false; +} + +AppL10n lookupAppL10n(Locale locale) { + // Lookup logic when only language code is specified. + switch (locale.languageCode) { + case 'de': + return AppL10nDe(); + case 'en': + return AppL10nEn(); + case 'es': + return AppL10nEs(); + case 'fr': + return AppL10nFr(); + case 'it': + return AppL10nIt(); + case 'ja': + return AppL10nJa(); + case 'ko': + return AppL10nKo(); + case 'pt': + return AppL10nPt(); + case 'ru': + return AppL10nRu(); + case 'zh': + return AppL10nZh(); + } + + throw FlutterError( + 'AppL10n.delegate failed to load unsupported locale "$locale". This is likely ' + 'an issue with the localizations generation tool. Please file an issue ' + 'on GitHub with a reproducible sample app and the gen-l10n configuration ' + 'that was used.'); +} diff --git a/lib/src/l10n/gen/app_l10n_de.dart b/lib/src/l10n/gen/app_l10n_de.dart new file mode 100644 index 000000000..f8e2660cb --- /dev/null +++ b/lib/src/l10n/gen/app_l10n_de.dart @@ -0,0 +1,52 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_l10n.dart'; + +// ignore_for_file: type=lint + +/// The translations for German (`de`). +class AppL10nDe extends AppL10n { + AppL10nDe([String locale = 'de']) : super(locale); + + @override + String get deskTabWidgets => 'Widget-Sammlung'; + + @override + String get deskTabPainter => 'Zeichnungssammlung'; + + @override + String get deskTabKnowledge => 'Wissensdatenbank'; + + @override + String get deskTabTools => 'Werkzeugkasten'; + + @override + String get deskTabMine => 'App-Info'; + + @override + String get messageBoard => 'Pinnwand'; + + @override + String get mobileTabWidgets => 'Widgets'; + + @override + String get mobileTabPainter => 'Malen'; + + @override + String get mobileTabKnowledge => 'Wissen'; + + @override + String get mobileTabTools => 'Werkzeuge'; + + @override + String get mobileTabMine => 'Mein'; + + @override + String get newBoard => 'Neu'; + + @override + String get news => 'Neuigkeiten'; + + @override + String get moreNews => 'Mehr News'; +} diff --git a/lib/src/l10n/gen/app_l10n_en.dart b/lib/src/l10n/gen/app_l10n_en.dart new file mode 100644 index 000000000..65194446c --- /dev/null +++ b/lib/src/l10n/gen/app_l10n_en.dart @@ -0,0 +1,52 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_l10n.dart'; + +// ignore_for_file: type=lint + +/// The translations for English (`en`). +class AppL10nEn extends AppL10n { + AppL10nEn([String locale = 'en']) : super(locale); + + @override + String get deskTabWidgets => 'Widgets'; + + @override + String get deskTabPainter => 'Painter'; + + @override + String get deskTabKnowledge => 'Knowledge'; + + @override + String get deskTabTools => 'Treasure'; + + @override + String get deskTabMine => 'About'; + + @override + String get messageBoard => 'Message Board'; + + @override + String get mobileTabWidgets => 'Widgets'; + + @override + String get mobileTabPainter => 'Painter'; + + @override + String get mobileTabKnowledge => 'Knowledge'; + + @override + String get mobileTabTools => 'Treasure'; + + @override + String get mobileTabMine => 'Mine'; + + @override + String get newBoard => 'New'; + + @override + String get news => 'News'; + + @override + String get moreNews => 'More News'; +} diff --git a/lib/src/l10n/gen/app_l10n_es.dart b/lib/src/l10n/gen/app_l10n_es.dart new file mode 100644 index 000000000..121c6f7df --- /dev/null +++ b/lib/src/l10n/gen/app_l10n_es.dart @@ -0,0 +1,52 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_l10n.dart'; + +// ignore_for_file: type=lint + +/// The translations for Spanish Castilian (`es`). +class AppL10nEs extends AppL10n { + AppL10nEs([String locale = 'es']) : super(locale); + + @override + String get deskTabWidgets => 'Colección de widgets'; + + @override + String get deskTabPainter => 'Colección de dibujos'; + + @override + String get deskTabKnowledge => 'Centro de conocimiento'; + + @override + String get deskTabTools => 'Caja de herramientas'; + + @override + String get deskTabMine => 'Info App'; + + @override + String get messageBoard => 'Tablero de Mensajes'; + + @override + String get mobileTabWidgets => 'Widgets'; + + @override + String get mobileTabPainter => 'Dibujo'; + + @override + String get mobileTabKnowledge => 'Conocimiento'; + + @override + String get mobileTabTools => 'Herramientas'; + + @override + String get mobileTabMine => 'Mi'; + + @override + String get newBoard => 'Nuevo'; + + @override + String get news => 'Noticias'; + + @override + String get moreNews => 'Más noticias'; +} diff --git a/lib/src/l10n/gen/app_l10n_fr.dart b/lib/src/l10n/gen/app_l10n_fr.dart new file mode 100644 index 000000000..14d753cae --- /dev/null +++ b/lib/src/l10n/gen/app_l10n_fr.dart @@ -0,0 +1,52 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_l10n.dart'; + +// ignore_for_file: type=lint + +/// The translations for French (`fr`). +class AppL10nFr extends AppL10n { + AppL10nFr([String locale = 'fr']) : super(locale); + + @override + String get deskTabWidgets => 'Collection de widgets'; + + @override + String get deskTabPainter => 'Collection de dessins'; + + @override + String get deskTabKnowledge => 'Base de connaissances'; + + @override + String get deskTabTools => 'Boîte à outils'; + + @override + String get deskTabMine => 'Infos App'; + + @override + String get messageBoard => 'Tableau de messages'; + + @override + String get mobileTabWidgets => 'Widgets'; + + @override + String get mobileTabPainter => 'Dessin'; + + @override + String get mobileTabKnowledge => 'Connaissances'; + + @override + String get mobileTabTools => 'Outils'; + + @override + String get mobileTabMine => 'Moi'; + + @override + String get newBoard => 'Nouveau'; + + @override + String get news => 'Actualités'; + + @override + String get moreNews => 'Plus d\'actualités'; +} diff --git a/lib/src/l10n/gen/app_l10n_it.dart b/lib/src/l10n/gen/app_l10n_it.dart new file mode 100644 index 000000000..20d3e47c8 --- /dev/null +++ b/lib/src/l10n/gen/app_l10n_it.dart @@ -0,0 +1,52 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_l10n.dart'; + +// ignore_for_file: type=lint + +/// The translations for Italian (`it`). +class AppL10nIt extends AppL10n { + AppL10nIt([String locale = 'it']) : super(locale); + + @override + String get deskTabWidgets => 'Raccolta widget'; + + @override + String get deskTabPainter => 'Raccolta disegni'; + + @override + String get deskTabKnowledge => 'Hub conoscitivo'; + + @override + String get deskTabTools => 'Cassetta degli attrezzi'; + + @override + String get deskTabMine => 'Info App'; + + @override + String get messageBoard => 'Bacheca'; + + @override + String get mobileTabWidgets => 'Widget'; + + @override + String get mobileTabPainter => 'Disegno'; + + @override + String get mobileTabKnowledge => 'Conoscenza'; + + @override + String get mobileTabTools => 'Strumenti'; + + @override + String get mobileTabMine => 'Io'; + + @override + String get newBoard => 'Nuovo'; + + @override + String get news => 'Notizie'; + + @override + String get moreNews => 'Altre notizie'; +} diff --git a/lib/src/l10n/gen/app_l10n_ja.dart b/lib/src/l10n/gen/app_l10n_ja.dart new file mode 100644 index 000000000..d55fc3273 --- /dev/null +++ b/lib/src/l10n/gen/app_l10n_ja.dart @@ -0,0 +1,52 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_l10n.dart'; + +// ignore_for_file: type=lint + +/// The translations for Japanese (`ja`). +class AppL10nJa extends AppL10n { + AppL10nJa([String locale = 'ja']) : super(locale); + + @override + String get deskTabWidgets => 'ウィジェット集'; + + @override + String get deskTabPainter => '描画集'; + + @override + String get deskTabKnowledge => 'ナレッジハブ'; + + @override + String get deskTabTools => 'ツールボックス'; + + @override + String get deskTabMine => 'アプリ情報'; + + @override + String get messageBoard => '掲示板'; + + @override + String get mobileTabWidgets => 'ウィジェット'; + + @override + String get mobileTabPainter => '描画'; + + @override + String get mobileTabKnowledge => '知識'; + + @override + String get mobileTabTools => 'ツール'; + + @override + String get mobileTabMine => 'マイ'; + + @override + String get newBoard => '新規作成'; + + @override + String get news => '最新情報'; + + @override + String get moreNews => 'もっと見る'; +} diff --git a/lib/src/l10n/gen/app_l10n_ko.dart b/lib/src/l10n/gen/app_l10n_ko.dart new file mode 100644 index 000000000..920b7a296 --- /dev/null +++ b/lib/src/l10n/gen/app_l10n_ko.dart @@ -0,0 +1,52 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_l10n.dart'; + +// ignore_for_file: type=lint + +/// The translations for Korean (`ko`). +class AppL10nKo extends AppL10n { + AppL10nKo([String locale = 'ko']) : super(locale); + + @override + String get deskTabWidgets => '위젯 모음집'; + + @override + String get deskTabPainter => '그림 모음집'; + + @override + String get deskTabKnowledge => '지식 허브'; + + @override + String get deskTabTools => '도구 상자'; + + @override + String get deskTabMine => '앱 정보'; + + @override + String get messageBoard => '게시판'; + + @override + String get mobileTabWidgets => '위젯'; + + @override + String get mobileTabPainter => '그리기'; + + @override + String get mobileTabKnowledge => '지식'; + + @override + String get mobileTabTools => '도구'; + + @override + String get mobileTabMine => '내 정보'; + + @override + String get newBoard => '새 글'; + + @override + String get news => '최신 소식'; + + @override + String get moreNews => '더 보기'; +} diff --git a/lib/src/l10n/gen/app_l10n_pt.dart b/lib/src/l10n/gen/app_l10n_pt.dart new file mode 100644 index 000000000..ba255d475 --- /dev/null +++ b/lib/src/l10n/gen/app_l10n_pt.dart @@ -0,0 +1,52 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_l10n.dart'; + +// ignore_for_file: type=lint + +/// The translations for Portuguese (`pt`). +class AppL10nPt extends AppL10n { + AppL10nPt([String locale = 'pt']) : super(locale); + + @override + String get deskTabWidgets => 'Coleção de widgets'; + + @override + String get deskTabPainter => 'Coleção de desenhos'; + + @override + String get deskTabKnowledge => 'Hub de conhecimento'; + + @override + String get deskTabTools => 'Caixa de ferramentas'; + + @override + String get deskTabMine => 'Info App'; + + @override + String get messageBoard => 'Quadro de Mensagens'; + + @override + String get mobileTabWidgets => 'Widgets'; + + @override + String get mobileTabPainter => 'Desenho'; + + @override + String get mobileTabKnowledge => 'Conhecimento'; + + @override + String get mobileTabTools => 'Ferramentas'; + + @override + String get mobileTabMine => 'Meu'; + + @override + String get newBoard => 'Novo'; + + @override + String get news => 'Novidades'; + + @override + String get moreNews => 'Mais novidades'; +} diff --git a/lib/src/l10n/gen/app_l10n_ru.dart b/lib/src/l10n/gen/app_l10n_ru.dart new file mode 100644 index 000000000..7f00e6c9e --- /dev/null +++ b/lib/src/l10n/gen/app_l10n_ru.dart @@ -0,0 +1,52 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_l10n.dart'; + +// ignore_for_file: type=lint + +/// The translations for Russian (`ru`). +class AppL10nRu extends AppL10n { + AppL10nRu([String locale = 'ru']) : super(locale); + + @override + String get deskTabWidgets => 'Коллекция виджетов'; + + @override + String get deskTabPainter => 'Коллекция рисунков'; + + @override + String get deskTabKnowledge => 'База знаний'; + + @override + String get deskTabTools => 'Инструменты'; + + @override + String get deskTabMine => 'Информация'; + + @override + String get messageBoard => 'Доска сообщений'; + + @override + String get mobileTabWidgets => 'Виджеты'; + + @override + String get mobileTabPainter => 'Рисование'; + + @override + String get mobileTabKnowledge => 'Знания'; + + @override + String get mobileTabTools => 'Инструменты'; + + @override + String get mobileTabMine => 'Мои'; + + @override + String get newBoard => 'Создать'; + + @override + String get news => 'Новости'; + + @override + String get moreNews => 'Больше новостей'; +} diff --git a/lib/src/l10n/gen/app_l10n_zh.dart b/lib/src/l10n/gen/app_l10n_zh.dart new file mode 100644 index 000000000..2e2086d1b --- /dev/null +++ b/lib/src/l10n/gen/app_l10n_zh.dart @@ -0,0 +1,52 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_l10n.dart'; + +// ignore_for_file: type=lint + +/// The translations for Chinese (`zh`). +class AppL10nZh extends AppL10n { + AppL10nZh([String locale = 'zh']) : super(locale); + + @override + String get deskTabWidgets => '组件集录'; + + @override + String get deskTabPainter => '绘制集录'; + + @override + String get deskTabKnowledge => '知识集锦'; + + @override + String get deskTabTools => '工具宝箱'; + + @override + String get deskTabMine => '应用信息'; + + @override + String get messageBoard => '留言板'; + + @override + String get mobileTabWidgets => '组件'; + + @override + String get mobileTabPainter => '绘制'; + + @override + String get mobileTabKnowledge => '知识'; + + @override + String get mobileTabTools => '工具'; + + @override + String get mobileTabMine => '我的'; + + @override + String get newBoard => '新建 '; + + @override + String get news => '最新资讯'; + + @override + String get moreNews => '查看更多'; +} diff --git a/lib/src/l10n/locale_provider.dart b/lib/src/l10n/locale_provider.dart new file mode 100644 index 000000000..7c631ee7c --- /dev/null +++ b/lib/src/l10n/locale_provider.dart @@ -0,0 +1,7 @@ +import 'package:flutter/material.dart'; + +mixin LocalProvider { + Iterable>? get localizationsDelegates; + + List get supportedLocales; +} diff --git a/lib/src/navigation/model/app_tab.dart b/lib/src/navigation/model/app_tab.dart new file mode 100644 index 000000000..9417b7c3d --- /dev/null +++ b/lib/src/navigation/model/app_tab.dart @@ -0,0 +1,53 @@ +import 'package:app/app.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_unit/src/l10n/gen/app_l10n.dart'; +import 'package:tolyui/tolyui.dart'; + +enum AppTab { + widgets('/widget', TolyIcon.icon_layout), + note('/note', Icons.note_alt_outlined), + knowledge('/knowledge', TolyIcon.icon_artifact), + painter('/painter', TolyIcon.dingzhi1), + tools('/tools', TolyIcon.icon_fast), + mine('/account', TolyIcon.yonghu); + + final IconData icon; + final String path; + + static List get mobileTabs => [ + widgets, + painter, + knowledge, + note, + mine + ]; + + const AppTab(this.path, this.icon); + + String label(AppL10n l10n) { + if (kAppEnv.isDesktopUI) { + return switch (this) { + AppTab.widgets => l10n.deskTabWidgets, + AppTab.painter => l10n.deskTabPainter, + AppTab.knowledge => l10n.deskTabKnowledge, + AppTab.tools => l10n.deskTabTools, + AppTab.mine => l10n.deskTabMine, + AppTab.note => l10n.messageBoard, + }; + } + return switch (this) { + AppTab.widgets => l10n.mobileTabWidgets, + AppTab.painter => l10n.mobileTabPainter, + AppTab.knowledge => l10n.mobileTabKnowledge, + AppTab.tools => l10n.mobileTabTools, + AppTab.mine => l10n.mobileTabMine, + AppTab.note => l10n.messageBoard, + }; + } + + IconMenu menu(AppL10n l10n) => IconMenu(icon, + label: label(l10n), + route: path, + ); +} diff --git a/lib/src/navigation/router/app_route.dart b/lib/src/navigation/router/app_route.dart new file mode 100644 index 000000000..8c42ff213 --- /dev/null +++ b/lib/src/navigation/router/app_route.dart @@ -0,0 +1,40 @@ +import 'package:app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_unit/src/l10n/gen/app_l10n.dart'; +import '../view/desktop/flutter_unit_desk_navigation.dart'; + +import 'system/app.dart'; +import 'system/global.dart'; +import 'system/settings.dart'; +import 'widgets/collection_route.dart'; +import 'widgets/widgets_route.dart'; +import 'package:note/note.dart'; + +RouteBase get appRoute { + List body = [ + widgetsRoute, + noteRoute, + collectRoute, + settingsRoute, + GoRoute( + path: AppRoute.moreNews.path, + builder: (ctx, __) => NewsPage( + title: AppL10n.of(ctx).news, + ), + ), + ...systemRoutes, + ]; + return GoRoute( + path: AppRoute.home.path, + redirect: (_, __) => null, + routes: [ + ...globalRoutes, + if (kAppEnv.isDesktopUI) + ShellRoute( + builder: (_, __, Widget child) => AppDeskNavigation(content: child), + routes: body, + ), + if (!kAppEnv.isDesktopUI) ...body, + ], + ); +} diff --git a/lib/src/navigation/router/system/app.dart b/lib/src/navigation/router/system/app.dart new file mode 100644 index 000000000..79ab782ef --- /dev/null +++ b/lib/src/navigation/router/system/app.dart @@ -0,0 +1,44 @@ +import 'package:app/app.dart'; +import 'package:artifact/artifact.dart'; +import 'package:authentication/authentication.dart'; +import 'package:draw_system/draw_system.dart'; +import 'package:treasure_tools/treasure_tools.dart'; + +List get systemRoutes => [ + GoRoute( + path: AppRoute.dataManage.path, + builder: (_, __) => const DataManagePage(), + ), + GoRoute( + path: AppRoute.account.path, + builder: (_, __) => const DeskAccountPage(), + ), + GoRoute( + path: AppRoute.aboutApp.path, + builder: (_, __) => const AboutAppPage(), + ), + GoRoute( + path: AppRoute.aboutMe.path, + builder: (_, __) => const AboutMePage(), + ), + GoRoute( + path: AppRoute.supportMe.path, + builder: (_, __) => const SupportMe(), + ), + if (kAppEnv.isDesktopUI) ...deskTopRoutes + ]; + +List get deskTopRoutes => [ + GoRoute( + path: AppRoute.knowledge.path, + builder: (_, __) => const DeskKnowledgePage(), + ), + GoRoute( + path: AppRoute.painter.path, + builder: (_, __) => const GalleryUnit(), + ), + GoRoute( + path: AppRoute.tools.path, + builder: (_, __) => const CodeGenPage(), + ), + ]; diff --git a/lib/src/navigation/router/system/global.dart b/lib/src/navigation/router/system/global.dart new file mode 100644 index 000000000..d7c1c0f37 --- /dev/null +++ b/lib/src/navigation/router/system/global.dart @@ -0,0 +1,17 @@ +import 'package:app/app.dart'; +import '../../../starter/fx_application.dart'; + +List get globalRoutes => [ + GoRoute( + path: AppRoute.splash.path, + builder: (_, __) => const FlutterUnitSplash(), + ), + GoRoute( + path: AppRoute.startError.path, + builder: (_, GoRouterState state) => AppStartErrorPage(error: state.extra), + ), + GoRoute( + path: AppRoute.globalError.path, + builder: (_, GoRouterState state) => AppStartErrorPage(error: state.extra), + ), + ]; diff --git a/lib/src/navigation/router/system/settings.dart b/lib/src/navigation/router/system/settings.dart new file mode 100644 index 000000000..820cd1dd0 --- /dev/null +++ b/lib/src/navigation/router/system/settings.dart @@ -0,0 +1,28 @@ +import 'package:app/app.dart'; + +GoRoute get settingsRoute => GoRoute( + path: AppRoute.settings.path, + builder: (_, __) => const SettingPage(), + routes: [ + GoRoute( + path: AppRoute.darkModel.path, + builder: (_, __) => const ThemeModelSetting(), + ), + GoRoute( + path: AppRoute.codeStyle.path, + builder: (_, __) => const CodeStyleSettingPage(), + ), + GoRoute( + path: AppRoute.themeColor.path, + builder: (_, __) => const ThemeColorSettingPage(), + ), + GoRoute( + path: AppRoute.fontSetting.path, + builder: (_, __) => const FontSettingPage(), + ), + GoRoute( + path: AppRoute.version.path, + builder: (_, __) => const VersionInfo(), + ), + ], + ); diff --git a/lib/src/navigation/router/widgets/collection_route.dart b/lib/src/navigation/router/widgets/collection_route.dart new file mode 100644 index 000000000..9b34deecb --- /dev/null +++ b/lib/src/navigation/router/widgets/collection_route.dart @@ -0,0 +1,29 @@ +import 'package:app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:widget_module/widget_module.dart'; +import 'package:note/note.dart'; + +GoRoute get collectRoute => GoRoute( + path: AppRoute.collection.path, + builder: (_, __) => const CollectPageAdapter(), + routes: [ + GoRoute(path: AppRoute.collectionDetail.path, builder: collectionDetailBuilder), + ], +); + +GoRoute get noteRoute => GoRoute( + path: AppRoute.note.path, + builder: (_, __) => ArtSysScope(child: const ArticleAdmin()), + // routes: [ + // GoRoute(path: AppRoute.collectionDetail.path, builder: collectionDetailBuilder), + // ], +); + +Widget collectionDetailBuilder(BuildContext context, GoRouterState state) { + Object? extra = state.extra; + CategoryModel? model; + if (extra is CategoryModel) { + model = extra; + } + return CategoryShow(model: model!); +} diff --git a/lib/src/navigation/router/widgets/widgets_route.dart b/lib/src/navigation/router/widgets/widgets_route.dart new file mode 100644 index 000000000..125fec83f --- /dev/null +++ b/lib/src/navigation/router/widgets/widgets_route.dart @@ -0,0 +1,39 @@ +import 'package:app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:widget_module/widget_module.dart'; + +import '../../view/mobile/news.dart'; +import '../../view/mobile/unit_navigation.dart'; + +GoRoute get widgetsRoute => GoRoute( + path: AppRoute.widget.path, + builder: (_, __) { + if (kAppEnv.isDesktopUI) { + return const DeskWidgetPanel( + header: NewsHeader(), + ); + } + return const UnitPhoneNavigation(); + }, + routes: [ + GoRoute(path: AppRoute.widgetDetail.path, builder: widgetDetailBuilder), + ], + ); + +Widget widgetDetailBuilder(BuildContext context, GoRouterState state) { + Object? extra = state.extra; + String? widgetName = state.pathParameters['name']; + + WidgetModel? model; + if (extra is WidgetModel) { + model = extra; + } + if (kAppEnv.isDesktopUI) { + return DeskWidgetDetailPageScope( + model: model, + widgetName: widgetName, + ); + } + assert(model != null); + return WidgetDetailPageScope(model: model!); +} diff --git a/lib/src/navigation/view/app_bloc_provider.dart b/lib/src/navigation/view/app_bloc_provider.dart new file mode 100644 index 000000000..9c339a59d --- /dev/null +++ b/lib/src/navigation/view/app_bloc_provider.dart @@ -0,0 +1,50 @@ +import 'package:app/app.dart'; +import 'package:note/note.dart'; +import 'package:app_update/app_update.dart'; +import 'package:authentication/authentication.dart'; +import 'package:draw_system/draw_system.dart'; +import 'package:storage/storage.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:widget_module/widget_module.dart'; + +/// create by 张风捷特烈 on 2020/4/28 +/// contact me by email 1981462002@qq.com +/// 说明: Bloc提供器包裹层 + +class AppBlocProvider extends StatefulWidget { + final Widget child; + + const AppBlocProvider({Key? key, required this.child}) : super(key: key); + + @override + State createState() => _AppBlocProviderState(); +} + +class _AppBlocProviderState extends State { + @override + Widget build(BuildContext context) { + return MultiBlocProvider( + providers: [ + // 全局 bloc : 维护应用存储状态、更新、认证 + BlocProvider( + create: (_) => AuthBloc(repository: HttpAuthRepository())), + BlocProvider(create: (_) => AppConfigBloc()), + BlocProvider( + create: (_) => UpgradeBloc(api: UnitUpgradeApi())), + BlocProvider(create: (_) => UserBloc()), + BlocProvider(create: (_) => NewsBloc()..initByCache()), + + BlocProvider( + create: (_) => GalleryUnitBloc()..loadGalleryInfo()), + ], + child: WidgetsBlocProvider(child: widget.child), + ); + } + + @override + void dispose() { + AppStorage().close(); + super.dispose(); + } +} diff --git a/lib/src/navigation/view/desktop/flutter_unit_desk_navigation.dart b/lib/src/navigation/view/desktop/flutter_unit_desk_navigation.dart new file mode 100644 index 000000000..29a55ee68 --- /dev/null +++ b/lib/src/navigation/view/desktop/flutter_unit_desk_navigation.dart @@ -0,0 +1,75 @@ +import 'package:app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_unit/src/l10n/gen/app_l10n.dart'; +import 'package:flutter_unit/src/navigation/model/app_tab.dart'; +import 'package:flutter_unit/src/navigation/view/desktop/unit_shortcuts_scope.dart'; +import 'package:go_router/go_router.dart'; +import 'package:tolyui_navigation/tolyui_navigation.dart'; +import 'menu_bar_leading.dart'; +import 'menu_bar_tail.dart'; +import 'toly_unit_menu_cell.dart'; +class AppDeskNavigation extends StatelessWidget { + final Widget content; + + const AppDeskNavigation({super.key, required this.content}); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: UnitShortcutsScope( + child: Row( + children: [ + const DragToMoveWrapper(child: DeskNavigationRail()), + Expanded(child: content), + ], + ), + ), + ); + } +} + +class DeskNavigationRail extends StatefulWidget { + const DeskNavigationRail({super.key}); + + @override + State createState() => _DeskNavigationRailState(); +} + +class _DeskNavigationRailState extends State { + + @override + Widget build(BuildContext context) { + return TolyRailMenuBar( + cellBuilder: FlutterUnitMenuCell.create, + width: 140, + gap: 8, + padding: EdgeInsets.zero, + backgroundColor: const Color(0xff2C3036), + menus: deskNavBarMenus, + activeId: activePath, + enableWidthChange: false, + onSelected: context.go, + tail: (_) => const MenuBarTail(), + leading: (_) => const MenuBarLeading(), + ); + } + + late List deskNavBarMenus; + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + AppL10n l10n = AppL10n.of(context); + deskNavBarMenus = AppTab.values.map((e)=>e.menu(l10n)).toList(); + } + + final RegExp _segReg = RegExp(r'/\w+'); + + String? get activePath { + final String path = GoRouterState.of(context).uri.toString(); + RegExpMatch? match = _segReg.firstMatch(path); + if (match == null) return null; + String? target = match.group(0); + return target; + } +} diff --git a/lib/src/navigation/view/desktop/locale_change_menu.dart b/lib/src/navigation/view/desktop/locale_change_menu.dart new file mode 100644 index 000000000..54b1cf72e --- /dev/null +++ b/lib/src/navigation/view/desktop/locale_change_menu.dart @@ -0,0 +1,58 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:l10n/l10n.dart'; +import 'package:tolyui/tolyui.dart'; +import 'package:app/app.dart'; + +class LocaleChangeMenu extends StatelessWidget { + const LocaleChangeMenu({super.key}); + + @override + Widget build(BuildContext context) { + List labels = Language.values.map((e) => e.label).toList(); + + DropMenuCellStyle lightStyle = const DropMenuCellStyle( + padding: EdgeInsets.symmetric(horizontal: 4, vertical: 0), + borderRadius: BorderRadius.all(Radius.circular(6)), + foregroundColor: Color(0xff1f1f1f), + backgroundColor: Colors.transparent, + disableColor: Color(0xffbfbfbf), + hoverBackgroundColor: Color(0xfff5f5f5), + hoverForegroundColor: Color(0xff1f1f1f), + textStyle: TextStyle(fontFamily: '微软雅黑', fontSize: 12,)); + Language language = + context.select((AppConfigBloc bloc) => bloc.state.language); + int index = Language.values.indexOf(language); + return Stack( + alignment: Alignment.centerLeft, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 6.0), + child: Icon(Icons.translate,color: Colors.white,size: 14), + ), + IconTheme( + data: const IconThemeData(color: Colors.white), + child: DefaultTextStyle( + style: const TextStyle(color: Colors.white), + child: TolySelect( + fontSize: 12, + cellStyle: lightStyle, + data: labels, + selectIndex: index, + iconSize: 16, + height: 26, + width: 100, + minWidth: 100, + maxHeight: 180, + padding: const EdgeInsets.only(right: 6,left: 24), + onSelected: (int index) async { + Language type = Language.values[index]; + context.read().switchLanguage(type); + }, + ), + ), + ), + ], + ); + } +} diff --git a/lib/src/navigation/view/desktop/menu_bar_leading.dart b/lib/src/navigation/view/desktop/menu_bar_leading.dart new file mode 100644 index 000000000..c3d097b0b --- /dev/null +++ b/lib/src/navigation/view/desktop/menu_bar_leading.dart @@ -0,0 +1,88 @@ +// Copyright 2014 The 张风捷特烈 . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Author: 张风捷特烈 +// CreateTime: 2024-05-13 +// Contact Me: 1981462002@qq.com + +import 'package:app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:toly_ui/toly_ui.dart'; +import 'package:tolyui/tolyui.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class MenuBarLeading extends StatelessWidget { + const MenuBarLeading({super.key}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(top: 20, bottom: 8), + child: Column( + children: [ + Wrap( + direction: Axis.vertical, + spacing: 8, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + GestureDetector( + onDoubleTap: () { + sendEvent(1); + }, + child: const CircleImage( + image: AssetImage('assets/images/icon_head.webp'), + size: 60, + ), + ), + const Text( + '张风捷特烈', + style: TextStyle(color: Colors.white70), + ) + ], + ), + _buildIcons(), + const Divider(color: Colors.white, height: 1, endIndent: 20), + const SizedBox(height: 16), + ], + ), + ); + } + + final List menus = const [ + LinkIconMenu( + TolyIcon.icon_github, "https://github.com/toly1994328/FlutterUnit"), + LinkIconMenu(TolyIcon.icon_juejin, + 'https://juejin.im/user/5b42c0656fb9a04fe727eb37'), + LinkIconMenu(TolyIcon.icon_item, 'http://toly1994.com'), + ]; + + Widget _buildIcons() { + return Padding( + padding: const EdgeInsets.only(bottom: 8, top: 8), + child: Wrap( + spacing: 8, + children: menus + .map((menu) => TolyAction( + style: const ActionStyle.dark(), + onTap: menu.launch, + child: Icon(menu.icon, color: Colors.white, size: 22), + )) + .toList(), + ), + ); + } +} + +class LinkIconMenu { + final IconData icon; + final String url; + + const LinkIconMenu(this.icon, this.url); + + void launch() => _launchUrl(url); + + void _launchUrl(String url) async { + if (!await launchUrl(Uri.parse(url))) {} + } +} diff --git a/lib/src/navigation/view/desktop/menu_bar_tail.dart b/lib/src/navigation/view/desktop/menu_bar_tail.dart new file mode 100644 index 000000000..17b33129c --- /dev/null +++ b/lib/src/navigation/view/desktop/menu_bar_tail.dart @@ -0,0 +1,82 @@ +// Copyright 2014 The 张风捷特烈 . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Author: 张风捷特烈 +// CreateTime: 2024-05-13 +// Contact Me: 1981462002@qq.com + +import 'package:app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_unit/src/flutter_unit.dart'; +import 'package:go_router/go_router.dart'; +import 'package:toly_ui/toly_ui.dart'; +import 'package:tolyui/basic/basic.dart'; + +import 'locale_change_menu.dart'; +import 'theme_model_switch_icon.dart'; +import 'package:app_update/app_update.dart'; + +enum ActionType { + settings(path: '/settings'), + collection(path: '/collection'); + + final String path; + + const ActionType({required this.path}); +} + +class MenuBarTail extends StatelessWidget { + const MenuBarTail({super.key}); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + const Divider(indent: 20, color: Colors.white, height: 1), + const SizedBox(height: 8,), + const LocaleChangeMenu(), + Padding( + padding: const EdgeInsets.only(left: 8, right: 8,bottom: 8,top: 2), + child: Wrap( + crossAxisAlignment: WrapCrossAlignment.center, + spacing: 8, + children: [ + const SettingIcon(), + TolyAction( + style: const ActionStyle.dark(), + onTap: () => context.push(ActionType.collection.path), + child: const Icon( + TolyIcon.icon_collect, + color: Colors.white, + size: 22, + ), + ), + const ThemeModelSwitchIcon(), + ], + ), + ), + ], + ); + } +} + +class SettingIcon extends StatelessWidget { + const SettingIcon({super.key}); + + @override + Widget build(BuildContext context) { + UpdateState state = context.watch().state; + Color tipColor = Colors.redAccent; + Widget child = TolyAction( + style: const ActionStyle.dark(), + onTap: () => context.push(ActionType.settings.path), + child: const Icon(Icons.settings, color: Colors.white, size: 22), + ); + return switch (state) { + ShouldUpdateState() => Badge(backgroundColor: tipColor, child: child), + _ => child, + }; + } +} diff --git a/lib/src/navigation/view/desktop/theme_model_switch_icon.dart b/lib/src/navigation/view/desktop/theme_model_switch_icon.dart new file mode 100644 index 000000000..3aae39e60 --- /dev/null +++ b/lib/src/navigation/view/desktop/theme_model_switch_icon.dart @@ -0,0 +1,28 @@ +import 'package:app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:tolyui/tolyui.dart'; + +class ThemeModelSwitchIcon extends StatelessWidget { + + const ThemeModelSwitchIcon({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + bool isDark = Theme.of(context).brightness == Brightness.dark; + return MouseRegion( + cursor: SystemMouseCursors.click, + child: TolyAction( + style: const ActionStyle.dark(), + onTap: (){ + context.read().changeThemeMode(isDark?ThemeMode.light:ThemeMode.dark); + }, + child: Icon( + !isDark?TolyIcon.dark:TolyIcon.wb_sunny, + color: Colors.white, + size: 22, + ), + ), + ); + } +} diff --git a/lib/src/navigation/view/desktop/toly_unit_menu_cell.dart b/lib/src/navigation/view/desktop/toly_unit_menu_cell.dart new file mode 100644 index 000000000..8c17403ea --- /dev/null +++ b/lib/src/navigation/view/desktop/toly_unit_menu_cell.dart @@ -0,0 +1,86 @@ +// Copyright 2014 The 张风捷特烈 . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Author: 张风捷特烈 +// CreateTime: 2024-05-13 +// Contact Me: 1981462002@qq.com + +import 'package:flutter/material.dart'; +import 'package:tolyui/tolyui.dart'; +import 'package:tolyui_navigation/tolyui_navigation.dart'; + +final Tween _widthTween = Tween(begin: 0.82, end: 0.95); +final Tween _sizeTween = Tween(begin: 18.0, end: 22.0); +final Tween _fontSizeTween = Tween(begin: 14.0, end: 15); + +class FlutterUnitMenuCell extends StatelessWidget { + final MenuMeta menu; + final bool enableTooltip; + final DisplayMeta display; + + const FlutterUnitMenuCell.create(this.menu, this.display, + {super.key, this.enableTooltip = false}); + + Color? get foregroundColor => + display.selected ? Colors.white : Colors.white70; + + @override + Widget build(BuildContext context) { + double height = 42; + + double anim = display.rate; + Color? color = ColorTween( + begin: Colors.white.withAlpha(33), + end: Theme.of(context).primaryColor) + .transform(anim); + + double iconSize = _sizeTween.transform(anim); + double fontSize = _fontSizeTween.transform(anim); + IconData? icon; + if (menu is IconMenu) { + icon = (menu as IconMenu).icon; + } + TextStyle style = TextStyle(color: foregroundColor, fontSize: fontSize); + Radius radius = Radius.circular(height / 2); + BorderRadius br = BorderRadius.only(topRight: radius, bottomRight: radius); + Widget child = Container( + padding: EdgeInsets.only(left: 12), + alignment: Alignment.centerLeft, + decoration: BoxDecoration(color: color, borderRadius: br), + width: _widthTween.transform(anim) * 140, + height: height, + child: Row( + spacing: 6, + // crossAxisAlignment: WrapCrossAlignment.center, + children: [ + Icon(icon, color: foregroundColor, size: iconSize), + Expanded( + child: Text( + menu.label, + style: style, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + const SizedBox( + width: 2, + ) + ], + ), + ); + + if (enableTooltip) { + child = TolyTooltip( + placement: Placement.right, + message: menu.label, + child: child, + ); + } + + return Align( + alignment: Alignment.centerLeft, + child: child, + ); + } +} diff --git a/lib/src/navigation/view/desktop/unit_shortcuts_scope.dart b/lib/src/navigation/view/desktop/unit_shortcuts_scope.dart new file mode 100644 index 000000000..96818beaf --- /dev/null +++ b/lib/src/navigation/view/desktop/unit_shortcuts_scope.dart @@ -0,0 +1,48 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:fx_trace/fx_trace.dart'; +import 'package:go_router/go_router.dart'; +import 'package:widget_module/widget_module.dart'; + +class GlobalFind extends Intent { + const GlobalFind(); +} + +class UnitShortcutsScope extends StatefulWidget { + final Widget child; + + const UnitShortcutsScope({super.key, required this.child}); + + @override + State createState() => _UnitShortcutsScopeState(); +} + +class _UnitShortcutsScopeState extends State + with FxEmitterMixin { + @override + Widget build(BuildContext context) { + return Shortcuts( + shortcuts: { + LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyF): const GlobalFind(), + }, + child: Actions( + actions: >{ + GlobalFind: CallbackAction(onInvoke: _onGlobalSearch), + }, + child: widget.child, + ), + ); + } + + @override + void onEvent(FxEvent event) { + if (event is SelectWidgetEvent) { + context.push('/widget/detail/${event.name}', extra: event.model); + } + } + + Object? _onGlobalSearch(GlobalFind intent) { + showDialog(context: context, builder: (_) => const GlobalFindDialog()); + return null; + } +} diff --git a/lib/src/navigation/view/mobile/carousel.dart b/lib/src/navigation/view/mobile/carousel.dart new file mode 100644 index 000000000..6802defa5 --- /dev/null +++ b/lib/src/navigation/view/mobile/carousel.dart @@ -0,0 +1,236 @@ +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:app/app.dart'; +import 'news.dart'; + +typedef TypeWidgetBuilder = Widget Function(BuildContext context, T data); + +class Carousel extends StatefulWidget { + final List data; + final TypeWidgetBuilder itemBuilder; + + const Carousel({ + super.key, + required this.data, + required this.itemBuilder, + }); + + @override + State createState() => _CarouselState(); +} + +class _CarouselState extends State> { + final ValueNotifier factor = ValueNotifier(0); + + late PageController _ctrl; + + final int _firstOffset = 1000; //初始偏移 + int _position = 0; //页面位置 + + @override + void initState() { + super.initState(); + _position = _position + _firstOffset; + + double value = ((_position - _firstOffset + 1) % 5) / 5; + factor.value = value == 0 ? 1 : value; + _ctrl = PageController( + viewportFraction: kAppEnv.isDesktopUI ? 0.38 : 0.8, + initialPage: _position, + )..addListener(() { + if (_ctrl.page != null) { + double value = (_ctrl.page! - _firstOffset + 1) % 5 / 5; + factor.value = value == 0 ? 1 : value; + } + }); + } + + @override + void dispose() { + _ctrl.dispose(); + factor.dispose(); + super.dispose(); + } + + Color get color => Colors.blue; + + Color get nextColor => Colors.orangeAccent; + + bool get isDark => Theme.of(context).brightness == Brightness.dark; + + @override + Widget build(BuildContext context) { + List data = widget.data; + if (data.isEmpty) return const SizedBox(); + Widget child = PageView.builder( + controller: _ctrl, // itemCount: 7, + itemBuilder: (_, index) { + int realIndex = _fixPosition(index, _firstOffset, data.length); + return GestureDetector( + child: AnimatedBuilder( + animation: _ctrl, + builder: (context, child) => _buildAnimItemByIndex( + context, + child, + index, + ), + child: widget.itemBuilder(context, data[realIndex]), + ), + ); + }, + onPageChanged: (index) { + _position = index; + setState(() {}); + }, + ); + + int realIndex = _fixPosition(_position, _firstOffset, data.length); + + child = Stack( + alignment: Alignment.bottomCenter, + children: [ + child, + Padding( + padding: const EdgeInsets.only(bottom: 16.0), + child: Wrap( + spacing: 6, + children: widget.data.asMap().keys.map((int index) { + return GestureDetector( + onTap: () { + int deta = index - realIndex; + _position += deta; + print('$_position,$realIndex'); + _ctrl.animateToPage(_position, + duration: Duration(milliseconds: 500), + curve: Curves.easeIn); + }, + child: Container( + width: 8, + height: 8, + decoration: BoxDecoration( + color: realIndex == index + ? Colors.white + : Colors.black.withValues(alpha: 0.4), + shape: BoxShape.circle, + boxShadow: [ + BoxShadow( + color: Colors.white.withValues(alpha: 0.3), + spreadRadius: 1, + blurRadius: 10, + blurStyle: BlurStyle.outer) + ]), + ), + ); + }).toList(), + ), + ) + ], + ); + + if (!kIsDesk) { + return child; + } + + return Stack( + alignment: Alignment.center, + children: [ + child, + Positioned( + right: 0, + child: HoverIndicator( + onTap: () { + _position += 1; + _ctrl.animateToPage(_position, + duration: Duration(milliseconds: 500), + curve: Curves.easeIn); + }, + icon: Icons.navigate_next_outlined, + )), + Positioned( + left: 0, + child: HoverIndicator( + onTap: () { + _position -= 1; + _ctrl.animateToPage(_position, + duration: Duration(milliseconds: 500), + curve: Curves.easeIn); + }, + icon: Icons.navigate_before)), + ], + ); + } + + Widget _buildAnimItemByIndex(BuildContext context, Widget? child, int index) { + double value; + if (_ctrl.position.haveDimensions && _ctrl.page != null) { + value = _ctrl.page! - index; + } else { + value = (_position - index).toDouble(); + } + value = (1 - ((value.abs()) * 0.2)).clamp(0, 1).toDouble(); + value = Curves.easeOut.transform(value); + + return Transform( + transform: Matrix4.diagonal3Values(value, value, 1.0), + alignment: Alignment.center, + child: child, + ); + } + + int _fixPosition(int realPos, int initPos, int length) { + if (length == 0) return 0; + final int offset = realPos - initPos; + int result = offset % length; + return result < 0 ? length + result : result; + } +} + +class HoverIndicator extends StatefulWidget { + final IconData icon; + final VoidCallback onTap; + + const HoverIndicator({super.key, required this.icon, required this.onTap}); + + @override + State createState() => _HoverIndicatorState(); +} + +class _HoverIndicatorState extends State { + @override + Widget build(BuildContext context) { + return MouseRegion( + onEnter: _onEnter, + onExit: _onExit, + cursor: SystemMouseCursors.click, + child: GestureDetector( + onTap: widget.onTap, + child: Container( + width: 36, + height: 260, + child: !_hover + ? null + : Icon( + widget.icon, + color: Colors.white, + ), + color: _hover + ? Colors.blue.withValues(alpha: 0.2) + : Colors.transparent, + ), + )); + } + + bool _hover = false; + + void _onEnter(PointerEnterEvent event) { + setState(() { + _hover = true; + }); + } + + void _onExit(PointerExitEvent event) { + setState(() { + _hover = false; + }); + } +} diff --git a/lib/src/navigation/view/mobile/news.dart b/lib/src/navigation/view/mobile/news.dart new file mode 100644 index 000000000..c7086cc40 --- /dev/null +++ b/lib/src/navigation/view/mobile/news.dart @@ -0,0 +1,148 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:note/note.dart'; +import 'package:tolyui/basic/basic.dart'; +import 'package:url_launcher/url_launcher.dart'; +import '../../../l10n/gen/app_l10n.dart'; +import 'carousel.dart'; +import 'package:app/app.dart'; + +class NewsHeader extends StatefulWidget { + const NewsHeader({super.key}); + + @override + State createState() => _NewsHeaderState(); +} + +class _NewsHeaderState extends State { + @override + Widget build(BuildContext context) { + AppL10n l10n = AppL10n.of(context); + + List data = + context.select((NewsBloc bloc) => bloc.state.headerNews); + return ConstrainedBox( + constraints: const BoxConstraints(maxHeight: 64), + child: Column( + spacing: 2, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 4), + child: Row( + children: [ + Text( + l10n.news, + style: TextStyle(fontWeight: FontWeight.w600), + ), + if (kAppEnv.isDesktopUI) + TolyAction( + style: ActionStyle(padding: EdgeInsets.all(2)), + child: Icon( + Icons.refresh, + size: 16, + ), + onTap: () { + context.read().refreshFromNet(); + }), + Spacer(), + MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + onTap: () { + context.push('/more_news'); + }, + child: Text( + l10n.moreNews, + style: TextStyle(fontSize: 12, color: Color(0xff999999)), + ), + ), + ) + ], + ), + ), + Expanded( + child: Carousel( + data: data, + itemBuilder: (BuildContext context, ArticlePo data) { + return NewsArticleDisplay(article: data); + }, + ), + ), + ], + ), + ); + } +} + +void launch(String url) => _launchUrl(url); + +void _launchUrl(String url) async { + if (!await launchUrl(Uri.parse(url))) {} +} + +class NewsArticleDisplay extends StatelessWidget { + const NewsArticleDisplay({ + super.key, + required this.article, + }); + + final ArticlePo article; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + _launchUrl(article.url); + }, + child: Container( + // margin: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 4), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + image: DecorationImage( + image: NetworkImage(article.cover ?? ''), fit: BoxFit.cover)), + padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Row( + children: [ + Container( + width: 16, + height: 16, + margin: EdgeInsets.only(right: 6), + alignment: Alignment.center, + decoration: BoxDecoration( + color: Colors.red, + borderRadius: BorderRadius.circular(4)), + child: Text( + '新', + style: + TextStyle(fontSize: 8, color: Colors.white, height: 1), + ), + ), + Flexible( + child: Text( + article.title, + style: TextStyle( + color: Colors.white, + shadows: [ + Shadow( + color: Colors.black, + offset: Offset(.5, .5), + blurRadius: 4) + ], + fontSize: 14, + fontWeight: FontWeight.bold), + softWrap: false, + ), + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/lib/src/navigation/view/mobile/pure_bottom_bar.dart b/lib/src/navigation/view/mobile/pure_bottom_bar.dart new file mode 100644 index 000000000..685b45cf5 --- /dev/null +++ b/lib/src/navigation/view/mobile/pure_bottom_bar.dart @@ -0,0 +1,49 @@ +import 'package:flutter/material.dart'; + +import '../../../l10n/gen/app_l10n.dart'; +import '../../model/app_tab.dart'; + +class PureBottomBar extends StatelessWidget { + final ValueChanged? onTap; + final AppTab activeTab; + + + const PureBottomBar({ + super.key, + this.onTap, + required this.activeTab, + // required this.labels, + // required this.icons, + }); + + @override + Widget build(BuildContext context) { + AppL10n l10n = AppL10n.of(context); + return BottomNavigationBar( + onTap: onTap, + currentIndex: activeTab.index, + elevation: 3, + // fixedColor: themeColor.activeColor, + type: BottomNavigationBarType.fixed, + iconSize: 22, + selectedItemColor: Theme.of(context).primaryColor, + selectedLabelStyle: const TextStyle(fontWeight: FontWeight.bold), + showUnselectedLabels: true, + showSelectedLabels: true, + // backgroundColor: themeColor.itemColor, + items: AppTab.mobileTabs + .map((AppTab tab) => BottomNavigationBarItem( + label: tab.label(l10n), + icon: Icon(tab.icon), + )) + .toList() + + // labels + // .asMap() + // .keys + // .map((index) => + // + // .toList(), + ); + } +} diff --git a/lib/src/navigation/view/mobile/unit_navigation.dart b/lib/src/navigation/view/mobile/unit_navigation.dart new file mode 100644 index 000000000..2873021dc --- /dev/null +++ b/lib/src/navigation/view/mobile/unit_navigation.dart @@ -0,0 +1,102 @@ +import 'dart:io'; + +import 'package:algorithm/algorithm.dart'; +import 'package:app/app.dart'; +import 'package:app_update/app_update.dart'; +import 'package:artifact/artifact.dart'; +import 'package:authentication/authentication.dart'; +import 'package:draw_system/draw_system.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_unit/src/navigation/model/app_tab.dart'; +import 'package:treasure_tools/treasure_tools.dart'; +import 'package:widget_module/blocs/blocs.dart'; +import 'package:widget_module/widget_module.dart'; +import 'package:note/note.dart'; +import 'news.dart'; +import 'pure_bottom_bar.dart'; + +/// create by 张风捷特烈 on 2020-04-11 +/// contact me by email 1981462002@qq.com +/// 说明: 主题结构 左右滑页 + 底部导航栏 + +class UnitPhoneNavigation extends StatefulWidget { + const UnitPhoneNavigation({super.key}); + + @override + State createState() => _UnitPhoneNavigationState(); +} + +class _UnitPhoneNavigationState extends State { + //页面控制器,初始 0 + final PageController _controller = PageController(); + final ValueNotifier _activeTab = ValueNotifier(AppTab.widgets); + + // 禁止 PageView 滑动 + final ScrollPhysics _neverScroll = const NeverScrollableScrollPhysics(); + + @override + void initState() { + super.initState(); + if (Platform.isAndroid || Platform.isIOS) {} + String locale = + context.read().state.language.locale.toString(); + context.read().add(CheckUpdate(appId: 1, locale: locale)); + } + + @override + void dispose() { + _controller.dispose(); //释放控制器 + _activeTab.dispose(); + super.dispose(); + } + + /// extendBody = true 凹嵌透明,需要处理底部 边距 + @override + Widget build(BuildContext context) { + return Scaffold( + extendBody: true, + endDrawer: const HomeRightDrawer(), + body: PageView( + physics: _neverScroll, + controller: _controller, + children: [ + StandardHomePage(heard: NewsHeader()), + GalleryUnit(), + AlgoScope(child: ArtifactPage()), + ArtSysScope(child: MobileArticlePage()), + // MobileToolPage(), + UserPage(), + ], + ), + bottomNavigationBar: _buildBottomNav(context), + ); + } + + bool get isDark => Theme.of(context).brightness == Brightness.dark; + + // 由于 bottomNavigationBar 颜色需要随 点击头部栏 状态而改变, + // 使用 BlocBuilder 构建 + Widget _buildBottomNav(BuildContext context) { + return Stack( + children: [ + ValueListenableBuilder( + valueListenable: _activeTab, + builder: (_, value, __) => PureBottomBar( + onTap: _onTapBottomNav, + activeTab: value, + )), + const Positioned(right: 22, top: 8, child: UpdateRedPoint()) + ], + ); + } + + // 点击底部按钮事件,切换页面 + void _onTapBottomNav(int index) { + _controller.jumpToPage(index); + _activeTab.value = AppTab.values[index]; + if (index == 3) { + context.read().loadLikeData(); + } + } +} diff --git a/lib/src/starter/bridge/unit_bridge.dart b/lib/src/starter/bridge/unit_bridge.dart new file mode 100644 index 000000000..11c7f4bc8 --- /dev/null +++ b/lib/src/starter/bridge/unit_bridge.dart @@ -0,0 +1,9 @@ +import 'package:fx_dio/fx_dio.dart'; +import 'package:fx_dio/src/client/host.dart'; +import 'package:note/note.dart'; +import 'package:app/app.dart'; + +class UnitNoteBridge with NoteModuleBridge { + @override + Host get host => FxDio()(); +} diff --git a/lib/src/starter/fx_application.dart b/lib/src/starter/fx_application.dart new file mode 100644 index 000000000..b029751d0 --- /dev/null +++ b/lib/src/starter/fx_application.dart @@ -0,0 +1,55 @@ +import 'package:app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:fx_boot_starter/fx_boot_starter.dart'; +import 'package:go_router/go_router.dart'; +import 'package:widget_module/widget_module.dart'; +import 'package:widget_module/blocs/blocs.dart'; +import '../flutter_unit.dart'; +import '../navigation/view/app_bloc_provider.dart'; +import 'start_repository.dart'; +import 'package:app_update/app_update.dart'; +import 'package:note/note.dart'; + +export 'view/splash/Flutter_unit_splash.dart'; +export 'view/error/app_start_error.dart'; + +class FxApplication with FxStarter { + const FxApplication(); + + @override + Widget get app => const AppBlocProvider(child: FlutterUnit3()); + + @override + AppStartRepository get repository => const FlutterUnitStartRepo(); + + @override + void onLoaded(BuildContext context, int cost, AppConfig state) async { + debugPrint("App启动耗时:$cost ms"); + context.read().init(state); + context.initWidgetData(); + if (!kAppEnv.isWeb) { + context.read().loadLikeData(); + context.read().add(const EventLoadCategory()); + context.read().load(); + } + } + + @override + void onStartSuccess(BuildContext context, AppConfig state) { + CheckUpdate event = CheckUpdate(appId: 1, locale: state.localeValue); + context.read().add(event); + context.go(AppRoute.widget.url); + sendEvent(1); + } + + @override + void onStartError(BuildContext context, Object error, StackTrace trace) { + context.go(AppRoute.startError.url, extra: error); + } + + @override + void onGlobalError(Object error, StackTrace stack) { + print(error); + } +} diff --git a/lib/src/starter/start_repository.dart b/lib/src/starter/start_repository.dart new file mode 100644 index 000000000..e022bdf03 --- /dev/null +++ b/lib/src/starter/start_repository.dart @@ -0,0 +1,100 @@ +import 'dart:convert'; +import 'dart:io'; +import 'package:note/note.dart'; +import 'package:app/app.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:fx_boot_starter/fx_boot_starter.dart'; +import 'package:flutter/services.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:storage/storage.dart'; +import 'package:path/path.dart' as path; +import 'package:utils/utils.dart'; +import 'package:path/path.dart' as p; +import 'bridge/unit_bridge.dart'; +import 'package:widget_module/widget_module.dart'; + +class FlutterUnitStartRepo implements AppStartRepository { + const FlutterUnitStartRepo(); + + /// 初始化 app 的异步任务 + /// 返回本地持久化的 AppConfig 对象 + @override + Future initApp() async { + WidgetsFlutterBinding.ensureInitialized(); + // 滚动性能优化 1.22.0 + GestureBinding.instance.resamplingEnabled = true; + WindowSizeAdapter.setSize(); + await SpStorage().initSp(); + await initAppMeta(); + + registerHttpClient(); + NoteEnv().attachBridge(UnitNoteBridge()); + if (!kAppEnv.isWeb) await initDb(); + await initWidgetStatistics(); // 加载统计数据 + + HttpUtil.instance.rebase(PathUnit.baseUrl); + AppConfigPo po = await SpStorage().appConfig.read(); + AppConfig state = AppConfig.fromPo(po); + return state; + } + + Future initDb() async { + //数据库不存在,执行拷贝 + String dbPath = await AppStorage().flutter.dbpath; + bool shouldCopy = await _checkShouldCopy(dbPath, SpStorage().spf); + if (shouldCopy) { + await _doCopyAssetsDb(dbPath); + } else { + print("=====flutter.db 已存在===="); + } + await AppStorage().init(); + } + + Future _doCopyAssetsDb(String dbPath) async { + Directory dir = Directory(path.dirname(dbPath)); + if (!dir.existsSync()) { + await dir.create(recursive: true); + } + { + ByteData data = await rootBundle.load("assets/flutter.db"); + List bytes = + data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); + await File(dbPath).writeAsBytes(bytes, flush: true); + } + { + ByteData data = await rootBundle.load("assets/article.db"); + List bytes = + data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); + await File(p.join(dir.path, 'article.db')) + .writeAsBytes(bytes, flush: true); + } + print("=====flutter.db==== assets ======拷贝完成===="); + } + + Future _checkShouldCopy(String dbPath, SharedPreferences prefs) async { + bool shouldCopy = false; + String versionStr = await rootBundle.loadString('assets/version.json'); + int dbVersion = await json.decode(versionStr)['dbVersion']; + int versionInSP = prefs.getInt(SpKey.dbVersionKey) ?? -1; + + // 版本升级,执行拷贝 + if (dbVersion > versionInSP) { + shouldCopy = true; + await prefs.setInt(SpKey.dbVersionKey, dbVersion); + } + + //非 release模式,执行拷贝 + if (kDebugMode) { + shouldCopy = true; + } + + //数据库不存在,执行拷贝 + if (!File(dbPath).existsSync()) { + shouldCopy = true; + } + + return shouldCopy; + } +} diff --git a/lib/src/starter/view/error/app_start_error.dart b/lib/src/starter/view/error/app_start_error.dart new file mode 100644 index 000000000..ab3784fd2 --- /dev/null +++ b/lib/src/starter/view/error/app_start_error.dart @@ -0,0 +1,49 @@ +import 'package:app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:tolyui/basic/basic.dart'; + +class AppStartErrorPage extends StatelessWidget { + final Object? error; + const AppStartErrorPage({super.key, required this.error}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: PreferredDragToMoveWrapper( + child: AppBar( + title: const Text( + "App 启动异常", + style: TextStyle(fontFamily: '宋体'), + ), + actions: const [WindowButtons()], + ), + ), + body: Center( + child: Column( + children: [ + Expanded( + child: Center( + child: Wrap( + direction: Axis.vertical, + children: [ + const Text('应用启动异常:'), + Text( + error.toString(), + style: const TextStyle(color: Colors.redAccent), + ), + ], + ))), + TolyLink( + href: 'https://github.com/toly1994328/', + text: 'Github 开源地址: FlutterUnit', + onTap: (l) {}), + const Text("联系邮箱: 1981462002@qq.com"), + const SizedBox( + height: 12, + ), + ], + ), + ), + ); + } +} diff --git a/lib/src/starter/view/splash/Flutter_unit_splash.dart b/lib/src/starter/view/splash/Flutter_unit_splash.dart new file mode 100644 index 000000000..fadb85b50 --- /dev/null +++ b/lib/src/starter/view/splash/Flutter_unit_splash.dart @@ -0,0 +1,156 @@ +import 'dart:math'; + +import 'package:app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:fx_boot_starter/fx_boot_starter.dart'; + +import 'dart:ui' as ui; + +import 'flutter_unit_text.dart'; + +/// create by 张风捷特烈 on 2020-03-07 +/// contact me by email 1981462002@qq.com +/// 说明: app 闪屏页 +class FlutterUnitSplash extends StatelessWidget { + const FlutterUnitSplash({super.key}); + + @override + Widget build(BuildContext context) { + return const AppStartListener( + child: AnnotatedRegion( + value: SystemUiOverlayStyle( + statusBarColor: Colors.transparent, + statusBarIconBrightness: Brightness.dark, + systemNavigationBarColor: Colors.transparent, + ), + child: Material(color: Colors.white, child: _SplashBody()), + ), + ); + } +} + +class _SplashBody extends StatelessWidget { + const _SplashBody({super.key}); + + @override + Widget build(BuildContext context) { + final Color color = Theme.of(context).primaryColor; + const TextStyle shadowStyle = UnitTextStyle.splashShadows; + const TextStyle titleStyle = TextStyle(fontWeight: FontWeight.bold); + + return Column( + children: [ + const SplashTopBar( + leading: Text('Flutter Unit', style: titleStyle), + logo: CircleAvatar( + backgroundImage: AssetImage('assets/images/icon_head.webp'), + radius: 14, + ), + ), + const Spacer(), + Expanded( + child: Wrap( + direction: Axis.vertical, + alignment: WrapAlignment.center, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + const Stack(children: [ColorfulText(), FlutterLogo(size: 60)]), + const SizedBox(height: 20), + FlutterUnitText( + text: StrUnit.appName, + color: color, + ), + ], + )), + const Expanded( + child: Stack( + alignment: Alignment.bottomCenter, + children: [ + Positioned( + bottom: 15, + child: Wrap( + direction: Axis.vertical, + alignment: WrapAlignment.center, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + Text("Power By 张风捷特烈", style: shadowStyle), + Text("· 2025 · @编程之王 ", style: shadowStyle), + ], + )), + ], + )) + ], + ); + } +} + +class ColorfulText extends StatelessWidget { + const ColorfulText({super.key}); + + @override + Widget build(BuildContext context) { + final Paint paint = Paint() + ..style = PaintingStyle.stroke + ..shader = ui.Gradient.linear( + const Offset(0, 0), + const Offset(22, 0), + [Colors.red, Colors.yellow, Colors.blue, Colors.green], + [1 / 4, 2 / 4, 3 / 4, 1], + TileMode.mirror, + Matrix4.rotationZ(pi / 4).storage, + ); + return Text( + "U", + style: TextStyle( + fontSize: 26, + height: 1, + fontWeight: FontWeight.bold, + foreground: paint), + ); + } +} + +class SplashTopBar extends StatelessWidget { + final Widget? leading; + final Widget? logo; + + const SplashTopBar({super.key, this.leading, this.logo}); + + @override + Widget build(BuildContext context) { + if (!kIsDesk) return const SizedBox.shrink(); + return DragToMoveWrapper( + child: Stack( + children: [ + Container( + alignment: Alignment.topLeft, + padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8), + child: Row( + children: [ + if (leading != null) + Row( + children: [ + if (logo != null) logo!, + const SizedBox( + width: 8, + ), + leading!, + ], + ), + const Spacer(), + const SizedBox( + width: 20, + ), + ], + ), + ), + const Positioned( + right: 0, + child: WindowButtons(), + ) + ], + ), + ); + } +} diff --git a/lib/app/views/splash/flutter_unit_text.dart b/lib/src/starter/view/splash/flutter_unit_text.dart similarity index 85% rename from lib/app/views/splash/flutter_unit_text.dart rename to lib/src/starter/view/splash/flutter_unit_text.dart index 09b6e3707..427cf92e6 100644 --- a/lib/app/views/splash/flutter_unit_text.dart +++ b/lib/src/starter/view/splash/flutter_unit_text.dart @@ -6,15 +6,17 @@ import 'package:flutter/material.dart'; class FlutterUnitText extends StatefulWidget { final String text; final Color color; + final double fontSize; const FlutterUnitText({ this.text = "Toly", this.color = Colors.blue, + this.fontSize = 32, Key? key, }) : super(key: key); @override - _FlutterUnitTextState createState() => _FlutterUnitTextState(); + State createState() => _FlutterUnitTextState(); } class _FlutterUnitTextState extends State @@ -31,7 +33,7 @@ class _FlutterUnitTextState extends State void initState() { super.initState(); TextSpan text = TextSpan( - text: widget.text, style: const TextStyle(fontSize: 32, color: Colors.blue)); + text: widget.text, style: TextStyle(fontSize: widget.fontSize, color: Colors.blue)); _textPainter.text = text; _textPainter.layout(); // 进行布局 @@ -60,6 +62,7 @@ class _FlutterUnitTextState extends State return CustomPaint( size: _textPainter.size, painter: SpringPainter( + fontSize: widget.fontSize, textPainter: _textPainter, color: widget.color, skew: animation, @@ -80,11 +83,12 @@ class Interpolator extends Curve { class SpringPainter extends CustomPainter { final ValueListenable skew; final TextPainter textPainter; + final double fontSize; String _text = ''; Color color; SpringPainter( - {required this.skew, required this.textPainter, this.color = Colors.blue}) + {required this.skew, required this.textPainter, this.color = Colors.blue,required this.fontSize}) : super(repaint: skew) { _text = textPainter.text?.toPlainText() ?? ''; } @@ -93,7 +97,7 @@ class SpringPainter extends CustomPainter { void paint(Canvas canvas, Size size) { canvas.translate(size.width / 2, size.height / 2); TextSpan text = - TextSpan(text: _text, style: TextStyle(fontSize: 32, color: color)); + TextSpan(text: _text, style: TextStyle(fontSize: fontSize, color: color)); textPainter.text = text; textPainter.layout(); // 进行布局 Size textSize = textPainter.size; // 尺寸必须在布局后获取 @@ -103,7 +107,7 @@ class SpringPainter extends CustomPainter { textPainter.paint(canvas, Offset.zero); TextSpan textShadow = TextSpan( text: _text, - style: TextStyle(fontSize: 32, color: color.withAlpha(88))); + style: TextStyle(fontSize: fontSize, color: color.withAlpha(88))); textPainter.text = textShadow; textPainter.layout(); // 进行布局 Matrix4 matrix4 = Matrix4.skewX((6 / 180 * pi) * skew.value); diff --git a/lib/widget_ui/desk_ui/category_panel/desk_category_page.dart b/lib/widget_ui/desk_ui/category_panel/desk_category_page.dart deleted file mode 100644 index a2986f1d4..000000000 --- a/lib/widget_ui/desk_ui/category_panel/desk_category_page.dart +++ /dev/null @@ -1,151 +0,0 @@ -import 'package:app/app.dart'; -import 'package:components/components.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:widget_module/blocs/blocs.dart'; -import 'package:widget_repository/widget_repository.dart'; - -import '../../../components/top_bar/desk_tab_top_bar.dart'; -import '../../mobile/category_page/category_list_item.dart'; -import '../../mobile/category_page/delete_category_dialog.dart'; -import '../../mobile/category_page/edit_category_panel.dart'; -import '../../mobile/category_page/like_widget_page.dart'; -import 'desk_top_like_panel.dart'; - -class DeskCategoryPage extends StatefulWidget { - const DeskCategoryPage({Key? key}) : super(key: key); - - @override - State createState() => _DeskCategoryPageState(); -} - -class _DeskCategoryPageState extends State { - - final PageController _ctrl = PageController(); - - @override - Widget build(BuildContext context) { - return Scaffold( - body: Column( - children: [ - DeskTabTopBar(onTabPressed: (int value) { - _ctrl.jumpToPage(value); - }, tabs: ['组件酒肆','珍藏组件', - // '添加收藏集' - ],), - Expanded(child: PageView( - controller: _ctrl, - children: [ - DeskCateGoryPage(), - DeskLikePage(), - ], - )) - ], - ), - ); - } -} - -class DeskCateGoryPage extends StatelessWidget { - const DeskCateGoryPage({Key? key}) : super(key: key); - - final SliverGridDelegate deskGridDelegate = - const SliverGridDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: 300, - mainAxisSpacing: 10, - mainAxisExtent: 260, - crossAxisSpacing: 10, - ); - - @override - Widget build(BuildContext context) { - - CategoryBloc bloc = context.read(); - CategoryState state = bloc.state; - if(state is CategoryLoadedState){ - return GridView.builder( - itemCount: state.categories.length, - padding: EdgeInsets.all(20), - gridDelegate: deskGridDelegate, itemBuilder: (_, index) => GestureDetector( - onTap: () => _toDetailPage(context, state.categories[index]), - child: CategoryListItem( - data: state.categories[index], - onDeleteItemClick: (model) => - _deleteCollect(context, model), - onEditItemClick: (model) => - _editCollect(context, model), - ))); - } - - return SizedBox.shrink(); - - } - - - ShapeBorder get rRectBorder => const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10))); - - void _deleteCollect(BuildContext context, CategoryModel model) { - showDialog( - context: context, - builder: (ctx) => Dialog( - elevation: 5, - shape: rRectBorder, - child: SizedBox( - width: 50, - child: DeleteCategoryDialog( - title: '删除收藏集', - content: ' 删除【${model.name}】收藏集,你将会失去其中的所有收藏组件,是否确定继续执行?', - onSubmit: () { - BlocProvider.of(context) - .add(EventDeleteCategory(id: model.id!)); - Navigator.of(context).pop(); - }, - ), - ), - )); - } - - void _editCollect(BuildContext context, CategoryModel model) { - showDialog( - context: context, - builder: (ctx) => Dialog( - backgroundColor: const Color(0xFFF2F2F2), - elevation: 5, - shape: rRectBorder, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Gap.H5, - Row( - children: [ - Padding( - padding: const EdgeInsets.only(left: 20, right: 10), - child: Circle( - color: Theme.of(context).primaryColor, - ), - ), - const Text( - '修改收藏集', - style: TextStyle(fontSize: 20), - ), - const Spacer(), - const CloseButton() - ], - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: EditCategoryPanel(model: model,type: EditType.update,), - ), - ], - ), - )); - } - - void _toDetailPage(BuildContext context, CategoryModel model) { - BlocProvider.of(context).add(EventLoadCategoryWidget(model.id!)); - Navigator.pushNamed(context, UnitRouter.category_show, arguments: model); - } - - -} diff --git a/lib/widget_ui/desk_ui/category_panel/desk_top_like_panel.dart b/lib/widget_ui/desk_ui/category_panel/desk_top_like_panel.dart deleted file mode 100644 index 7e5cd8afa..000000000 --- a/lib/widget_ui/desk_ui/category_panel/desk_top_like_panel.dart +++ /dev/null @@ -1,55 +0,0 @@ -import 'package:app/app.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/widget_ui/mobile/widget_detail/widget_detail_page.dart'; -import 'package:widget_module/blocs/blocs.dart'; -import 'package:widget_repository/widget_repository.dart'; - -import '../../mobile/widget_detail/collect_widget_list_item.dart'; - -class DeskLikePage extends StatelessWidget { - const DeskLikePage({Key? key}) : super(key: key); - - final SliverGridDelegate deskGridDelegate = - const SliverGridDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: 300, - mainAxisSpacing: 15, - mainAxisExtent: 80, - crossAxisSpacing: 15, - ); - - @override - Widget build(BuildContext context) { - - LikeWidgetBloc bloc = context.watch(); - LikeWidgetState state = bloc.state; - return GridView.builder( - itemCount: state.widgets.length, - padding: EdgeInsets.all(20), - gridDelegate: deskGridDelegate, itemBuilder: (_, index) => GestureDetector( - onTap: () => _toDetailPage(context, state.widgets[index]), - child: CollectWidgetListItem( - data: state.widgets[index], - onDeleteItemClick: (model) => - _deleteCollect(context, model), - ))); - - return SizedBox.shrink(); - - } - - - ShapeBorder get rRectBorder => const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10))); - - - _deleteCollect(BuildContext context, WidgetModel model) => - BlocProvider.of(context).add(ToggleLikeWidgetEvent(id: model.id)); - - _toDetailPage(BuildContext context, WidgetModel model) { - // BlocProvider.of(context).add(FetchWidgetDetail(model)); - Navigator.push(context, SlidePageRoute(child: WidgetDetailPageScope(model: model))); - } - - -} \ No newline at end of file diff --git a/lib/widget_ui/desk_ui/widget_detail/widget_detail_bar.dart b/lib/widget_ui/desk_ui/widget_detail/widget_detail_bar.dart deleted file mode 100644 index 660bb1f37..000000000 --- a/lib/widget_ui/desk_ui/widget_detail/widget_detail_bar.dart +++ /dev/null @@ -1,124 +0,0 @@ -import 'dart:math'; - -import 'package:app/app.dart'; -import 'package:components/components.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -import 'package:utils/utils.dart'; - -import 'package:widget_module/blocs/blocs.dart'; -import 'package:widget_repository/widget_repository.dart'; - -class DeskSliverWidgetDetailBar extends StatelessWidget { - final WidgetModel model; - - const DeskSliverWidgetDetailBar({Key? key, required this.model}) - : super(key: key); - - final Color backgroundColor = const Color(0xffFAFAFA); - static const Color textColor = Color(0xff262626); - - @override - Widget build(BuildContext context) { - bool isDark = Theme.of(context).brightness == Brightness.dark; - Color? appBarColor = Theme.of(context).appBarTheme.backgroundColor; - Color? appBarTextColor = Theme.of(context).appBarTheme.titleTextStyle?.color; - - return SliverAppBar( - pinned: true, - backgroundColor: isDark? appBarColor:backgroundColor, - titleTextStyle: TextStyle(color: isDark?appBarTextColor:Color(0xff696969)), - iconTheme: IconThemeData(color: isDark?appBarTextColor:Color(0xff696969)), - expandedHeight: 120.0, - scrolledUnderElevation: 0.5, - flexibleSpace: DragToMoveAreaNoDouble( - child: DiyFlexibleSpaceBar( - centerTitle: false, - expandedTitleScale: 2, - titleIconBuilder: (t) => WindmillWidget( - rotate: t * 2 * pi * 2, - radius: 15, - ), - fixedSubtitle: Text( - model.name, - style: TextStyle(color:isDark?appBarTextColor:Color(0xff696969), fontSize: 12), - ), - title: Padding( - padding: const EdgeInsets.only(bottom: 3), - child: Text( - model.nameCN, - style: TextStyle(color:isDark?appBarTextColor: textColor, fontSize: 16), - ), - ), - //伸展处布局 - titlePadding: const EdgeInsets.only(left: 20, bottom: 10), - //标题边距 - collapseMode: CollapseMode.parallax, - ), - ), - elevation: 0, - actions: [ - - WindowButtons( - actions: [ - _buildToHome(context), - FeedbackWidget( - onPressed: () => _toggleLikeState(context), - child: BlocConsumer( - listener: _listenLikeStateChange, - builder: _buildByLikeState, - ), - ), - ], - ) - ], - ); - } - - void _toggleLikeState(BuildContext context) { - BlocProvider.of(context).add( - ToggleLikeWidgetEvent(id: model.id), - ); - } - - // 监听 LikeWidgetBloc 伺机弹出 toast - void _listenLikeStateChange(BuildContext context, LikeWidgetState state) { - bool collected = state.widgets.contains(model); - String msg = - collected ? "收藏【${model.name}】组件成功!" : "已取消【${model.name}】组件收藏!"; - Toast.toast( - context, - msg, - duration: Duration(milliseconds: collected ? 1500 : 600), - action: collected - ? SnackBarAction( - textColor: Colors.white, - label: '收藏夹管理', - onPressed: () => Scaffold.of(context).openEndDrawer()) - : null, - ); - } - - // 根据 [LikeWidgetState ] 构建图标 - Widget _buildByLikeState(BuildContext context, LikeWidgetState state) { - bool liked = state.widgets.contains(model); - return SizedBox( - width: 30, - height: 30, - child: Icon( - liked ? TolyIcon.icon_star_ok : TolyIcon.icon_star_add, - size: 20, - ), - ); - } - - Widget _buildToHome(BuildContext context) => GestureDetector( - onLongPress: () => Scaffold.of(context).openEndDrawer(), - child:const SizedBox( - width: 30, - height: 30, - child: Icon(Icons.home,size: 20,), - ), - onTap: () => Navigator.of(context).pop()); -} diff --git a/lib/widget_ui/desk_ui/widget_detail/widget_detail_page.dart b/lib/widget_ui/desk_ui/widget_detail/widget_detail_page.dart deleted file mode 100644 index 6980a1894..000000000 --- a/lib/widget_ui/desk_ui/widget_detail/widget_detail_page.dart +++ /dev/null @@ -1,133 +0,0 @@ -import 'package:app/app.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/widget_ui/mobile/widget_detail/category_end_drawer.dart'; -import 'package:widget_module/blocs/blocs.dart'; - -import 'package:widget_repository/widget_repository.dart'; -import 'package:widgets/widgets.dart'; - -import 'link_widget_buttons.dart'; -import 'widget_detail_bar.dart'; -import 'widget_detail_panel.dart'; -import 'widget_node_panel.dart'; - -// 用于组件详情不需要在一开始就加载 -// WidgetDetailBloc 可以在稍后提供 -class DeskWidgetDetailPageScope extends StatelessWidget { - final WidgetModel model; - - const DeskWidgetDetailPageScope({super.key, required this.model}); - - @override - Widget build(BuildContext context) { - return BlocProvider( - create: (_) => WidgetDetailBloc( - widgetRepository: const WidgetDbRepository(), - nodeRepository: const NodeDbRepository()) - ..push(model), - child: DeskWidgetDetailPage( - model: model, - ), - ); - } -} - -class DeskWidgetDetailPage extends StatelessWidget { - final WidgetModel model; - - const DeskWidgetDetailPage({Key? key, required this.model}) : super(key: key); - - @override - Widget build(BuildContext context) { - WidgetDetailBloc bloc = context.watch(); - - return BlocBuilder( - builder: (_, state) => Scaffold( - endDrawer: CategoryEndDrawer(widget: bloc.currentWidget), - body: Builder(builder: (ctx) { - return _buildContent(ctx, bloc); - }), - ), - ); - } - - Widget get linkText => Row( - children: const [ - Padding( - padding: EdgeInsets.only(left: 15, right: 5), - child: Icon(Icons.link, color: Colors.blue), - ), - Text('相关组件', style: UnitTextStyle.labelBold), - ], - ); - - Widget _buildContent(BuildContext context, WidgetDetailBloc bloc) { - DetailState state = bloc.state; - return WillPopScope( - onWillPop: () => _whenPop(context), - child: CustomScrollView( - slivers: [ - DeskSliverWidgetDetailBar(model: bloc.currentWidget), - SliverToBoxAdapter( - child: Column( - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: DeskWidgetDetailPanel(model: bloc.currentWidget), - ), - const SizedBox( - width: 20, - ), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - linkText, - if (state is DetailWithData) - LinkWidgetButtons( - links: state.links, - onSelect: bloc.push, - ) - ], - )) - ], - ), - const Divider() - ], - ), - ), - if (state is DetailWithData) - _buildSliverNodeList(context, state.nodes, state.widgetModel) - ], - )); - } - - Future _whenPop(BuildContext context) async { - WidgetDetailBloc detailBloc = context.read(); - if (Scaffold.of(context).isEndDrawerOpen) { - return true; - } - return detailBloc.pop(); - } - - Widget _buildSliverNodeList( - BuildContext context, List nodes, WidgetModel model) { - AppState globalState = BlocProvider.of(context).state; - return SliverList( - delegate: SliverChildBuilderDelegate( - (_, i) => DeskWidgetNodePanel( - codeStyle: globalState.codeStyle, - codeFamily: 'Inconsolata', - text: nodes[i].name, - subText: nodes[i].subtitle, - code: nodes[i].code, - death: model.death, - show: WidgetsMap.map(model.name)[i], - ), - childCount: nodes.length, - )); - } -} diff --git a/lib/widget_ui/desk_ui/widget_detail/widget_detail_panel.dart b/lib/widget_ui/desk_ui/widget_detail/widget_detail_panel.dart deleted file mode 100644 index 6b8e195d8..000000000 --- a/lib/widget_ui/desk_ui/widget_detail/widget_detail_panel.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'package:components/toly_ui/toly_ui.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_star/flutter_star.dart'; -import 'package:widget_repository/widget_repository.dart'; - -class DeskWidgetDetailPanel extends StatelessWidget { - final WidgetModel model; - - const DeskWidgetDetailPanel({Key? key, required this.model}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - _buildLeft(model, context), - _buildRight(model), - ], - ), - // const Divider(), - ], - ); - } - - Widget _buildLeft(WidgetModel model, BuildContext context) => Expanded( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Panel( - color: Theme.of(context).appBarTheme.backgroundColor, - child: Text(model.info)), - ), - ); - - Widget _buildRight(WidgetModel model) => Column( - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox( - height: 100, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Hero( - tag: "hero_widget_image_${model.id}", - child: ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(8)), - child: model.image == null - ? Image.asset('assets/images/caver.webp') - : Image(image: model.image!))), - ), - ), - StarScore( - score: model.lever, - star: const Star(size: 15, fillColor: Colors.blue), - ) - ], - ); -} diff --git a/lib/widget_ui/desk_ui/widget_detail/widget_node_panel.dart b/lib/widget_ui/desk_ui/widget_detail/widget_node_panel.dart deleted file mode 100644 index ab11b1a95..000000000 --- a/lib/widget_ui/desk_ui/widget_detail/widget_node_panel.dart +++ /dev/null @@ -1,166 +0,0 @@ -import 'package:app/app.dart'; -import 'package:components/toly_ui/toly_ui.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - - -import 'package:toggle_rotate/toggle_rotate.dart'; -import 'package:utils/utils.dart'; - - -/// create by 张风捷特烈 on 2020-04-13 -/// contact me by email 1981462002@qq.com -/// 说明: 一个Widget的知识点对应的界面 - -class DeskWidgetNodePanel extends StatefulWidget { - final String text; - final String subText; - final String code; - final Widget? show; - final HighlighterStyle? codeStyle; - final String? codeFamily; - final bool death; - - const DeskWidgetNodePanel( - {Key? key, this.text='', - this.subText='', - this.code='', - this.death=false, - this.show, - required this.codeStyle, - this.codeFamily}) : super(key: key); - - @override - _DeskWidgetNodePanelState createState() => _DeskWidgetNodePanelState(); -} - -class _DeskWidgetNodePanelState extends State { - CrossFadeState _crossFadeState = CrossFadeState.showFirst; - - bool get isFirst => _crossFadeState == CrossFadeState.showFirst; - - Color get themeColor => Theme.of(context).primaryColor; - - @override - Widget build(BuildContext context) { - return Container( - margin: const EdgeInsets.all(10), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - buildNodeTitle(), - const SizedBox( - height: 20, - ), - _buildCode(context), - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: Center( - child: Padding( - padding: const EdgeInsets.only(top: 10, bottom: 20), - child: widget.show, - ), - ), - ), - if(!widget.death) - Expanded(child: _buildNodeInfo()), - ], - ), - - - const Divider(), - ], - ), - ); - } - - Widget buildNodeTitle() => Row( - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8), - child: Circle( - color: themeColor, - radius: 5, - ), - ), - Expanded( - child: Text( - widget.text, - style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 15), - ), - ), - _buildShareButton(), - _buildCodeButton() - ], - ); - - Widget _buildNodeInfo() => SizedBox( - width: double.infinity, - child: Panel( - color: Theme.of(context).appBarTheme.backgroundColor, - child: Text( - widget.subText, - style: const TextStyle(fontSize: 12), - )), - ); - - Widget _buildCodeButton() => Padding( - padding: const EdgeInsets.only(right: 10.0), - child: ToggleRotate( - durationMs: 300, - child: Icon( - TolyIcon.icon_code, - color: themeColor, - ), - onTap: _toggleCodePanel, - ), - ); - - Widget _buildShareButton() => FeedbackWidget( - mode: FeedMode.fade, - a: 0.4, - onPressed: _doShare, - child: Padding( - padding: const EdgeInsets.only(right: 10), - child: Icon( - Icons.copy, - size: 20, - color: themeColor, - ), - ), - ); - - Widget _buildCode(BuildContext context) => AnimatedCrossFade( - firstCurve: Curves.easeInCirc, - secondCurve: Curves.easeInToLinear, - firstChild: const SizedBox(), - secondChild: SizedBox( - width: MediaQuery.of(context).size.width, - child: CodeWidget( - fontFamily: widget.codeFamily, - code: isFirst?'':widget.code, - style: widget.codeStyle ?? - HighlighterStyle.fromColors(HighlighterStyle.lightColor), - ), - ), - duration: const Duration(milliseconds: 200), - crossFadeState: _crossFadeState, - ); - - //执行分享 - _doShare() async{ - // Share.share(widget.code); - await Clipboard.setData(ClipboardData(text: widget.code)); - Toast.success(context, '代码复制成功!'); - } - - // 折叠代码面板 - _toggleCodePanel() { - setState(() { - _crossFadeState = - !isFirst ? CrossFadeState.showFirst : CrossFadeState.showSecond; - }); - } -} diff --git a/lib/widget_ui/desk_ui/widget_panel/desk_widget_model_item.dart b/lib/widget_ui/desk_ui/widget_panel/desk_widget_model_item.dart deleted file mode 100644 index eba629fea..000000000 --- a/lib/widget_ui/desk_ui/widget_panel/desk_widget_model_item.dart +++ /dev/null @@ -1,172 +0,0 @@ -import 'package:app/app.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_star/flutter_star.dart'; -import 'package:widget_module/blocs/blocs.dart'; - -import 'package:widget_repository/widget_repository.dart'; -import 'package:wrapper/wrapper.dart'; - -class DeskWidgetItem extends StatelessWidget { - final WidgetModel model; - final VoidCallback onTap; - final String? searchArg; - - const DeskWidgetItem( - {Key? key, required this.model, required this.onTap, this.searchArg}) - : super(key: key); - - @override - Widget build(BuildContext context) { - - Color? tileColor = Theme.of(context).listTileTheme.tileColor; - Color? textColor = Theme.of(context).listTileTheme.textColor; - bool isDark = Theme.of(context).brightness == Brightness.dark; - textColor = isDark?textColor: Color(0xff2F3032); - - return InkWell( - borderRadius: BorderRadius.circular(6), - onTap: onTap, - child: Ink( - decoration: BoxDecoration( - color: tileColor, - borderRadius: BorderRadius.circular(6), - boxShadow: [ - BoxShadow( - color: Theme.of(context).primaryColor.withOpacity(0.1), - blurRadius: 2, - ) - ] - ), - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15), - // margin: - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - _buildTitle(Theme.of(context).primaryColor,textColor,isDark), - _buildContent(textColor), - _buildFoot(isDark) - ], - ), - ), - ); - } - - Widget _buildTitle(Color color,Color? textColor,bool isDark) { - return Row( - children: [ - if (searchArg == null) - Text( - model.name, - style: TextStyle( - fontSize: 15, - color: textColor, - fontWeight: FontWeight.bold, - ), - ), - if (searchArg != null) Text.rich(formSpan(model.name, searchArg!)), - const SizedBox(width: 8), - BlocBuilder(builder: (_, s) { - bool show = s.widgets.contains(model); - if (!show) return const SizedBox(); - return Opacity( - opacity: show ? 1.0 : 0.0, - child: Wrapper.just( - radius: 10, - color: isDark? Color(0xff292A2D):const Color(0xffF3F3F5), - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), - child: Text( - '已收藏', - style: TextStyle( - color: color, - height: 1, - fontSize: 10, - shadows: [ - Shadow( - color:isDark? Colors.black: Colors.white, - blurRadius: 2, - offset: Offset(1, 1)) - ]), - ), - ), - ); - }), - const Spacer(), - StarScore( - star: Star(emptyColor: Colors.white, size: 12, fillColor: color), - score: model.lever, - ), - ], - ); - } - - Widget _buildContent(Color? textColor) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 8), - child: Text( - model.info, - maxLines: 2, - overflow: TextOverflow.ellipsis, - style: TextStyle(fontSize: 14, color: textColor), - ), - ); - } - - Widget _buildFoot(bool isDark) { - return Row( - children: [ - Container( - width: 4, - height: 4, - margin: const EdgeInsets.only(right: 6), - decoration: const BoxDecoration( - color: Color(0xff86909c), shape: BoxShape.circle), - ), - Text( - model.nameCN, - style: const TextStyle( - fontSize: 12, height: 1, color: Color(0xff86909c)), - ), - const Spacer(), - Wrapper.just( - radius: 2, - color: isDark? Color(0xff292A2D):const Color(0xffF3F3F5), - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), - child: Text( - Cons.kWidgetFamilyLabelMap[model.family]!, - style: TextStyle( - color: isDark?Color(0xffCCCCCC):Color(0xff878D96), - height: 1, - fontSize: 12, - shadows: [ - Shadow( - color: isDark? Colors.black:Colors.white, blurRadius: 2, offset: Offset(1, 1)) - ]), - ), - ), - ], - ); - } - - final TextStyle lightTextStyle = const TextStyle( - color: Colors.red, - fontSize: 16, - fontWeight: FontWeight.bold, - ); - - InlineSpan formSpan(String src, String pattern) { - List span = []; - RegExp regExp = RegExp(pattern, caseSensitive: false); - src.splitMapJoin(regExp, onMatch: (Match match) { - span.add(TextSpan(text: match.group(0), style: lightTextStyle)); - return ''; - }, onNonMatch: (str) { - span.add(TextSpan( - text: str, - style: lightTextStyle.copyWith(color: const Color(0xff2F3032)))); - return ''; - }); - return TextSpan(children: span); - } -} diff --git a/lib/widget_ui/desk_ui/widget_panel/desk_widget_top_bar.dart b/lib/widget_ui/desk_ui/widget_panel/desk_widget_top_bar.dart deleted file mode 100644 index 040d2d839..000000000 --- a/lib/widget_ui/desk_ui/widget_panel/desk_widget_top_bar.dart +++ /dev/null @@ -1,76 +0,0 @@ -import 'package:app/app.dart'; -import 'package:components/toly_ui/toly_ui.dart'; -import 'package:flutter/material.dart'; - -import 'desk_search_bar.dart'; - -class DeskWidgetTopBar extends StatefulWidget { - final ValueChanged onTabPressed; - - const DeskWidgetTopBar({Key? key,required this.onTabPressed}) : super(key: key); - - @override - State createState() => _DeskWidgetTopBarState(); -} - -class _DeskWidgetTopBarState extends State with SingleTickerProviderStateMixin { - late TabController tabController; - - static const List _tabs = ['无态', '有态', '单渲', '多渲', '滑片', '代理', '其它']; - - @override - void initState() { - super.initState(); - tabController = TabController(length: _tabs.length, vsync: this); - } - - @override - Widget build(BuildContext context) { - Color themeColor = Theme.of(context).primaryColor; - bool isDark = Theme.of(context).brightness == Brightness.dark; - - return DragToMoveAreaNoDouble( - child: Container( - padding: const EdgeInsets.only(left: 20), - height: 64, - color: isDark?Color(0xff2C3036):Colors.white, - child: Row( - children: [ - SizedBox( - width: 350, - child: TabBar( - onTap: widget.onTabPressed, - indicatorSize: TabBarIndicatorSize.label, - labelPadding: const EdgeInsets.symmetric(horizontal: 6), - isScrollable: false, - indicator: RoundRectTabIndicator( - borderSide: BorderSide(color: themeColor, width: 3), - ), - labelStyle: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), - controller: tabController, - labelColor: themeColor, - indicatorWeight: 3, - unselectedLabelColor: Colors.grey, - indicatorColor: themeColor, - tabs: - _tabs.map((String name) => Tab(text: name)).toList(), - ), - ), - Spacer(), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: SizedBox( - width: 250, height: 30, child: DeskSearchBar( - )), - ), - // const SizedBox(width: 20,), - const WindowButtons(), - ], - ), - ), - ); - } -} diff --git a/lib/widget_ui/desk_ui/widget_panel/widget_panel.dart b/lib/widget_ui/desk_ui/widget_panel/widget_panel.dart deleted file mode 100644 index d3e10a541..000000000 --- a/lib/widget_ui/desk_ui/widget_panel/widget_panel.dart +++ /dev/null @@ -1,98 +0,0 @@ -import 'package:app/app.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/app/utils/convert.dart'; -import 'package:widget_module/blocs/blocs.dart'; -import 'package:widget_repository/widget_repository.dart'; - -import 'desk_widget_model_item.dart'; -import 'desk_widget_top_bar.dart'; - -class DeskWidgetPanel extends StatefulWidget { - const DeskWidgetPanel({Key? key}) : super(key: key); - - @override - State createState() => _DeskWidgetPanelState(); -} - -class _DeskWidgetPanelState extends State{ - - @override - Widget build(BuildContext context) { - WidgetsState state = context.watch().state; - - return Scaffold( - body: Column( - children: [ - Shortcuts( - shortcuts: { - const SingleActivator(LogicalKeyboardKey.keyQ): VoidCallbackIntent(() { - print("hello"); - }), - }, - child: DeskWidgetTopBar( - onTabPressed: _switchTab, - ), - ), - const Divider(height: 1), - Expanded( - child: _buildByState(state), - ), - ], - ), - ); - } - - Widget _buildByState(WidgetsState state) { - if (state is WidgetsLoaded) { - return WidgetList(state: state); - } - return Center( - child: Text("${state.runtimeType}"), - ); - } - - void _switchTab(int index) { - WidgetFamily widgetFamily = Convert.toFamily(index); - BlocProvider.of(context).add(EventTabTap(widgetFamily)); - } -} - -class WidgetList extends StatelessWidget { - final WidgetsLoaded state; - - const WidgetList({Key? key, required this.state}) : super(key: key); - - @override - Widget build(BuildContext context) { - SliverGridDelegate gridDelegate = - const SliverGridDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: 400, - mainAxisSpacing: 10, - mainAxisExtent: 130, - crossAxisSpacing: 10, - ); - - return GridView.builder( - padding: EdgeInsets.symmetric(horizontal: 15, vertical: 10), - gridDelegate: gridDelegate, - itemBuilder: _buildItem, - itemCount: state.widgets.length, - ); - } - - Widget _buildItem(BuildContext context, int index) { - WidgetModel model = state.widgets[index]; - return DeskWidgetItem( - model: model, - onTap: () { - Navigator.pushNamed( - context, - UnitRouter.widget_detail, - arguments: model, - ); - }, - ); - } -} diff --git a/lib/widget_ui/mobile/category_page/like_widget_page.dart b/lib/widget_ui/mobile/category_page/like_widget_page.dart deleted file mode 100644 index 4a29dc31d..000000000 --- a/lib/widget_ui/mobile/category_page/like_widget_page.dart +++ /dev/null @@ -1,86 +0,0 @@ -import 'package:app/app.dart'; -import 'package:components/project_ui/project_ui.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -import 'package:widget_repository/widget_repository.dart'; -import 'package:flutter_unit/widget_ui/mobile/widget_detail/collect_widget_list_item.dart'; -import 'package:widget_module/blocs/blocs.dart'; - -import '../widget_detail/widget_detail_page.dart'; - - - -/// create by 张风捷特烈 on 2020/6/16 -/// contact me by email 1981462002@qq.com -/// 说明: - -class LikeWidgetPage extends StatelessWidget { - const LikeWidgetPage({Key? key}) : super(key: key); - - final SliverGridDelegate gridDelegate = - const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, - mainAxisSpacing: 8, - crossAxisSpacing: 8, - childAspectRatio: 1 / 0.5, - ); - - final SliverGridDelegate deskGridDelegate = - const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 3, - mainAxisSpacing: 10, - crossAxisSpacing: 10, - childAspectRatio: 1 / 0.5, - ); - - @override - Widget build(BuildContext context) { - return BlocBuilder(builder: (ctx, state) { - return CustomScrollView( - slivers: [ - SliverOverlapInjector( - handle: NestedScrollView.sliverOverlapAbsorberHandleFor(ctx), - ), - _buildContent(context, state), - const SliverToBoxAdapter( - child: NoMoreWidget(), - ) - ], - ); - }); - } - - Widget _buildContent(BuildContext context, LikeWidgetState state) { - return SliverPadding( - padding: const EdgeInsets.only(top: 10, left: 10, right: 10, bottom: 0), - sliver: SliverLayoutBuilder( - builder: (_,c){ - SliverGridDelegate delegate = gridDelegate; - if(c.crossAxisExtent>500){ - delegate = deskGridDelegate; - } - return SliverGrid( - delegate: SliverChildBuilderDelegate( - (_, index) => GestureDetector( - onTap: () => - _toDetailPage(context, state.widgets[index]), - child: CollectWidgetListItem( - data: state.widgets[index], - onDeleteItemClick: (model) => - _deleteCollect(context, model), - )), - childCount: state.widgets.length), - gridDelegate: delegate);}, - )); - } - - _deleteCollect(BuildContext context, WidgetModel model) => - BlocProvider.of(context) - .add(ToggleLikeWidgetEvent(id: model.id)); - - _toDetailPage(BuildContext context, WidgetModel model) { - Navigator.push(context, SlidePageRoute(child: WidgetDetailPageScope(model: model))); - } -} diff --git a/lib/widget_ui/mobile/search_page/search_page.dart b/lib/widget_ui/mobile/search_page/search_page.dart deleted file mode 100644 index bcf16ae59..000000000 --- a/lib/widget_ui/mobile/search_page/search_page.dart +++ /dev/null @@ -1,169 +0,0 @@ -// import 'package:app/app.dart'; -// import 'package:components/components.dart'; -// import 'package:flutter/material.dart'; -// import 'package:flutter_bloc/flutter_bloc.dart'; -// -// // import 'package:old_fancy_mobile_ui/old_fancy_mobile_ui.dart'; -// import 'package:widget_module/blocs/blocs.dart'; -// -// import 'package:widget_repository/widget_repository.dart'; -// import 'app_search_bar.dart'; -// -// -// // SearchPage 可以复用 WidgetsBloc,进行局部的 Bloc -// // 不必单独提供 SearchBloc 增加复杂性 -// class SearchPageProvider extends StatelessWidget { -// const SearchPageProvider({Key? key}) : super(key: key); -// -// @override -// Widget build(BuildContext context) { -// return BlocProvider( -// lazy: false, -// create: (BuildContext context) => WidgetsBloc( -// repository: BlocProvider.of(context).repository, -// ), -// child: const SearchPage(), -// ); -// } -// } -// -// class SearchPage extends StatefulWidget { -// const SearchPage({Key? key}) : super(key: key); -// -// @override -// _SearchPageState createState() => _SearchPageState(); -// } -// -// class _SearchPageState extends State { -// @override -// Widget build(BuildContext context) { -// return Scaffold( -// body: CustomScrollView( -// slivers: [ -// _buildSliverAppBar(), -// SliverToBoxAdapter(child: _buildStarFilter()), -// BlocBuilder(builder: _buildBodyByState) -// ], -// ), -// ); -// } -// -// Widget _buildSliverAppBar() { -// return const SliverAppBar( -// pinned: true, -// title: AppSearchBar(), -// actions: [ -// Padding( -// padding: EdgeInsets.only(right: 15.0), -// child: Icon(TolyIcon.icon_sound), -// ) -// ], -// ); -// } -// -// Widget _buildStarFilter() => Column( -// crossAxisAlignment: CrossAxisAlignment.start, -// children: [ -// Padding( -// padding: const EdgeInsets.only(top: 10.0, left: 20, bottom: 5), -// child: Wrap( -// spacing: 5, -// crossAxisAlignment: WrapCrossAlignment.center, -// children: [ -// const Circle( -// radius: 5, -// color: Colors.orange, -// ), -// Text( -// '星级查询', -// style: TextStyle( -// color: Theme.of(context).primaryColor, -// fontWeight: FontWeight.bold), -// ), -// ], -// ), -// ), -// MultiChipFilter( -// data: const [1, 2, 3, 4, 5], -// avatarBuilder: (_, index) => -// CircleAvatar(child: Text((index + 1).toString())), -// labelBuilder: (_, selected) => Icon( -// Icons.star, -// color: selected ? Colors.blue : Colors.grey, -// size: 18, -// ), -// onChange: _doSelectStart, -// ), -// const Divider(), -// const SizedBox( -// height: 10, -// ) -// ], -// ); -// -// Widget _buildBodyByState(BuildContext context, WidgetsState state) { -// Widget noSearchArg = const SliverToBoxAdapter(child: NotSearchPage()); -// if (state.filter.name.isEmpty) { -// return noSearchArg; -// } -// -// if (state is WidgetsLoading) { -// return const SliverToBoxAdapter(child: LoadingShower()); -// } -// -// if (state is WidgetsLoadFailed) { -// return const SliverToBoxAdapter(child: ErrorPage()); -// } -// -// if (state is WidgetsLoaded) { -// if (state.widgets.isEmpty) { -// return const SliverToBoxAdapter( -// child: EmptyShower( -// message: "没数据,哥也没办法\n(≡ _ ≡)/~┴┴", -// ), -// ); -// } -// return _buildSliverList(state.widgets); -// } -// return const SliverToBoxAdapter(child: NotSearchPage()); -// } -// -// Widget _buildSliverList(List models) => SliverList( -// delegate: SliverChildBuilderDelegate( -// (_, int index) => Padding( -// padding: const EdgeInsets.only( -// bottom: 10, top: 2, left: 10, right: 10), -// child: InkWell( -// customBorder: HomeItemSupport.shapeBorderMap[index], -// onTap: () => _toDetailPage(models[index]), -// child: TechnoWidgetListItem( -// data: models[index], -// ))), -// childCount: models.length), -// ); -// -// void _doSelectStart(List select) { -// List temp = select.map((e) => e + 1).toList(); -// if (temp.length < 5) { -// temp.addAll(List.generate(5 - temp.length, (e) => -1)); -// } -// WidgetsBloc widgetsBloc = BlocProvider.of(context); -// final WidgetFilter filter = widgetsBloc.state.filter.copyWith( -// stars: temp, -// ); -// widgetsBloc.add( -// EventSearchWidget(filter: filter), -// ); -// } -// -// void _toDetailPage(WidgetModel model) { -// //收起键盘 -// final FocusScopeNode focusScope = FocusScope.of(context); -// if (focusScope.hasFocus) { -// focusScope.unfocus(); -// } -// BlocProvider.of(context).queryDetail(model); -// Navigator.pushNamed(context, UnitRouter.widget_detail,arguments: model); -// } -// } -// diff --git a/lib/widget_ui/mobile/search_page/standard_search_bar.dart b/lib/widget_ui/mobile/search_page/standard_search_bar.dart deleted file mode 100644 index 5761a2c85..000000000 --- a/lib/widget_ui/mobile/search_page/standard_search_bar.dart +++ /dev/null @@ -1,103 +0,0 @@ -import 'package:app/app.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:widget_module/blocs/blocs.dart'; -import 'package:widget_repository/widget_repository.dart'; - - - -class StandardSearchBarInner extends StatelessWidget implements PreferredSizeWidget { - - const StandardSearchBarInner({Key? key}) : super(key: key); - - @override - Size get preferredSize => const Size.fromHeight(35 + 8 * 2); - - @override - Widget build(BuildContext context) { - bool isDark = Theme.of(context).brightness == Brightness.dark; - Color? color = Theme.of(context).appBarTheme.backgroundColor; - return Container( - color: isDark?color:Colors.white, - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Row( - children: [ - const SizedBox( - width: 15, - ), - - GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: (){ - Navigator.of(context).maybePop(); - }, - child: const SizedBox( - height: 32, - width: 32, - child: Icon( -Icons.arrow_back - ), - ), - ), - Expanded( - child: Container( - height: 35, - padding: const EdgeInsets.only(left: 10, right: 10), - child: Material( - color: Colors.transparent, - child: TextField( - autofocus: true, - enabled: true, - cursorColor: Colors.blue, - maxLines: 1, - onChanged: (str)=>_doSearch(context,str), - onSubmitted: (str) { - //提交后,收起键盘 - FocusScope.of(context).requestFocus(FocusNode()); - }, - decoration: InputDecoration( - filled: true, - fillColor: isDark?Color(0xff292929):Color(0xffF3F6F9), - - prefixIcon: Icon( - Icons.search, - color: Colors.grey, - ), - border: UnderlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.all(Radius.circular(35 / 2)), - ), - hintText: "搜索组件", - hintStyle: TextStyle(fontSize: 14) - ), - ), - )), - ), - Wrap( - spacing: 3, - alignment: WrapAlignment.center, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - const Icon(TolyIcon.icon_sound), - // Text('已签',style: TextStyle(color: Colors.grey),) - ], - ), - const SizedBox( - width: 15, - ) - ], - ), - - ); - } - - void _doSearch(BuildContext context,String str) { - WidgetsBloc widgetsBloc = BlocProvider.of(context); - final WidgetFilter filter = widgetsBloc.state.filter.copyWith( - name: str, - ); - widgetsBloc.add( - EventSearchWidget(filter: filter), - ); - } -} diff --git a/lib/widget_ui/mobile/search_page/standard_search_page.dart b/lib/widget_ui/mobile/search_page/standard_search_page.dart deleted file mode 100644 index 161206e35..000000000 --- a/lib/widget_ui/mobile/search_page/standard_search_page.dart +++ /dev/null @@ -1,94 +0,0 @@ -import 'package:app/app.dart'; -import 'package:components/components.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -import 'package:widget_module/blocs/blocs.dart'; -import 'package:widget_repository/widget_repository.dart'; - -import '../widget_panel/widget_model_item.dart'; -import 'standard_search_bar.dart'; - -// SearchPage 可以复用 WidgetsBloc,进行局部的 Bloc -// 不必单独提供 SearchBloc 增加复杂性 -class StandardSearchPageProvider extends StatelessWidget { - const StandardSearchPageProvider({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return BlocProvider( - lazy: false, - create: (BuildContext context) => WidgetsBloc( - repository: BlocProvider.of(context).repository, - ), - child: const StandardSearchPage(), - ); - } -} - -class StandardSearchPage extends StatelessWidget { - const StandardSearchPage({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - bool isDark = Theme.of(context).brightness == Brightness.dark; - - return Scaffold( - body: Column( - children: [ - SizedBox( - height: MediaQuery.of(context).padding.top, - width: MediaQuery.of(context).size.width, - child: ColoredBox(color: isDark?Theme.of(context).appBarTheme.backgroundColor??Colors.black:Colors.white), - ), - const StandardSearchBarInner(), - Expanded( - child: BlocBuilder( - builder: _buildBodyByState)) - ], - ), - ); - } - - Widget _buildBodyByState(BuildContext context, WidgetsState state) { - Widget noSearchArg = const NotSearchPage(); - if (state.filter.name.isEmpty) { - return noSearchArg; - } - - if (state is WidgetsLoaded) { - if (state.widgets.isEmpty) { - return const EmptyShower( - message: "没数据,哥也没办法\n(≡ _ ≡)/~┴┴", - ); - } - return ListView.builder( - padding: EdgeInsets.zero, - itemBuilder: (_, index) => StandardWidgetItem( - searchArg: state.filter.name, - model: state.widgets[index], - onTap: () => _toDetail(context, state.widgets[index])), - itemCount: state.widgets.length, - ); - } - - if (state is WidgetsLoading) { - return const LoadingShower(); - } - - if (state is WidgetsLoadFailed) { - return const ErrorPage(); - } - - return noSearchArg; - } - - void _toDetail(BuildContext context, WidgetModel model) { - // BlocProvider.of(context).add(FetchWidgetDetail(model)); - Navigator.pushNamed( - context, - UnitRouter.widget_detail, - arguments: model, - ); - } -} diff --git a/lib/widget_ui/mobile/widget_detail/widget_detail_bar.dart b/lib/widget_ui/mobile/widget_detail/widget_detail_bar.dart deleted file mode 100644 index c34992a1f..000000000 --- a/lib/widget_ui/mobile/widget_detail/widget_detail_bar.dart +++ /dev/null @@ -1,116 +0,0 @@ -import 'dart:math'; - -import 'package:app/app.dart'; -import 'package:components/components.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:components/toly_ui/toly_ui.dart'; -import 'package:utils/utils.dart'; -import 'package:widget_module/blocs/blocs.dart'; -import 'package:widget_repository/widget_repository.dart'; - -class SliverWidgetDetailBar extends StatelessWidget { - final WidgetModel model; - - const SliverWidgetDetailBar({Key? key, required this.model}) - : super(key: key); - - final Color backgroundColor = const Color(0xffFAFAFA); - static const Color textColor = Color(0xff262626); - - @override - Widget build(BuildContext context) { - bool isDark = Theme.of(context).brightness == Brightness.dark; - Color? appBarColor = Theme.of(context).appBarTheme.backgroundColor; - Color? appBarTextColor = Theme.of(context).appBarTheme.titleTextStyle?.color; - - - return SliverAppBar( - pinned: true, - backgroundColor: isDark? appBarColor:backgroundColor, - titleTextStyle: TextStyle(color:isDark?appBarTextColor: textColor), - iconTheme: isDark? null:const IconThemeData(color: textColor), - centerTitle: false, - expandedHeight: 120.0, - scrolledUnderElevation: 0.5, - flexibleSpace: DiyFlexibleSpaceBar( - centerTitle: false, - expandedTitleScale: 2, - titleIconBuilder: (t) => WindmillWidget( - rotate: t * 2 * pi * 2, - radius: 15, - ), - fixedSubtitle: Text( - model.name, - style: TextStyle(color: isDark?appBarTextColor:Color(0xff696969), fontSize: 12), - ), - title: Padding( - padding: const EdgeInsets.only(bottom: 3), - child: Text( - model.nameCN, - style: TextStyle( fontSize: 16,color:isDark?appBarTextColor: textColor), - ), - ), - //伸展处布局 - titlePadding: const EdgeInsets.only(left: 20, bottom: 10), - //标题边距 - collapseMode: CollapseMode.parallax, - ), - elevation: 0, - actions: [ - _buildToHome(context), - FeedbackWidget( - onPressed: () => _toggleLikeState(context), - child: BlocConsumer( - listener: _listenLikeStateChange, - builder: _buildByLikeState, - ), - ) - ], - ); - } - - void _toggleLikeState(BuildContext context) { - BlocProvider.of(context).add( - ToggleLikeWidgetEvent(id: model.id), - ); - } - - // 监听 LikeWidgetBloc 伺机弹出 toast - void _listenLikeStateChange(BuildContext context, LikeWidgetState state) { - bool collected = state.widgets.contains(model); - String msg = - collected ? "收藏【${model.name}】组件成功!" : "已取消【${model.name}】组件收藏!"; - Toast.toast( - context, - msg, - duration: Duration(milliseconds: collected ? 1500 : 600), - action: collected - ? SnackBarAction( - textColor: Colors.white, - label: '收藏夹管理', - onPressed: () => Scaffold.of(context).openEndDrawer()) - : null, - ); - } - - // 根据 [LikeWidgetState ] 构建图标 - Widget _buildByLikeState(BuildContext context, LikeWidgetState state) { - bool liked = state.widgets.contains(model); - return Padding( - padding: const EdgeInsets.only(right: 20.0), - child: Icon( - liked ? TolyIcon.icon_star_ok : TolyIcon.icon_star_add, - size: 25, - ), - ); - } - - Widget _buildToHome(BuildContext context) => GestureDetector( - onLongPress: () => Scaffold.of(context).openEndDrawer(), - child: const Padding( - padding: EdgeInsets.all(15.0), - child: Icon(Icons.home), - ), - onTap: () => Navigator.of(context).pop()); -} diff --git a/lib/widget_ui/mobile/widget_detail/widget_detail_page.dart b/lib/widget_ui/mobile/widget_detail/widget_detail_page.dart deleted file mode 100644 index fc229a03e..000000000 --- a/lib/widget_ui/mobile/widget_detail/widget_detail_page.dart +++ /dev/null @@ -1,120 +0,0 @@ -import 'dart:io'; - -import 'package:app/app.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -import '../../desk_ui/widget_detail/link_widget_buttons.dart'; -import 'widget_node_panel.dart'; -import 'package:widget_module/blocs/blocs.dart'; - -import 'package:widget_repository/widget_repository.dart'; -import 'package:widgets/widgets.dart'; - -import 'category_end_drawer.dart'; -import 'widget_detail_bar.dart'; -import 'widget_detail_panel.dart'; - - -// 用于组件详情不需要在一开始就加载 -// WidgetDetailBloc 可以在稍后提供 -class WidgetDetailPageScope extends StatelessWidget { - final WidgetModel model; - - const WidgetDetailPageScope({super.key, required this.model}); - - @override - Widget build(BuildContext context) { - return BlocProvider( - create: (_) => WidgetDetailBloc( - widgetRepository: const WidgetDbRepository(), - nodeRepository: const NodeDbRepository()) - ..push(model), - child: WidgetDetailPage( - model: model, - ), - ); - } -} - -class WidgetDetailPage extends StatelessWidget { - final WidgetModel model; - - const WidgetDetailPage({Key? key, required this.model}) : super(key: key); - - @override - Widget build(BuildContext context) { - WidgetDetailBloc bloc = context.watch(); - bool isDark = Theme.of(context).brightness == Brightness.dark; - - return Scaffold( - backgroundColor: isDark?Colors.black:Colors.white, - endDrawer: CategoryEndDrawer(widget: bloc.currentWidget), - body: Builder(builder: (ctx) => _buildContent(ctx, bloc)), - ); - } - - Widget get linkText => Row( - children: const [ - Padding( - padding: EdgeInsets.only(left: 15, right: 5), - child: Icon(Icons.link, color: Colors.blue), - ), - Text('相关组件', style: UnitTextStyle.labelBold), - ], - ); - - Widget _buildContent(BuildContext context, WidgetDetailBloc bloc) { - DetailState state = bloc.state; - return WillPopScope( - onWillPop: () => _whenPop(context), - child: CustomScrollView( - slivers: [ - SliverWidgetDetailBar( - model: bloc.currentWidget - ), - SliverToBoxAdapter( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - WidgetDetailPanel(model: bloc.currentWidget), - linkText, - if (state is DetailWithData) - LinkWidgetButtons(links: state.links, onSelect: bloc.push,), - const Divider(), - ], - ), - ), - if (state is DetailWithData) - _buildSliverNodeList(context,state.nodes, state.widgetModel) - ], - )); - } - - Future _whenPop(BuildContext context) async { - // print("======_whenPop============"); - WidgetDetailBloc detailBloc = context.read(); - if (Scaffold.of(context).isEndDrawerOpen) { - return true; - } - return detailBloc.pop(); - } - - - Widget _buildSliverNodeList(BuildContext context,List nodes, WidgetModel model) { - AppState globalState = BlocProvider.of(context).state; - return SliverList( - delegate: SliverChildBuilderDelegate( - (_, i) => WidgetNodePanel( - codeStyle: globalState.codeStyle, - codeFamily: 'Inconsolata', - text: nodes[i].name, - subText: nodes[i].subtitle, - code: nodes[i].code, - death: model.death, - show: WidgetsMap.map(model.name)[i], - ), - childCount: nodes.length, - )); - } -} \ No newline at end of file diff --git a/lib/widget_ui/mobile/widget_detail/widget_detail_panel.dart b/lib/widget_ui/mobile/widget_detail/widget_detail_panel.dart deleted file mode 100644 index f149fbd5b..000000000 --- a/lib/widget_ui/mobile/widget_detail/widget_detail_panel.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'package:components/toly_ui/toly_ui.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_star/flutter_star.dart'; -import 'package:widget_repository/widget_repository.dart'; - -class WidgetDetailPanel extends StatelessWidget { - final WidgetModel model; - - const WidgetDetailPanel({Key? key, required this.model}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - _buildLeft(model, context), - _buildRight(model), - ], - ), - const Divider(), - ], - ); - } - - Widget _buildLeft(WidgetModel model, BuildContext context) => Expanded( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Panel( - color: Theme.of(context).scaffoldBackgroundColor, - child: Text(model.info)), - ), - ); - - Widget _buildRight(WidgetModel model) => Column( - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox( - height: 100, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Hero( - tag: "hero_widget_image_${model.id}", - child: ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(8)), - child: model.image == null - ? Image.asset('assets/images/caver.webp') - : Image(image: model.image!))), - ), - ), - StarScore( - score: model.lever, - star: const Star(size: 15, fillColor: Colors.blue), - ) - ], - ); -} diff --git a/lib/widget_ui/mobile/widget_detail/widget_node_panel.dart b/lib/widget_ui/mobile/widget_detail/widget_node_panel.dart deleted file mode 100644 index cf46fd0a3..000000000 --- a/lib/widget_ui/mobile/widget_detail/widget_node_panel.dart +++ /dev/null @@ -1,152 +0,0 @@ -import 'package:app/app.dart'; -import 'package:components/toly_ui/toly_ui.dart'; -import 'package:flutter/material.dart'; -import 'package:share_plus/share_plus.dart'; -import 'package:toggle_rotate/toggle_rotate.dart'; - - -/// create by 张风捷特烈 on 2020-04-13 -/// contact me by email 1981462002@qq.com -/// 说明: 一个Widget的知识点对应的界面 - -class WidgetNodePanel extends StatefulWidget { - final String text; - final String subText; - final String code; - final Widget? show; - final HighlighterStyle? codeStyle; - final String? codeFamily; - final bool death; - - const WidgetNodePanel( - {Key? key, this.text='', - this.subText='', - this.code='', - this.death=false, - this.show, - required this.codeStyle, - this.codeFamily}) : super(key: key); - - @override - _WidgetNodePanelState createState() => _WidgetNodePanelState(); -} - -class _WidgetNodePanelState extends State { - CrossFadeState _crossFadeState = CrossFadeState.showFirst; - - bool get isFirst => _crossFadeState == CrossFadeState.showFirst; - - Color get themeColor => Theme.of(context).primaryColor; - - @override - Widget build(BuildContext context) { - return Container( - margin: const EdgeInsets.all(10), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - buildNodeTitle(), - const SizedBox( - height: 20, - ), - _buildCode(context), - Padding( - padding: const EdgeInsets.only(top: 10, bottom: 20), - child: widget.show, - ), - if(!widget.death) - _buildNodeInfo(), - const Divider(), - ], - ), - ); - } - - Widget buildNodeTitle() => Row( - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8), - child: Circle( - color: themeColor, - radius: 5, - ), - ), - Expanded( - child: Text( - widget.text, - style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 15), - ), - ), - _buildShareButton(), - _buildCodeButton() - ], - ); - - Widget _buildNodeInfo() => SizedBox( - width: double.infinity, - child: Panel( - color: Theme.of(context).scaffoldBackgroundColor, - child: Text( - widget.subText, - style: const TextStyle(fontSize: 12), - )), - ); - - Widget _buildCodeButton() => Padding( - padding: const EdgeInsets.only(right: 10.0), - child: ToggleRotate( - durationMs: 300, - child: Icon( - TolyIcon.icon_code, - color: themeColor, - ), - onTap: _toggleCodePanel, - ), - ); - - Widget _buildShareButton() => FeedbackWidget( - mode: FeedMode.fade, - a: 0.4, - onPressed: _doShare, - child: Padding( - padding: const EdgeInsets.only( - right: 10, - ), - child: Icon( - TolyIcon.icon_share, - size: 20, - color: themeColor, - ), - ), - ); - - Widget _buildCode(BuildContext context) => AnimatedCrossFade( - firstCurve: Curves.easeInCirc, - secondCurve: Curves.easeInToLinear, - firstChild: const SizedBox(), - secondChild: SizedBox( - width: MediaQuery.of(context).size.width, - child: CodeWidget( - fontFamily: widget.codeFamily, - code: isFirst?'':widget.code, - style: widget.codeStyle ?? - HighlighterStyle.fromColors(HighlighterStyle.lightColor), - ), - ), - duration: const Duration(milliseconds: 200), - crossFadeState: _crossFadeState, - ); - - //执行分享 - _doShare() { - Share.share(widget.code); - } - - // 折叠代码面板 - _toggleCodePanel() { - setState(() { - _crossFadeState = - !isFirst ? CrossFadeState.showFirst : CrossFadeState.showSecond; - }); - } -} diff --git a/lib/widget_ui/mobile/widget_panel/phone_widget_content.dart b/lib/widget_ui/mobile/widget_panel/phone_widget_content.dart deleted file mode 100644 index 7d1f7a1d2..000000000 --- a/lib/widget_ui/mobile/widget_panel/phone_widget_content.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:app/app.dart'; -import 'package:flutter/material.dart'; -import 'package:widget_repository/widget_repository.dart'; - -import 'widget_model_item.dart'; - -class PhoneWidgetContent extends StatelessWidget { - final List items; - - const PhoneWidgetContent({Key? key,required this.items}) : super(key: key); - - @override - Widget build(BuildContext context) { - return SliverList( - delegate: SliverChildBuilderDelegate( - (_, int index) => StandardWidgetItem( - model: items[index], - onTap: () => _toDetail(context, items[index]), - ), - childCount: items.length, - ), - ); - } - - void _toDetail(BuildContext context, WidgetModel model) { - Navigator.pushNamed( - context, - UnitRouter.widget_detail, - arguments: model, - ); - } -} diff --git a/lib/widget_ui/mobile/widget_panel/standard_home_page.dart b/lib/widget_ui/mobile/widget_panel/standard_home_page.dart deleted file mode 100644 index d2cea77f6..000000000 --- a/lib/widget_ui/mobile/widget_panel/standard_home_page.dart +++ /dev/null @@ -1,116 +0,0 @@ -import 'package:components/toly_ui/toly_ui.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/app/navigation/home_drawer.dart'; -import 'package:flutter_unit/app/utils/convert.dart'; -import 'package:widget_module/blocs/blocs.dart'; -import 'package:widget_repository/widget_repository.dart'; - -import 'standard_home_search.dart'; -import 'widget_page.dart'; - -class StandardHomePage extends StatefulWidget { - const StandardHomePage({Key? key}) : super(key: key); - - @override - State createState() => _StandardHomePageState(); -} - -class _StandardHomePageState extends State - with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin { - static const List _tabs = ['无态', '有态', '单渲', '多渲', '滑片', '代理', '其它']; - late TabController tabController; - - @override - void initState() { - super.initState(); - tabController = TabController(length: _tabs.length, vsync: this); - } - - int maxCount = 60; - - @override - void dispose() { - tabController.dispose(); - super.dispose(); - } - - void _switchTab(int index) { - WidgetFamily widgetFamily = Convert.toFamily(index); - WidgetsBloc bloc = BlocProvider.of(context); - if (bloc.state.filter.family == widgetFamily) return; - PrimaryScrollController.of(context).jumpTo(0); - BlocProvider.of(context).add(EventTabTap(widgetFamily)); - } - - @override - Widget build(BuildContext context) { - super.build(context); - final AppBarTheme appBarTheme = AppBarTheme.of(context); - bool isDark = Theme.of(context).brightness == Brightness.dark; - double bottom = MediaQuery.of(context).padding.bottom; - return Scaffold( - extendBody: true, - // backgroundColor: const Color(0xffF3F4F6), - drawer: const HomeDrawer(), - body: Column( - children: [ - AnnotatedRegion( - value: appBarTheme.systemOverlayStyle!, - child: Container( - color: isDark ? Colors.black : Colors.white, - height: MediaQuery.of(context).padding.top, - ), - ), - Expanded( - child: NestedScrollView( - floatHeaderSlivers: true, - headerSliverBuilder: _buildHeader, - body: WidgetPage()), - ), - SizedBox( - height: bottom, - ) - ], - ), - ); - } - - List _buildHeader(BuildContext context, bool innerBoxIsScrolled) { - Color themeColor = Theme.of(context).primaryColor; - bool isDark = Theme.of(context).brightness == Brightness.dark; - - return [ - const SliverSnapHeader(child: StandardHomeSearch()), - // SliverOverlapAbsorber( - // sliver: - SliverPinnedHeader( - color: isDark ? Colors.black : Colors.white, - child: TabBar( - onTap: _switchTab, - indicatorSize: TabBarIndicatorSize.label, - isScrollable: true, - indicator: RoundRectTabIndicator( - borderSide: BorderSide(color: themeColor, width: 3), - ), - labelStyle: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - ), - controller: tabController, - labelColor: themeColor, - indicatorWeight: 3, - unselectedLabelColor: Colors.grey, - indicatorColor: themeColor, - tabs: _tabs.map((String name) => Tab(text: name)).toList(), - )), - // handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), - // ), - ]; - } - - @override - bool get wantKeepAlive => true; -} diff --git a/lib/widget_ui/mobile/widget_panel/standard_home_search.dart b/lib/widget_ui/mobile/widget_panel/standard_home_search.dart deleted file mode 100644 index 272c04a2b..000000000 --- a/lib/widget_ui/mobile/widget_panel/standard_home_search.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'package:app/app.dart'; -import 'package:flutter/material.dart'; -import 'package:components/toly_ui/toly_ui.dart'; - -import '../search_page/standard_search_page.dart'; - -class StandardHomeSearch extends StatelessWidget implements PreferredSizeWidget { - const StandardHomeSearch({Key? key}) : super(key: key); - - @override - Size get preferredSize => const Size.fromHeight(35 + 8 * 2); - - @override - Widget build(BuildContext context) { - bool isDark = Theme.of(context).brightness == Brightness.dark; - - return Container( - color: isDark?null:Colors.white, - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Row( - children: [ - const SizedBox(width: 15), - FeedbackWidget( - onPressed: () => _openDrawer(context), - child: const CircleAvatar( - radius: 16, - backgroundImage: AssetImage('assets/images/icon_head.webp'), - ), - ), - Expanded( - child: GestureDetector( - onTap: () { - Navigator.of(context).push( - FadePageRoute(child: const StandardSearchPageProvider())); - }, - child: Container( - height: 35, - padding: const EdgeInsets.only(left: 10, right: 10), - child: Material( - color: Colors.transparent, - child: TextField( - autofocus: false, - enabled: false, - cursorColor: Colors.blue, - maxLines: 1, - decoration: InputDecoration( - filled: true, - fillColor: isDark?Color(0xff292929):Color(0xffF3F6F9), - prefixIcon: Icon( - Icons.search, - color: Colors.grey, - ), - border: UnderlineInputBorder( - borderSide: BorderSide.none, - borderRadius: - BorderRadius.all(Radius.circular(35 / 2)), - ), - hintText: "搜索组件", - hintStyle: TextStyle(fontSize: 14)), - ), - )), - ), - ), - const Icon(TolyIcon.icon_sound), - const SizedBox(width: 15) - ], - ), - ); - } - - void _openDrawer(BuildContext context) { - Scaffold.of(context).openDrawer(); - } -} diff --git a/lib/widget_ui/mobile/widget_panel/widget_model_item.dart b/lib/widget_ui/mobile/widget_panel/widget_model_item.dart deleted file mode 100644 index c701ee297..000000000 --- a/lib/widget_ui/mobile/widget_panel/widget_model_item.dart +++ /dev/null @@ -1,178 +0,0 @@ -import 'package:app/app.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_star/flutter_star.dart'; -import 'package:widget_module/blocs/blocs.dart'; - -import 'package:widget_repository/widget_repository.dart'; -import 'package:wrapper/wrapper.dart'; - -class StandardWidgetItem extends StatelessWidget { - final WidgetModel model; - final VoidCallback onTap; - final String? searchArg; - - const StandardWidgetItem( - {Key? key, required this.model, required this.onTap, this.searchArg}) - : super(key: key); - - @override - Widget build(BuildContext context) { - Color? tileColor = Theme.of(context).listTileTheme.tileColor; - Color? textColor = Theme.of(context).listTileTheme.textColor; - bool isDark = Theme.of(context).brightness == Brightness.dark; - textColor = isDark?textColor: Color(0xff2F3032); - - return Padding( - padding: const EdgeInsets.only(top: 10), - child: InkWell( - onTap: onTap, - child: Ink( - color: tileColor, - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12), - // margin: - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - _buildTitle(Theme.of(context).primaryColor,textColor,isDark), - _buildContent(textColor), - _buildFoot(isDark) - ], - ), - ), - ), - ); - } - - Widget _buildTitle(Color color,Color? textColor,bool isDark) { - Widget text ; - if(searchArg==null){ - text = Text( - model.name, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: 16, - color: textColor, - fontWeight: FontWeight.bold, - ), - ); - }else{ - text = Text.rich(formSpan(model.name, searchArg!)); - } - - - return Row( - children: [ - Expanded(child: Wrap( - children: [ - text, - const SizedBox( - width: 8, - ), - BlocBuilder(builder: (_, s) { - bool show = s.widgets.contains(model); - return Opacity( - opacity: show ? 1.0 : 0.0, - child: Wrapper.just( - radius: 10, - color: isDark? Color(0xff292A2D):const Color(0xffF3F3F5), - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), - child: Text( - '已收藏', - style: TextStyle( - color: color, - height: 1, - fontSize: 10, - shadows: [ - Shadow( - color:isDark? Colors.black: Colors.white, - blurRadius: 2, - offset: Offset(1, 1)) - ]), - ), - ), - ); - }), - ], - )), - - StarScore( - star: Star(emptyColor: Colors.white, size: 12, fillColor: color), - score: model.lever, - ), - ], - ); - } - - Widget _buildContent(Color? textColor) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 6), - child: Text( - model.info, - maxLines: 2, - overflow: TextOverflow.ellipsis, - style: TextStyle(fontSize: 14, color: textColor), - ), - ); - } - - Widget _buildFoot(bool isDark) { - - return Row( - children: [ - Container( - width: 4, - height: 4, - margin: const EdgeInsets.only(right: 6), - decoration: const BoxDecoration( - color: Color(0xff86909c), shape: BoxShape.circle), - ), - Text( - model.nameCN, - style: const TextStyle( - fontSize: 12, height: 1, color: Color(0xff86909c)), - ), - const Spacer(), - Wrapper.just( - radius: 2, - color: isDark? Color(0xff292A2D):const Color(0xffF3F3F5), - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), - child: Text( - Cons.kWidgetFamilyLabelMap[model.family]!, - style: TextStyle( - color: isDark?Color(0xffCCCCCC):Color(0xff878D96), - height: 1, - fontSize: 12, - shadows: [ - Shadow( - color: isDark? Colors.black:Colors.white, blurRadius: 2, offset: Offset(1, 1)) - ]), - ), - ), - ], - ); - } - - final TextStyle lightTextStyle = const TextStyle( - color: Colors.red, - fontSize: 16, - fontWeight: FontWeight.bold, - ); - - InlineSpan formSpan(String src, String pattern) { - List span = []; - RegExp regExp = RegExp(pattern, caseSensitive: false); - src.splitMapJoin(regExp, onMatch: (Match match) { - span.add(TextSpan(text: match.group(0), style: lightTextStyle)); - return ''; - }, onNonMatch: (str) { - span.add(TextSpan( - text: str, - style: lightTextStyle.copyWith(color: const Color(0xff2F3032)))); - return ''; - }); - return TextSpan(children: span); - } -} diff --git a/lib/widget_ui/widget_ui.dart b/lib/widget_ui/widget_ui.dart deleted file mode 100644 index 3e16562b4..000000000 --- a/lib/widget_ui/widget_ui.dart +++ /dev/null @@ -1,8 +0,0 @@ -// export '../widget_system/views/widget_detail_page/widget_detail_page.dart'; -// export '../widget_system/views/category_view/category_page.dart'; -// export '../widget_system/views/category_view/collect_page.dart'; -// export '../widget_system/views/category_view/home_right_drawer.dart'; -// export '../widget_system/views/category_view/category_detail.dart'; -// export '../widget_system/views/search_view/search_page.dart'; - - diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 0cacc7508..faea87d4e 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -6,14 +6,14 @@ #include "generated_plugin_registrant.h" -#include +#include #include #include void fl_register_plugins(FlPluginRegistry* registry) { - g_autoptr(FlPluginRegistrar) screen_retriever_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin"); - screen_retriever_plugin_register_with_registrar(screen_retriever_registrar); + g_autoptr(FlPluginRegistrar) screen_retriever_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverLinuxPlugin"); + screen_retriever_linux_plugin_register_with_registrar(screen_retriever_linux_registrar); g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 62f151fd7..4f427dd2a 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,7 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST - screen_retriever + screen_retriever_linux url_launcher_linux window_manager ) diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 56911c68f..ab2f2a3fc 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,24 +5,26 @@ import FlutterMacOS import Foundation -import connectivity_plus +import file_picker import package_info_plus import path_provider_foundation -import screen_retriever +import screen_retriever_macos import share_plus import shared_preferences_foundation -import sqflite +import sqflite_darwin import url_launcher_macos +import webview_flutter_wkwebview import window_manager func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { - ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) - FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) + FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin")) + FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) - ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin")) + ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverMacosPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) + WebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "WebViewFlutterPlugin")) WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin")) } diff --git a/macos/Podfile.lock b/macos/Podfile.lock index a584e8ac2..6fea947e4 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -1,73 +1,92 @@ PODS: + - file_picker (0.0.1): + - FlutterMacOS - FlutterMacOS (1.0.0) - - FMDB (2.7.5): - - FMDB/standard (= 2.7.5) - - FMDB/standard (2.7.5) + - open_file_mac (0.0.1): + - FlutterMacOS - package_info_plus (0.0.1): - FlutterMacOS - - path_provider_macos (0.0.1): + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS + - quill_native_bridge_macos (0.0.1): - FlutterMacOS - - screen_retriever (0.0.1): + - screen_retriever_macos (0.0.1): - FlutterMacOS - share_plus (0.0.1): - FlutterMacOS - - shared_preferences_macos (0.0.1): + - shared_preferences_foundation (0.0.1): + - Flutter - FlutterMacOS - - sqflite (0.0.2): + - sqflite_darwin (0.0.4): + - Flutter - FlutterMacOS - - FMDB (>= 2.7.5) - url_launcher_macos (0.0.1): - FlutterMacOS + - webview_flutter_wkwebview (0.0.1): + - Flutter + - FlutterMacOS - window_manager (0.2.0): - FlutterMacOS DEPENDENCIES: + - file_picker (from `Flutter/ephemeral/.symlinks/plugins/file_picker/macos`) - FlutterMacOS (from `Flutter/ephemeral`) + - open_file_mac (from `Flutter/ephemeral/.symlinks/plugins/open_file_mac/macos`) - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) - - path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`) - - screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos`) + - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) + - quill_native_bridge_macos (from `Flutter/ephemeral/.symlinks/plugins/quill_native_bridge_macos/macos`) + - screen_retriever_macos (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever_macos/macos`) - share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`) - - shared_preferences_macos (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos`) - - sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/macos`) + - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) + - sqflite_darwin (from `Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin`) - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) + - webview_flutter_wkwebview (from `Flutter/ephemeral/.symlinks/plugins/webview_flutter_wkwebview/darwin`) - window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`) -SPEC REPOS: - trunk: - - FMDB - EXTERNAL SOURCES: + file_picker: + :path: Flutter/ephemeral/.symlinks/plugins/file_picker/macos FlutterMacOS: :path: Flutter/ephemeral + open_file_mac: + :path: Flutter/ephemeral/.symlinks/plugins/open_file_mac/macos package_info_plus: :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos - path_provider_macos: - :path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos - screen_retriever: - :path: Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos + path_provider_foundation: + :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin + quill_native_bridge_macos: + :path: Flutter/ephemeral/.symlinks/plugins/quill_native_bridge_macos/macos + screen_retriever_macos: + :path: Flutter/ephemeral/.symlinks/plugins/screen_retriever_macos/macos share_plus: :path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos - shared_preferences_macos: - :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos - sqflite: - :path: Flutter/ephemeral/.symlinks/plugins/sqflite/macos + shared_preferences_foundation: + :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin + sqflite_darwin: + :path: Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin url_launcher_macos: :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos + webview_flutter_wkwebview: + :path: Flutter/ephemeral/.symlinks/plugins/webview_flutter_wkwebview/darwin window_manager: :path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos SPEC CHECKSUMS: + file_picker: e716a70a9fe5fd9e09ebc922d7541464289443af FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 - FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a - package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce - path_provider_macos: 3c0c3b4b0d4a76d2bf989a913c2de869c5641a19 - screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38 - share_plus: 76dd39142738f7a68dd57b05093b5e8193f220f7 - shared_preferences_macos: a64dc611287ed6cbe28fd1297898db1336975727 - sqflite: a5789cceda41d54d23f31d6de539d65bb14100ea - url_launcher_macos: 597e05b8e514239626bcf4a850fcf9ef5c856ec3 + open_file_mac: 0e554648e2a87ce59e9438e3e5ca3e552e90d89a + package_info_plus: 12f1c5c2cfe8727ca46cbd0b26677728972d9a5b + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + quill_native_bridge_macos: 3a5f378bc757eb92825193853020b759b56cbb2c + screen_retriever_macos: 776e0fa5d42c6163d2bf772d22478df4b302b161 + share_plus: 1fa619de8392a4398bfaf176d441853922614e89 + shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 + sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d + url_launcher_macos: c82c93949963e55b228a30115bd219499a6fe404 + webview_flutter_wkwebview: a4af96a051138e28e29f60101d094683b9f82188 window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8 PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7 -COCOAPODS: 1.12.0 +COCOAPODS: 1.14.3 diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index a618c6bd4..a19f9ac58 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -204,7 +204,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 33CC10EC2044A3C60003C045 = { diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 7c999745b..46ae587f6 100644 --- a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ diff --git a/macos/Runner/AppDelegate.swift b/macos/Runner/AppDelegate.swift index d53ef6437..b3c176141 100644 --- a/macos/Runner/AppDelegate.swift +++ b/macos/Runner/AppDelegate.swift @@ -1,9 +1,13 @@ import Cocoa import FlutterMacOS -@NSApplicationMain +@main class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } } diff --git a/modules/ability/r_upgrade-0.4.2/CHANGELOG.md b/modules/ability/r_upgrade-0.4.2/CHANGELOG.md new file mode 100644 index 000000000..c5c063984 --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/CHANGELOG.md @@ -0,0 +1,80 @@ +## 0.4.2 +* fix issues(#76) support android 13. +## 0.4.1 +* fix issues(#71) +## 0.4.0 +* add silent install, fix google play release issues. +## 0.3.9 +* fixed download file not exist error. +## 0.3.8+2 +* fixed download file not exist error. +## 0.3.8+1 +* fixed [installByPath] error. +## 0.3.8 +* fixed get download header error,big file adapter,add [installByPath] method +## 0.3.7+3 +* fixed Are you missing a call to unregisterReceiver(). +## 0.3.7+2 +* fixed android 31 notification problem. +## 0.3.7+1 +* Class [DownloadInfo] add [getSpeedString] method , change [LICENSE] to Apache License 2.0. +## 0.3.7 +* fixed android compile SDK version = 31 crash error,upgrade version click cancel crash error、 +## 0.3.6 +* add android platform [androidStores]、[getVersionFromAndroidStore] methods. +## 0.3.5 +* fix ios if not exist appId will crash error,add isChina params to [getVersionFrom]、[upgradeFromAppStore] method. +## 0.3.4 +* fix 301/302 download error, larger then Android N network disconnect turn to connect download recovery. +## 0.3.3 +* adapter null safety. +## 0.3.2+3 +* adapter android Q. +## 0.3.2+2 +* fix issues #23 again. +## 0.3.2+1 +* fix issues #23. +## 0.3.2 +* Repair the installation of two applications using the plug-in on the same device, resulting in two different but synchronous progress problems in the application, and the download failure occurred during the repair pause. +## 0.3.1+2 +* Improve the download process and repair the problem that incremental update files do not exist. +## 0.3.1+1 +* fix [#17](https://github.com/rhymelph/r_upgrade/issues/17) +## 0.3.1 +* add `Android` permission check, when you use `upgrade` or `install`. +## 0.3.0 +* remove `hotUpgrade` method,add increment upgrade.edit `upgrade` method. +## 0.2.6 +* fix mini bug ,and change notification channel name ,fix example hot upgrade error. +## 0.2.5 +* add `notificationStyle` params from `upgrade` method. +## 0.2.4 +* add `upgradeFromAndroidStore` method. +## 0.2.2 +* fix not use stream can not open notification,update `upgradeWithId` method. +## 0.2.1 +* fix Android 6.0 install error. +## 0.2.0 +* will power and awesome . +## 0.1.8 +* adapter FlutterPlugin . +## 0.1.7 +* add hot upgrade. +## 0.1.6 +* fix file provider merge error and add can select auto apk install. +## 0.1.5 +* add license. +## 0.1.4 +* fix [issues/2](https://github.com/rhymelph/r_upgrade/issues/2#issue-524088878) +## 0.1.3 +* fix progress will decrement. +## 0.1.2 +* fix progress is null and speed is negative issus. +## 0.1.1 +* add ios upgrade from AppStore. +## 0.1.0 +* fix install apk. +## 0.0.2 +* Improving health. +## 0.0.1 +* release plugin. \ No newline at end of file diff --git a/modules/ability/r_upgrade-0.4.2/LICENSE b/modules/ability/r_upgrade-0.4.2/LICENSE new file mode 100644 index 000000000..895657b9a --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/LICENSE @@ -0,0 +1,174 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. \ No newline at end of file diff --git a/modules/ability/r_upgrade-0.4.2/README.md b/modules/ability/r_upgrade-0.4.2/README.md new file mode 100644 index 000000000..a2aadf613 --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/README.md @@ -0,0 +1,364 @@ +# r_upgrade +[![pub package](https://img.shields.io/pub/v/r_upgrade.svg)](https://pub.dartlang.org/packages/r_upgrade) + +![](screen/r_upgrade.png) + +## [中文点此](README_CN.md) + +Android and IOS upgrade plugin. + +- [✔] Jump link mode upgrade +- [✔] `Android` Get user installed android stores. +- [✔] `Android` Get version from android stores(only support GooglePlay、XiaoMi、Tencent) +- [✔] `Android` Jump to store mode upgrade +- [✔] `Android` Download APK using download link + - [✔] Monitor download information + - [✔] cancel/pause/continue download + - [✔] Get download status according to ID + - [✔] Install app according to ID + - [✔] Get the last download ID (based on the version name and version number) + - [✔] Modify the information displayed in the notification bar +- [✔] `Android` hot upgrade +- [✔] `Android` increment upgrade +- [✔] `IOS` Jump to Appstore upgrade according to appid +- [✔] `IOS` Get the current online version of Appstore according to appid + +> For the development of this plug-in, I haven't had a good meal for a long time. I hope you can click on the sponsor and give a little bit of money. Thank you! + +## List +- [r_upgrade](#r_upgrade) + - [中文点此](#中文点此) + - [List](#list) + - [Getting Started](#getting-started) + - [1. Use Plugin:](#1-use-plugin) + - [2. Upgrade from your website ( Android or IOS )](#2-upgrade-from-your-website--android-or-ios-) + - [Android Platform](#android-platform) + - [1.Get android store list.](#1get-android-store-list) + - [2.Get version from android store.](#2get-version-from-android-store) + - [3. App upgrade from store.](#3-app-upgrade-from-store) + - [4. App upgrade from download link.](#4-app-upgrade-from-download-link) + - [1. Add Upgrade Download Listener](#1-add-upgrade-download-listener) + - [2. Upgrade your application](#2-upgrade-your-application) + - [3. Cancel Download](#3-cancel-download) + - [4. Install Apk](#4-install-apk) + - [5. Pause Download](#5-pause-download) + - [6. Continue Download](#6-continue-download) + - [7. Get the last upgrade id](#7-get-the-last-upgrade-id) + - [8. Get the download status from id](#8-get-the-download-status-from-id) + - [9. Increment Upgrade](#9-increment-upgrade) + - [10. Hot Upgrade](#10-hot-upgrade) + - [Android Platform Notification Bar](#android-platform-notification-bar) + - [IOS Platform](#ios-platform) + - [1.Go to the AppStore Upgrade](#1go-to-the-appstore-upgrade) + - [2.Get the last version form AppStore](#2get-the-last-version-form-appstore) + - [LICENSE](#license) + + +## Getting Started + +### 1. Use Plugin: +- add this code in `pubspec.yaml` + +```yaml +dependencies: + r_upgrade: last version +``` + +### 2. Upgrade from your website ( Android or IOS ) +```dart + void upgradeFromUrl()async{ + bool isSuccess =await RUpgrade.upgradeFromUrl( + 'https://www.google.com', + ); + print(isSuccess); + } +``` + +## Android Platform + +### 1.Get android store list. +```dart + void getAndroidStores() async { + final stores = await RUpgrade.androidStores; + } +``` + +### 2.Get version from android store. +```dart + void getVersionName() async { + final versionName = await RUpgrade.getVersionFromAndroidStore(AndroidStore.GOOGLE_PLAY); + } +``` + +### 3. App upgrade from store. +```dart + void upgradeFromAndroidStore(){ + bool isSuccess = await RUpgrade.upgradeFromAndroidStore(AndroidStore.GOOGLE_PLAY); + print('${isSuccess?'jump success':'jump error'}'); + } +``` + +### 4. App upgrade from download link. + +> make sure your application had this permission and request dynamic permission. + +```xml + + + + + + + +``` + +#### 1. Add Upgrade Download Listener +```dart +RUpgrade.stream.listen((DownloadInfo info){ + ///... +}); +``` +info: + +| param | desc | +| - | - | +| (int) id | download id | +| (int) max_length | download max bytes length (bytes) | +| (int) current_length | download current bytes length (bytes) | +| (double) percent | download percent 0-100 | +| (double) planTime | download plan time /s (X.toStringAsFixed(0)) | +| (String) path | download file path | +| (double) speed | download speed kb/s | +| (DownloadStatus) status | download status
`STATUS_PAUSED`
`STATUS_PENDING`
`STATUS_RUNNING`
`STATUS_SUCCESSFUL`
`STATUS_FAILED`
`STATUS_CANCEL`| + +#### 2. Upgrade your application +This upgrade have two part. +`useDownloadManager`: +- `true`: Use system `DownloadManager`to download + - advantage:Simple, use system. + - Inferiority:can not use http download , can not click the notification pause downloading, can not pause and continue download by network status etc... + - support: `RUpgrade.stream`、`install`、`cancel` +- `false`: Use `Service` download(default use) + - advantage:Power, support http/https download, support auto pause and continue download by network status etc.. + - Inferiority:No bugs found yet. If you find a bug, you are welcome to issue + - support: `RUpgrade.stream`、`install`、`cancel` +```dart + // [installType] downloaded finish will use install type to install apk. + // [apkName] apk name (such as `release.apk`) + // [notificationVisibility] notification visibility. + // [notificationStyle] download notification show style about content text, only support [useDownloadManager]==false. + // [useDownloadManager] if true will use DownloadManager,false will use my service , + // if true will no use [pause] , [upgradeWithId] , [getDownloadStatus] , [getLastUpgradedId] methods. + // [upgradeFlavor] you can use [RUpgradeFlavor.normal] , [RUpgradeFlavor.hotUpgrade] , [RUpgradeFlavor.incrementUpgrade] flavor + void upgrade() async { + int id = await RUpgrade.upgrade( + 'https://raw.githubusercontent.com/rhymelph/r_upgrade/master/apk/app-release.apk', + apkName: 'app-release.apk', installType: RUpgradeInstallType.normal,); + } +``` +New upgraded flavor:(no support use DownloadManager) +```dart +enum RUpgradeFlavor { + normal, // full upgrade + hotUpgrade, // hot upgrade + incrementUpgrade, // increment upgrade +} +``` + +#### 3. Cancel Download +```dart + void cancel() async { + bool isSuccess=await RUpgrade.cancel(id); + } +``` + +#### 4. Install Apk + +- use download id install +```dart + void install() async { + bool isSuccess=await RUpgrade.install(id); + } +``` +- use file path install +```dart + void installByPath(String path) async { + bool isSuccess=await RUpgrade.installByPath(path); + } +``` +- install type +```dart +/// [RUpgrade.upgradeWithId]、[RUpgrade.upgrade]、[RUpgrade.install]、[RUpgrade.installByPath] +enum RUpgradeInstallType { + normal,//normal install + silent,//silent install + none,// not install +} +``` + +#### 5. Pause Download +```dart + void pause() async { + bool isSuccess=await RUpgrade.pause(id); + } +``` + +#### 6. Continue Download +```dart + void pause() async { + bool isSuccess=await RUpgrade.upgradeWithId(id); + /// return true. + /// * if download status is [STATUS_PAUSED] or [STATUS_FAILED] or [STATUS_CANCEL], will restart running. + /// * if download status is [STATUS_RUNNING] or [STATUS_PENDING], nothing happened. + /// * if download status is [STATUS_SUCCESSFUL] , will install apk. + /// + /// return false. + /// * if not found the id , will return [false]. + } +``` + +#### 7. Get the last upgrade id +```dart + void getLastUpgradeId() async { + int id = await RUpgrade.getLastUpgradedId(); + } +``` + +#### 8. Get the download status from id +```dart + void getDownloadStatus()async{ + DownloadStatus status = await RUpgrade.getDownloadStatus(id); + } +``` +#### 9. Increment Upgrade +- 1.Download [bsdiff](https://github.com/rhymelph/r_upgrade/releases/download/v0.3.0/bsdiff) to local. +- 2.Prepare two installation packages, one is the one to be upgraded( old.apk ), an installation package that you need to update( new.apk ) +- 3.Switch to the 'bsdiff' directory downloaded above on the command line, and run the command`./bsdiff old.apk new.apk increment.patch` +- 4.Put the` increment.patch `Upload to server +- 5.use `RUpgrade.upgrade(...,upgradeFlavor:RUpgradeFlavor.incrementUpgrade)`download file +- 6.use `RUpgrade.install(id)` install apk. + +The code is as follows: +```dart + int id; + void incrementUpgrade(){ + id = await RUpgrade.upgrade( + 'https://mydata-1252536312.cos.ap-guangzhou.myqcloud.com/r_upgrade.patch', + fileName: 'r_upgrade.patch', + useDownloadManager: false, + installType: RUpgradeInstallType.none, + upgradeFlavor: RUpgradeFlavor.incrementUpgrade, + ); + } + + void install(){ + try { + await RUpgrade.install(id); + } catch (e) { + _state.currentState + .showSnackBar(SnackBar(content: Text('failure!'))); + } + } +``` + +#### 10. Hot Upgrade +- you can use this id to hot upgrade,but download file is zip. include three file [isolate_snapshot_data]、[kernel_blob.bin]、[vm_snapshot_data].Your can use `flutter build bundle` generate. +``` + flutter build bundle +``` + +- generate file path form ./build/flutter_assets and packaged into zip. + +``` +|- AssetManifest.json +|- FontManifest.json +|- fonts + |- ... +|- isolate_snapshot_data * +|- kernel-blob.bin * +|- LICENSE +|- packages + |- ... +|- vm_snapshot_data * +``` + +- use `RUpgrade.upgrade(...,upgradeFlavor:RUpgradeFlavor.hotUpgrade)`download file. +- download complete you can use download `id` to hot upgrade + +```dart + bool isSuccess = await RUpgrade.install(id); + if (isSuccess) { + _state.currentState + .showSnackBar(SnackBar(content: Text('Hot update succeeded, exit the application after 3S, please enter again'))); + Future.delayed(Duration(seconds: 3)).then((_){ + SystemNavigator.pop(animated: true); + }); + }else{ + _state.currentState + .showSnackBar(SnackBar(content: Text('Hot update failed, please wait for update package download to complete'))); + } +``` +> At present, the hot update is still in the testing stage, only supporting the change of the flutter code, not supporting the resource file, etc. the author of the plug-in is not responsible for all the consequences caused by the hot update, and the user is responsible for it. + +## Android Platform Notification Bar +If you want to customize the content displayed in the download notification bar, you can do so, modify or add files `project/android/app/main/res/values/r_upgrade_value.xml`,add the following code +```xml + + + %.0f kb/s + %.0fs left + Download finished + Download paused + Download failed + +``` +And then.When you use `upgrade` method,you should set the `notificationStyle` param. +```dart +/// Notification show style about content text +enum NotificationStyle { + speechAndPlanTime, // 100kb/s 1s left + planTimeAndSpeech, // 1s left 100kb/s + speech,// 100kb/s + planTime, // 1s left + none, // +} +``` + +## IOS Platform + +### 1.Go to the AppStore Upgrade +```dart + void upgradeFromAppStore() async { + bool isSuccess =await RUpgrade.upgradeFromAppStore( + 'your AppId',//such as:WeChat AppId:414478124 + false + ); + print(isSuccess); + } +``` + +### 2.Get the last version form AppStore +```dart + void getVersionFromAppStore() async { + String versionName = await RUpgrade.getVersionFromAppStore( + 'your AppId',//such as:WeChat AppId:414478124 + false + ); + print(versionName); + } +``` + +## LICENSE + Copyright 2021 rhymelph + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/modules/ability/r_upgrade-0.4.2/README_CN.md b/modules/ability/r_upgrade-0.4.2/README_CN.md new file mode 100644 index 000000000..9d0c8d171 --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/README_CN.md @@ -0,0 +1,367 @@ +# r_upgrade +[![pub package](https://img.shields.io/pub/v/r_upgrade.svg)](https://pub.dartlang.org/packages/r_upgrade) + +![](screen/r_upgrade.png) + +Android和IOS的升级应用插件==Flutter应用升级插件 + +- [✔] 跳转链接方式升级 +- [✔] `Android`跳转到应用商店升级 +- [✔] `Android`获取已安装的应用商店列表 +- [✔] `Android`获取应用商店版本号(目前仅支持GooglePlay、小米、应用宝) +- [✔] `Android`使用下载链接下载APK + - [✔] 监听下载信息 + - [✔] 取消/暂停/继续下载 + - [✔] 根据ID安装应用 + - [✔] 根据ID获取下载状态 + - [✔] 获取最后一次下载ID(根据版本名和版本号) + - [✔] 修改通知栏显示的信息 +- [✔] `Android`热更新 +- [✔] `Android`增量升级 +- [✔] `Android`普通安装,静默安装 +- [✔] `IOS`根据APPID跳转AppStore升级 +- [✔] `IOS`根据APPID获取AppStore当前上架版本 + +# 外卖红包🧧 +微信扫一扫下方的二维码关注公众号,领取外卖红包,点外卖最高可免单!(希望点外卖的时候都能领取一下,我会得到几毛钱的收益,也是对这个插件的支持,非常感谢!) + +![](screen/extension.png) + +# 目录 + +- [r_upgrade](#r_upgrade) +- [外卖红包🧧](#外卖红包) +- [目录](#目录) + - [开始吧](#开始吧) + - [1.使用插件:](#1使用插件) + - [2.使用打开链接的方式进行更新(`Android`和`IOS`通用)](#2使用打开链接的方式进行更新android和ios通用) + - [Android平台](#android平台) + - [1.获取应用商店列表](#1获取应用商店列表) + - [2.获取对应的应用商店上架版本号](#2获取对应的应用商店上架版本号) + - [3.跳转到应用商店升级](#3跳转到应用商店升级) + - [4.通过下载链接进行apk下载](#4通过下载链接进行apk下载) + - [1.添加升级下载进度监听](#1添加升级下载进度监听) + - [2.立即升级你的应用](#2立即升级你的应用) + - [3. 取消下载](#3-取消下载) + - [4. 安装应用](#4-安装应用) + - [5. 暂停下载](#5-暂停下载) + - [6. 继续下载](#6-继续下载) + - [7. 获取最后一次下载的ID](#7-获取最后一次下载的id) + - [8. 获取ID对应的下载状态](#8-获取id对应的下载状态) + - [9. 增量升级](#9-增量升级) + - [10. 热更新](#10-热更新) + - [安卓平台通知栏](#安卓平台通知栏) + - [IOS平台](#ios平台) + - [1.跳转到AppStore进行更新](#1跳转到appstore进行更新) + - [2.获取AppStore中你的应用最后的版本名](#2获取appstore中你的应用最后的版本名) + - [LICENSE](#license) + + +## 开始吧 + +### 1.使用插件: + +在`pubspec.yaml`文件添加下面代码 +```yaml +dependencies: + r_upgrade: last version +``` + +### 2.使用打开链接的方式进行更新(`Android`和`IOS`通用) +```dart + void upgradeFromUrl()async{ + bool isSuccess =await RUpgrade.upgradeFromUrl( + 'https://www.baidu.com', + ); + print(isSuccess); + } +``` + +## Android平台 + +### 1.获取应用商店列表 +```dart + void getAndroidStores() async { + final stores = await RUpgrade.androidStores; + } +``` + +### 2.获取对应的应用商店上架版本号 +```dart + void getVersionName() async { + final versionName = await RUpgrade.getVersionFromAndroidStore(AndroidStore.TENCENT); + } +``` + +### 3.跳转到应用商店升级 +```dart + void upgradeFromAndroidStore(){ + bool isSuccess = await RUpgrade.upgradeFromAndroidStore(AndroidStore.BAIDU); + print('${isSuccess?'跳转成功':'跳转失败'}'); + } +``` + +### 4.通过下载链接进行apk下载 + +> 注意,在Android应用中,请确保`AndroidManifest.xml`中声明以下权限,并在6.0系统上进行动态授权,不然会调用升级方法将抛出权限异常 +```xml + + + + + + + + +``` + +#### 1.添加升级下载进度监听 +```dart +RUpgrade.stream.listen((DownloadInfo info){ + ///... +}); +``` +info 里包含的信息如下: + +| 字段 | 含义 | +| - | - | +| (int) id | 当前下载任务的id | +| (int) max_length | 所需下载的总大小 (bytes) | +| (int) current_length | 当前已下载的大小 (bytes) | +| (double) percent | 当前下载进度(0-100) | +| (double) planTime | 计划下载完成所需时间/秒 (需要.toStringAsFixed(0)) | +| (String) path | 当前下载的文件路径 | +| (double) speed | 当前下载的速度kb/s | +| (DownloadStatus) status | 当前下载状态
`STATUS_PAUSED` 下载已暂停
`STATUS_PENDING`等待下载
`STATUS_RUNNING`下载中
`STATUS_SUCCESSFUL`下载成功
`STATUS_FAILED`下载失败
`STATUS_CANCEL`下载取消| + +注意: 部分http下载链接可能返回 `max_length = -1`,请自行判断 + +#### 2.立即升级你的应用 +目前分为两部分 +`useDownloadManager`: +- `true`: 调用系统的`DownloadManager`进行下载 + - 优势:接入简单,无需担心操作,下载全由系统管理 + - 劣势:无法使用http方式进行下载,无法在下载过程中点击通知栏进行暂停,无法根据有无网络进行暂停和继续下载,适配机型问题等 + - 支持的方法:`RUpgrade.stream`、`install`、`cancel` +- `false`: 调用`Service`进行下载(默认使用) + - 优势:功能较全,支持http/https下载,支持网络断开后自动暂停下载,连接上后继续下载,支持断点续传,支持查询最后一次下载等 + - 劣势:暂无发现,如果发现bug欢迎提issue. + - 支持的方法:默认全部 +```dart + // [installType] 下载完成后的安装类型,详情可以看[RUpgradeInstallType] + // [apkName] 安装包的名字(需要包含.apk) + // [notificationVisibility] 通知栏显示方式 + // [useDownloadManager] 是否使用DownloadManager,默认不使用(DownloadManager不支持http下载,下载手动暂停,断点续传等,不建议使用) + // [upgradeFlavor] 升级的口味,默认全量升级(默认) + void upgrade() async { + int id = await RUpgrade.upgrade( + 'https://raw.githubusercontent.com/rhymelph/r_upgrade/master/apk/app-release.apk', + apkName: 'app-release.apk',installType: RUpgradeInstallType.normal,); + } +``` +新增升级的口味:(不支持使用DownloadManager下载) +```dart +enum RUpgradeFlavor { + normal, // 全量升级 + hotUpgrade, // 热更新 + incrementUpgrade, // 增量升级 +} +``` + +#### 3. 取消下载 +```dart + void cancel() async { + bool isSuccess=await RUpgrade.cancel(id); + } +``` + +#### 4. 安装应用 + +- 使用下载的id进行安装应用 +```dart + void install() async { + bool isSuccess=await RUpgrade.install(id); + } +``` +- 使用文件路径进行安装应用 +```dart + void installByPath(String path) async { + bool isSuccess=await RUpgrade.installByPath(path); + } +``` +- 新增安装类型 +```dart +/// [RUpgrade.upgradeWithId]、[RUpgrade.upgrade]、[RUpgrade.install]、[RUpgrade.installByPath] +enum RUpgradeInstallType { + normal,//普通安装 + silent,//静默安装 + none,// 不进行安装 +} +``` + +#### 5. 暂停下载 +```dart + void pause() async { + bool isSuccess=await RUpgrade.pause(id); + } +``` + +#### 6. 继续下载 +```dart + void pause() async { + bool isSuccess=await RUpgrade.upgradeWithId(id); + // 返回 false 即表示从来不存在此ID + // 返回 true + // 调用此方法前状态为 [STATUS_PAUSED]、[STATUS_FAILED]、[STATUS_CANCEL],将继续下载 + // 调用此方法前状态为 [STATUS_RUNNING]、[STATUS_PENDING],不会发生任何变化 + // 调用此方法前状态为 [STATUS_SUCCESSFUL],将会安装应用 + // 当文件被删除时,重新下载 + } +``` + +#### 7. 获取最后一次下载的ID +该方法只会寻找当前应用版本名和版本号下下载过的ID +```dart + void getLastUpgradeId() async { + int id = await RUpgrade.getLastUpgradedId(); + } +``` + +#### 8. 获取ID对应的下载状态 +```dart + void getDownloadStatus()async{ + DownloadStatus status = await RUpgrade.getDownloadStatus(id); + } +``` + +#### 9. 增量升级 +- 1.下载[bsdiff](https://github.com/rhymelph/r_upgrade/releases/download/v0.3.0/bsdiff)工具到本地 +- 2.准备两个安装包,一个是即将需要升级的安装包(old.apk)、一个是你需要更新的安装包(new.apk) +- 3.在命令行切换到上面下载的`bsdiff`目录下,运行命令`./bsdiff old.apk new.apk increment.patch` +- 4.将上面生成的`increment.patch`上传到服务器 +- 5.调用`RUpgrade.upgrade(...,upgradeFlavor:RUpgradeFlavor.incrementUpgrade)`方法进行下载,即可 +- 6.调用`RUpgrade.install(id)`进行安装 + +代码如下: +```dart + int id; + void incrementUpgrade(){ + id = await RUpgrade.upgrade( + 'https://mydata-1252536312.cos.ap-guangzhou.myqcloud.com/r_upgrade.patch', + fileName: 'r_upgrade.patch', + useDownloadManager: false, + installType: RUpgradeInstallType.none, + upgradeFlavor: RUpgradeFlavor.incrementUpgrade, + ); + } + + void install(){ + try { + await RUpgrade.install(id); + } catch (e) { + _state.currentState + .showSnackBar(SnackBar(content: Text('增量更新失败!'))); + } + } +``` + +#### 10. 热更新 +- 你可以使用升级返回的`id`进行热更新,下载的文件需要将新版本生成的`isolate_snapshot_data`、`kernel_blob.bin`、`vm_snapshot_data`打进zip文件中下载 +步骤: + - 运行 `flutter clean` 清理build文件 + - 运行 `flutter build bundle` 生成需要的产物,下面标记星号为必须文件 +``` +|- AssetManifest.json +|- FontManifest.json +|- fonts + |- ... +|- isolate_snapshot_data * +|- kernel-blob.bin * +|- LICENSE +|- packages + |- ... +|- vm_snapshot_data * +``` + - 将标记星号的文件打包成zip文件,上传到服务器 + - 调用`RUpgrade.upgrade(...,upgradeFlavor:RUpgradeFlavor.hotUpgrade)`方法进行下载 + - 下载完成后,将上面获取到的id进行热更新,调用如下代码 + +```dart + bool isSuccess = await RUpgrade.install(id); + if (isSuccess) { + _state.currentState + .showSnackBar(SnackBar(content: Text('热更新成功,3s后退出应用,请重新进入'))); + Future.delayed(Duration(seconds: 3)).then((_){ + SystemNavigator.pop(animated: true); + }); + }else{ + _state.currentState + .showSnackBar(SnackBar(content: Text('热更新失败,请等待更新包下载完成'))); + } +``` + - 重启应用即可 + +> 注意:目前热更新尚处于测试阶段,只支持Flutter代码的变更,不支持资源文件等,热更新造成的一切的后果插件的作者概不负责,由使用者承担。 + + +## 安卓平台通知栏 + +如果你想自定义通知栏显示的内容, 可以这样做, 修改或添加文件路径为`project/android/app/main/res/values/r_upgrade_value.xml`,添加下面代码 +```xml + + + %.2fkb/s + 预计%.0f秒后完成 + 下载完成 + 下载被暂停 + 下载失败 + +``` +然后.当你使用`upgrade`方法时,你应该设置参数`notificationStyle`,默认为显示预计完成时间. +```dart +/// Notification show style about content text +enum NotificationStyle { + speechAndPlanTime, // 100kb/s 预计1秒后完成 + planTimeAndSpeech, // 预计1秒后完成 100kb/s + speech,// 100kb/s + planTime, // 预计1秒后完成 + none, // +} +``` + +## IOS平台 + +### 1.跳转到AppStore进行更新 +```dart + void upgradeFromAppStore() async { + bool isSuccess =await RUpgrade.upgradeFromAppStore( + '您的AppId',//例如:微信的AppId:414478124 + ); + print(isSuccess); + } +``` + +### 2.获取AppStore中你的应用最后的版本名 +```dart + void getVersionFromAppStore() async { + String versionName = await RUpgrade.getVersionFromAppStore( + '您的AppId',//例如:微信的AppId:414478124 + ); + print(versionName); + } +``` + +## LICENSE + Copyright 2021 rhymelph + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/modules/ability/r_upgrade-0.4.2/_config.yml b/modules/ability/r_upgrade-0.4.2/_config.yml new file mode 100644 index 000000000..c4192631f --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-cayman \ No newline at end of file diff --git a/modules/ability/r_upgrade-0.4.2/analysis_options.yaml b/modules/ability/r_upgrade-0.4.2/analysis_options.yaml new file mode 100644 index 000000000..e69de29bb diff --git a/modules/ability/r_upgrade-0.4.2/android/.gitignore b/modules/ability/r_upgrade-0.4.2/android/.gitignore new file mode 100644 index 000000000..c6cbe562a --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/android/.gitignore @@ -0,0 +1,8 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures diff --git a/modules/ability/r_upgrade-0.4.2/android/build.gradle b/modules/ability/r_upgrade-0.4.2/android/build.gradle new file mode 100644 index 000000000..048c5d94a --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/android/build.gradle @@ -0,0 +1,44 @@ +group 'com.example.r_upgrade' +version '1.0-SNAPSHOT' + +buildscript { + repositories { + google() + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.2.1' + } +} + +rootProject.allprojects { + repositories { + google() + jcenter() + flatDir{ + dirs project(':r_upgrade').file('libs') + } + } +} + +apply plugin: 'com.android.library' + +android { + namespace "com.example.r_upgrade" + compileSdkVersion 33 + + defaultConfig { + minSdkVersion 16 + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + lintOptions { + disable 'InvalidPackage' + } +} + +dependencies { + implementation 'androidx.core:core:1.0.2' + implementation 'org.jsoup:jsoup:1.14.3' + implementation(name:"r_upgrade_lib",ext:"aar") +} diff --git a/modules/ability/r_upgrade-0.4.2/android/gradle.properties b/modules/ability/r_upgrade-0.4.2/android/gradle.properties new file mode 100644 index 000000000..755300e3a --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/android/gradle.properties @@ -0,0 +1,4 @@ +org.gradle.jvmargs=-Xmx1536M + +android.useAndroidX=true +android.enableJetifier=true diff --git a/modules/ability/r_upgrade-0.4.2/android/libs/r_upgrade_lib.aar b/modules/ability/r_upgrade-0.4.2/android/libs/r_upgrade_lib.aar new file mode 100644 index 000000000..2eef399f4 Binary files /dev/null and b/modules/ability/r_upgrade-0.4.2/android/libs/r_upgrade_lib.aar differ diff --git a/modules/ability/r_upgrade-0.4.2/android/settings.gradle b/modules/ability/r_upgrade-0.4.2/android/settings.gradle new file mode 100644 index 000000000..94542efb0 --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'r_upgrade' diff --git a/modules/ability/r_upgrade-0.4.2/android/src/main/AndroidManifest.xml b/modules/ability/r_upgrade-0.4.2/android/src/main/AndroidManifest.xml new file mode 100644 index 000000000..3552ee569 --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/android/src/main/AndroidManifest.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + diff --git a/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/RUpgradeFileProvider.java b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/RUpgradeFileProvider.java new file mode 100644 index 000000000..50ecd2b10 --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/RUpgradeFileProvider.java @@ -0,0 +1,6 @@ +package com.example.r_upgrade; + +import androidx.core.content.FileProvider; + +public class RUpgradeFileProvider extends FileProvider { +} diff --git a/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/RUpgradePlugin.java b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/RUpgradePlugin.java new file mode 100644 index 000000000..93e056c21 --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/RUpgradePlugin.java @@ -0,0 +1,105 @@ +package com.example.r_upgrade; + +import android.app.Activity; +import android.content.Intent; + +import androidx.annotation.NonNull; + +import com.example.r_upgrade.common.DownloadPermissions; +import com.example.r_upgrade.common.manager.UpgradeManager; +import com.example.r_upgrade.common.UpgradeService; +import com.example.r_upgrade.method.RUpgradeMethodCallHandler; + +import io.flutter.embedding.engine.plugins.FlutterPlugin; +import io.flutter.embedding.engine.plugins.activity.ActivityAware; +import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugin.common.MethodChannel; +import io.flutter.plugin.common.PluginRegistry; +//import io.flutter.plugin.common.PluginRegistry.Registrar; + +/** + * RUpgradePlugin + */ +public class RUpgradePlugin implements FlutterPlugin, ActivityAware { + private static final String PLUGIN_METHOD_NAME = "com.rhyme/r_upgrade_method"; + + private MethodChannel _channel; + private UpgradeManager upgradeManager; + private FlutterPluginBinding flutterPluginBinding; + + public RUpgradePlugin() { + + } + + private RUpgradePlugin(Activity activity, BinaryMessenger messenger, DownloadPermissions.PermissionsRegistry permissionsRegistry) { + initPlugin(activity, messenger, permissionsRegistry); + } + + private void initPlugin(Activity activity, BinaryMessenger messenger, DownloadPermissions.PermissionsRegistry permissionsRegistry) { + _channel = new MethodChannel(messenger, PLUGIN_METHOD_NAME); + upgradeManager = new UpgradeManager(activity, _channel, new DownloadPermissions(), permissionsRegistry); + _channel.setMethodCallHandler(new RUpgradeMethodCallHandler(upgradeManager)); + } + +// /** +// * Plugin registration. +// */ +// public static void registerWith(final Registrar registrar) { +// +// new RUpgradePlugin(registrar.activity(), registrar.messenger(), new DownloadPermissions.PermissionsRegistry() { +// @Override +// public void addListener(PluginRegistry.RequestPermissionsResultListener handler) { +// registrar.addRequestPermissionsResultListener(handler); +// } +// }); +// } + + + @Override + public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { + this.flutterPluginBinding = binding; + + } + + @Override + public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { + onDetachedFromActivity(); + flutterPluginBinding = null; + } + + + @Override + public void onAttachedToActivity(@NonNull final ActivityPluginBinding binding) { + initPlugin(binding.getActivity(), flutterPluginBinding.getBinaryMessenger(), new DownloadPermissions.PermissionsRegistry() { + @Override + public void addListener(PluginRegistry.RequestPermissionsResultListener handler) { + binding.addRequestPermissionsResultListener(handler); + } + }); + } + + @Override + public void onDetachedFromActivityForConfigChanges() { + onDetachedFromActivity(); + } + + @Override + public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) { + onAttachedToActivity(binding); + } + + @Override + public void onDetachedFromActivity() { + Intent intent = new Intent(flutterPluginBinding.getApplicationContext(), UpgradeService.class); + flutterPluginBinding.getApplicationContext().stopService(intent); + if (upgradeManager != null) { + upgradeManager.dispose(); + upgradeManager = null; + } + if (_channel != null) { + _channel.setMethodCallHandler(null); + _channel = null; + } + } +} diff --git a/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/DownloadPermissions.java b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/DownloadPermissions.java new file mode 100644 index 000000000..dfaaae426 --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/DownloadPermissions.java @@ -0,0 +1,139 @@ +package com.example.r_upgrade.common; + +import android.Manifest; +import android.app.Activity; +import android.content.pm.PackageManager; +import android.os.Build; + +import androidx.annotation.RequiresApi; +import androidx.annotation.VisibleForTesting; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; + +import io.flutter.plugin.common.PluginRegistry; + +public class DownloadPermissions { + public interface PermissionsRegistry { + void addListener(PluginRegistry.RequestPermissionsResultListener handler); + } + + public interface ResultCallback { + void onResult(String errorCode, String errorDescription); + } + + private static final int STORAGE_REQUEST_ID = 9790; + private boolean ongoing = false; + + public void requestPermissions( + Activity activity, + PermissionsRegistry permissionsRegistry, + Integer notificationVisibility, + final ResultCallback callback) { + + if (Build.VERSION.SDK_INT >= 33) { + if (ongoing) { + callback.onResult("downloadPermission", "Notification permission request ongoing"); + } + if (notificationVisibility != 2&&!hasNotificationPermission(activity)) { + permissionsRegistry.addListener( + new StorageRequestPermissionsListener(new ResultCallback() { + @Override + public void onResult(String errorCode, String errorDescription) { + ongoing = false; + callback.onResult(errorCode, errorDescription); + } + })); + ongoing = true; + ActivityCompat.requestPermissions( + activity, + new String[]{Manifest.permission.POST_NOTIFICATIONS}, + STORAGE_REQUEST_ID); + } else { + // Permissions already exist. Call the callback with success. + callback.onResult(null, null); + } + } else if (Build.VERSION.SDK_INT < 30 && (!hasReadStoragePermission(activity) || !hasWritePermission(activity))) { + if (ongoing) { + callback.onResult("downloadPermission", "Read/Write External Storage permission request ongoing"); + } + permissionsRegistry.addListener( + new StorageRequestPermissionsListener(new ResultCallback() { + @Override + public void onResult(String errorCode, String errorDescription) { + ongoing = false; + callback.onResult(errorCode, errorDescription); + } + })); + ongoing = true; + ActivityCompat.requestPermissions( + activity, + new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, + STORAGE_REQUEST_ID); + } else { + // Permissions already exist. Call the callback with success. + callback.onResult(null, null); + } + } + + @RequiresApi(api = Build.VERSION_CODES.TIRAMISU) + private boolean hasNotificationPermission(Activity activity) { + return ContextCompat.checkSelfPermission(activity, Manifest.permission.POST_NOTIFICATIONS) + == PackageManager.PERMISSION_GRANTED; + } + + private boolean hasReadStoragePermission(Activity activity) { + return ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE) + == PackageManager.PERMISSION_GRANTED; + } + + private boolean hasWritePermission(Activity activity) { + return ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) + == PackageManager.PERMISSION_GRANTED; + } + + @VisibleForTesting + public static final class StorageRequestPermissionsListener + implements PluginRegistry.RequestPermissionsResultListener { + + // There's no way to unregister permission listeners in the v1 embedding, so we'll be called + // duplicate times in cases where the user denies and then grants a permission. Keep track of if + // we've responded before and bail out of handling the callback manually if this is a repeat + // call. + boolean alreadyCalled = false; + + final ResultCallback callback; + + @VisibleForTesting + StorageRequestPermissionsListener(ResultCallback callback) { + this.callback = callback; + } + + @Override + public boolean onRequestPermissionsResult(int id, String[] permissions, int[] grantResults) { + if (alreadyCalled || id != STORAGE_REQUEST_ID) { + return false; + } + if (Build.VERSION.SDK_INT >= 33) { + if (grantResults.length != 1) return false; + alreadyCalled = true; + + if (grantResults[0] != PackageManager.PERMISSION_GRANTED) { + callback.onResult("downloadPermission", "Notification permission not granted"); + } else { + callback.onResult(null, null); + } + return true; + } else { + if (grantResults.length != 2) return false; + alreadyCalled = true; + + if (grantResults[0] != PackageManager.PERMISSION_GRANTED || grantResults[1] != PackageManager.PERMISSION_GRANTED) { + callback.onResult("downloadPermission", "Read/Write External Storage permission not granted"); + } else { + callback.onResult(null, null); + } + return true; + } + } + } +} diff --git a/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/DownloadStatus.java b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/DownloadStatus.java new file mode 100644 index 000000000..d374f7e9b --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/DownloadStatus.java @@ -0,0 +1,24 @@ +package com.example.r_upgrade.common; + +public enum DownloadStatus { + STATUS_PAUSED(0), + STATUS_PENDING(1), + STATUS_RUNNING(2), + STATUS_SUCCESSFUL(3), + STATUS_FAILED(4), + STATUS_CANCEL(5); + + private int value; + + DownloadStatus(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public void setValue(int value) { + this.value = value; + } +} diff --git a/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/RUpgradeLogger.java b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/RUpgradeLogger.java new file mode 100644 index 000000000..b986b8736 --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/RUpgradeLogger.java @@ -0,0 +1,27 @@ +package com.example.r_upgrade.common; + +import android.util.Log; + +public class RUpgradeLogger { + private static RUpgradeLogger _logger; + + public static RUpgradeLogger get() { + if (_logger == null) { + _logger = new RUpgradeLogger(); + } + return _logger; + } + + private boolean isDebug = true; + + + public void setDebug(boolean isDebug) { + this.isDebug = isDebug; + } + + public void d(String tag, String content) { + if (isDebug) { + Log.d(tag, content); + } + } +} diff --git a/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/ResultMap.java b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/ResultMap.java new file mode 100644 index 000000000..2a9d979ab --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/ResultMap.java @@ -0,0 +1,43 @@ +package com.example.r_upgrade.common; + + +import java.util.HashMap; +import java.util.Map; + +public class ResultMap { + private static ResultMap instance; + + private final Map map; + + private ResultMap() { + this.map = new HashMap(); + } + + public static ResultMap getInstance() { + if (instance == null) { + synchronized (ResultMap.class) { + if (instance == null) { + instance = new ResultMap(); + } + } + } + return instance; + } + + public ResultMap pubClear(String key, Object value) { + map.clear(); + map.put(key, value); + return instance; + } + + + public ResultMap put(String key, Object value) { + map.put(key, value); + return instance; + } + + public Map getMap(){ + return map; + } + +} diff --git a/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/UpgradeNotification.java b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/UpgradeNotification.java new file mode 100644 index 000000000..0ee09ba3c --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/UpgradeNotification.java @@ -0,0 +1,143 @@ +package com.example.r_upgrade.common; + +import android.annotation.TargetApi; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.os.Build; + +import androidx.core.app.NotificationCompat; +import androidx.core.app.NotificationManagerCompat; + +import com.example.r_upgrade.R; +import com.example.r_upgrade.common.manager.UpgradeManager; + +public class UpgradeNotification { + public static final String TAG = "r_upgrade.Notification"; + + private static String CHANNEL_NAME; + + public static void createNotification(Context context, int id, String title,boolean indeterminate, Double percent, String contentText, int status) { + if (CHANNEL_NAME == null) { + try { + CHANNEL_NAME = context.getPackageName() + "_notification"; + } catch (Exception e) { + e.printStackTrace(); + CHANNEL_NAME = "r_upgrade_notification"; + } + } + if (status == DownloadStatus.STATUS_CANCEL.getValue()) { + removeNotification(context, id); + return; + } + Notification notification; + if (status == DownloadStatus.STATUS_RUNNING.getValue()) { + Intent pauseIntent = new Intent(); + pauseIntent.setAction(UpgradeService.RECEIVER_PAUSE); + pauseIntent.putExtra(UpgradeManager.PARAMS_ID, id); + pauseIntent.putExtra(UpgradeManager.PARAMS_PACKAGE, context.getPackageName()); + + + PendingIntent pausePendingIntent = + PendingIntent.getBroadcast(context, 0, pauseIntent, getPendingIntentFlag()); +// PendingIntent.getBroadcast(context, 0, pauseIntent, PendingIntent.FLAG_IMMUTABLE); + int current_length = percent.intValue(); + + notification = new NotificationCompat.Builder(context, CHANNEL_NAME) + .setSmallIcon(context.getApplicationInfo().icon) + .setContentTitle(title) + .setContentText(indeterminate ? "" : contentText) + .setContentIntent(pausePendingIntent) + .setProgress(indeterminate ? 0 : 100, indeterminate ? 0 : current_length, indeterminate) + .build(); + } else if (status == DownloadStatus.STATUS_SUCCESSFUL.getValue()) { + Intent installIntent = new Intent(); + installIntent.setAction(UpgradeManager.DOWNLOAD_INSTALL); + installIntent.putExtra(UpgradeService.DOWNLOAD_ID, id); + installIntent.putExtra(UpgradeManager.PARAMS_PACKAGE, context.getPackageName()); + + PendingIntent installPendingIntent = + PendingIntent.getBroadcast(context, 0, installIntent, getPendingIntentFlag()); +// PendingIntent.getBroadcast(context, 0, installIntent, PendingIntent.FLAG_IMMUTABLE); + + notification = new NotificationCompat.Builder(context, CHANNEL_NAME) + .setSmallIcon(context.getApplicationInfo().icon) + .setContentTitle(title) + .setContentIntent(installPendingIntent) + .setContentText(context.getResources().getString(R.string.r_upgrade_download_finish)) + .build(); + } else if (status == DownloadStatus.STATUS_PAUSED.getValue()) { + Intent reStartIntent = new Intent(); + reStartIntent.setAction(UpgradeService.RECEIVER_RESTART); + reStartIntent.putExtra(UpgradeManager.PARAMS_ID, id); + reStartIntent.putExtra(UpgradeManager.PARAMS_PACKAGE, context.getPackageName()); + + PendingIntent reStartPendingIntent = + PendingIntent.getBroadcast(context, 0, reStartIntent, getPendingIntentFlag()); +// PendingIntent.getBroadcast(context, 0, reStartIntent, PendingIntent.FLAG_IMMUTABLE); + + notification = new NotificationCompat.Builder(context, CHANNEL_NAME) + .setSmallIcon(context.getApplicationInfo().icon) + .setContentTitle(title) + .setContentIntent(reStartPendingIntent) + .setContentText(context.getResources().getString(R.string.r_upgrade_download_paused)) + .build(); + } else if (status == DownloadStatus.STATUS_FAILED.getValue()) { + Intent failedIntent = new Intent(); + failedIntent.setAction(UpgradeService.RECEIVER_RESTART); + failedIntent.putExtra(UpgradeManager.PARAMS_ID, id); + failedIntent.putExtra(UpgradeManager.PARAMS_PACKAGE, context.getPackageName()); + + PendingIntent reStartPendingIntent = + PendingIntent.getBroadcast(context, 0, failedIntent, getPendingIntentFlag()); +// PendingIntent.getBroadcast(context, 0, failedIntent, PendingIntent.FLAG_IMMUTABLE); + notification = new NotificationCompat.Builder(context, CHANNEL_NAME) + .setSmallIcon(context.getApplicationInfo().icon) + .setContentTitle(title) + .setContentIntent(reStartPendingIntent) + .setContentText(context.getResources().getString(R.string.r_upgrade_download_failed)) + .build(); + } else { + notification = new NotificationCompat.Builder(context, CHANNEL_NAME) + .setSmallIcon(context.getApplicationInfo().icon) + .setContentTitle(title) + .setProgress(0, 0, true) + .build(); + } + NotificationManagerCompat compat = NotificationManagerCompat.from(context); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + NotificationManager notificationManager = context.getSystemService(NotificationManager.class); + notificationManager.createNotificationChannel(createNotificationChannel()); + } + compat.notify(id, notification); + } + + private static int getPendingIntentFlag() { + int pendingFlag = PendingIntent.FLAG_UPDATE_CURRENT; + if (Build.VERSION.SDK_INT >= 31) { + pendingFlag = PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT; + } + return pendingFlag; + } + + public static void removeNotification(Context context, long id) { + NotificationManagerCompat compat = NotificationManagerCompat.from(context); + compat.cancel((int) id); + } + + @TargetApi(Build.VERSION_CODES.O) + private static NotificationChannel createNotificationChannel() { + String description = "Upgrade Application"; + int importance = NotificationManager.IMPORTANCE_DEFAULT; + NotificationChannel channel = new NotificationChannel(CHANNEL_NAME, CHANNEL_NAME, importance); + channel.setDescription(description); + channel.enableVibration(false); + channel.setImportance(NotificationManager.IMPORTANCE_LOW); + // Register the channel with the system; you can't change the importance + // or other notification behaviors after this + return channel; + } +} diff --git a/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/UpgradeNotificationStyle.java b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/UpgradeNotificationStyle.java new file mode 100644 index 000000000..e0ce99fb6 --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/UpgradeNotificationStyle.java @@ -0,0 +1,31 @@ +package com.example.r_upgrade.common; + +import android.content.Context; + +import com.example.r_upgrade.R; + +public enum UpgradeNotificationStyle { + speechAndPlanTime, + planTimeAndSpeech, + speech, + planTime, + none; + + + public String getNotificationStyleString(Context context, double speech, double planTime) { + String speechString = context.getResources().getString(R.string.r_upgrade_download_speech, speech); + String planTimeString = context.getResources().getString(R.string.r_upgrade_download_planTime, planTime); + switch (this) { + case speech: + return speechString; + case planTime: + return planTimeString; + case planTimeAndSpeech: + return planTimeString + " " + speechString; + case speechAndPlanTime: + return speechString + " " + planTimeString; + } + return ""; + } + +} diff --git a/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/UpgradeSQLite.java b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/UpgradeSQLite.java new file mode 100644 index 000000000..8dd980e1d --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/UpgradeSQLite.java @@ -0,0 +1,220 @@ +package com.example.r_upgrade.common; + +import android.content.ContentValues; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.util.Log; + +import androidx.annotation.Nullable; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + + +public class UpgradeSQLite extends SQLiteOpenHelper { + private static final String TAG = "r_upgrade.SQLite"; + private static final String DATABASE_NAME = "r_upgrade.db"; + private static final int DATABASE_VERSION = 2; + + private static UpgradeSQLite instance; + + public synchronized static UpgradeSQLite getInstance(Context context) { + if (instance == null) { + instance = new UpgradeSQLite(context); + } + return instance; + } + + public static final String VERSION_MANAGER = "version_manager"; + + public static final String ID = "id"; + public static final String URL = "url"; + public static final String APK_NAME = "apk_name"; + public static final String PATH = "path"; + + public static final String HEADER = "header"; + public static final String CURRENT_LENGTH = "current_length"; + public static final String MAX_LENGTH = "max_length"; + public static final String STATUS = "status"; + + public static final String VERSION_NAME = "version_name"; + public static final String VERSION_CODE = "version_code"; + public static final String UPGRADE_FLAVOR = "upgrade_flavor"; + public static final int UPGRADE_FLAVOR_Normal = 0; + public static final int UPGRADE_FLAVOR_HOT_UPDATE = 1; + public static final int UPGRADE_FLAVOR_INCREMENT = 2; + + private static final String DATABASE_CREATE = "create table if not exists " + VERSION_MANAGER + "(" + + ID + " integer primary key autoincrement," + + URL + " text," + + PATH + " text," + + APK_NAME + " text," + + HEADER + " text," + + CURRENT_LENGTH + " integer," + + MAX_LENGTH + " integer," + + STATUS + " integer," + + VERSION_NAME + " text," + + VERSION_CODE + " integer,"+ + UPGRADE_FLAVOR + " integer)"; + + public UpgradeSQLite(@Nullable Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + } + + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL(DATABASE_CREATE); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + RUpgradeLogger.get().d(TAG, String.format(Locale.ENGLISH,"onUpgrade: oldVersion:%d , newVersion:%d", oldVersion, newVersion)); + if(oldVersion == 1){ + db.execSQL("delete from "+VERSION_MANAGER); + db.execSQL("alter table "+VERSION_MANAGER+" add column "+UPGRADE_FLAVOR+" integer"); + } + } + + public Integer queryIdByVersionNameAndVersionCode(String versionName, int versionCode) { + SQLiteDatabase readableDatabase = getReadableDatabase(); + Cursor cursor = readableDatabase.rawQuery("select * from " + VERSION_MANAGER + " where " + VERSION_NAME + "=?" + " and " + VERSION_CODE + "=? order by " + ID + " DESC LIMIT 1", new String[]{versionName, String + .valueOf(versionCode)}); + boolean canMoveNext = cursor.moveToNext(); + if (canMoveNext) { + int id = cursor.getInt(cursor.getColumnIndex(ID)); + RUpgradeLogger.get().d(TAG, "queryByVersionNameAndVersionCode: " + id); + cursor.close(); + return id; + } else { + cursor.close(); + return null; + } + } + + public Map queryById(long id) { + Map result = new HashMap<>(); + + SQLiteDatabase readableDatabase = getReadableDatabase(); + Cursor cursor = readableDatabase.rawQuery("select * from " + VERSION_MANAGER + " where " + ID + "=?", new String[]{String.valueOf(id)}); + boolean hasNext = cursor.moveToNext(); + if (hasNext) { + String path = cursor.getString(cursor.getColumnIndex(PATH)); + String url = cursor.getString(cursor.getColumnIndex(URL)); + String apkName = cursor.getString(cursor.getColumnIndex(APK_NAME)); + int status = cursor.getInt(cursor.getColumnIndex(STATUS)); + String header = cursor.getString(cursor.getColumnIndex(HEADER)); + int rUpgradeFlavor = cursor.getInt(cursor.getColumnIndex(UPGRADE_FLAVOR)); + result.put(PATH, path); + result.put(APK_NAME, apkName); + result.put(URL, url); + result.put(STATUS, status); + result.put(HEADER, header); + result.put(UPGRADE_FLAVOR, rUpgradeFlavor); + return result; + } else { + cursor.close(); + return null; + } + } + + + public Integer queryStatusById(int id) { + SQLiteDatabase readableDatabase = getReadableDatabase(); + Cursor cursor = readableDatabase.rawQuery("select * from " + VERSION_MANAGER + " where " + ID + "=?", new String[]{String.valueOf(id)}); + boolean canMoveNext = cursor.moveToNext(); + if (canMoveNext) { + int status = cursor.getInt(cursor.getColumnIndex(STATUS)); + cursor.close(); + return status; + } else { + cursor.close(); + return null; + } + } + + + //创建记录 + public long createRecord(Context context, String url, String apk_name, String header, int status, int upgradeFlavor) { + String versionName = ""; + int versionCode = 0; + try { + PackageManager manager = context.getPackageManager(); + PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0); + versionName = info.versionName; + versionCode = info.versionCode; + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + + SQLiteDatabase writableDatabase = getWritableDatabase(); + ContentValues values = new ContentValues(); + values.put(URL, url); + values.put(PATH, ""); + values.put(APK_NAME, apk_name); + values.put(HEADER, header); + values.put(CURRENT_LENGTH, 0); + values.put(MAX_LENGTH, 0); + values.put(STATUS, status); + values.put(VERSION_NAME, versionName); + values.put(VERSION_CODE, versionCode); + values.put(UPGRADE_FLAVOR, upgradeFlavor); + return writableDatabase.insert(VERSION_MANAGER, null, values); + } + + void update(long id, String url, String path, String apkName, String header, Long current_length, Long max_length, int status) { + SQLiteDatabase writableDatabase = getWritableDatabase(); + ContentValues values = new ContentValues(); + if (current_length != null) { + values.put(CURRENT_LENGTH, current_length); + } + if (max_length != null) { + values.put(MAX_LENGTH, max_length); + } + if (url != null) { + values.put(URL, url); + } + + if (path != null) { + values.put(PATH, path); + } + + if (apkName != null) { + values.put(APK_NAME, apkName); + } + if (header != null) { + values.put(HEADER, header); + } + values.put(STATUS, status); + writableDatabase.update(VERSION_MANAGER, values, ID + "=?", new String[]{String.valueOf(id)}); + } + + void update(long id, Long current_length, Long max_length, int status) { + SQLiteDatabase writableDatabase = getWritableDatabase(); + ContentValues values = new ContentValues(); + if (current_length != null) { + values.put(CURRENT_LENGTH, current_length); + } + if (max_length != null) { + values.put(MAX_LENGTH, max_length); + } + values.put(STATUS, status); + writableDatabase.update(VERSION_MANAGER, values, ID + "=?", new String[]{String.valueOf(id)}); + } + + void delete(long id) { + SQLiteDatabase writableDatabase = getWritableDatabase(); + writableDatabase.delete(VERSION_MANAGER, ID + "=?", new String[]{String.valueOf(id)}); + } + + public void pauseDownloading() { + SQLiteDatabase writableDatabase = getWritableDatabase(); + ContentValues values = new ContentValues(); + values.put(STATUS, DownloadStatus.STATUS_PAUSED.getValue()); + writableDatabase.update(VERSION_MANAGER, values, STATUS + "=? or " + STATUS + "=?", new String[]{String.valueOf(DownloadStatus.STATUS_PENDING.getValue()), String.valueOf(DownloadStatus.STATUS_RUNNING.getValue()),}); + } +} diff --git a/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/UpgradeService.java b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/UpgradeService.java new file mode 100644 index 000000000..545930282 --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/UpgradeService.java @@ -0,0 +1,693 @@ +package com.example.r_upgrade.common; + +import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkInfo; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.os.IBinder; + +import androidx.core.net.ConnectivityManagerCompat; + +import org.json.JSONObject; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.RandomAccessFile; +import java.math.BigDecimal; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLSocketFactory; + +import static com.example.r_upgrade.common.manager.UpgradeManager.DOWNLOAD_STATUS; +import static com.example.r_upgrade.common.manager.UpgradeManager.PARAMS_APK_NAME; +import static com.example.r_upgrade.common.manager.UpgradeManager.PARAMS_CURRENT_LENGTH; +import static com.example.r_upgrade.common.manager.UpgradeManager.PARAMS_ID; +import static com.example.r_upgrade.common.manager.UpgradeManager.PARAMS_MAX_LENGTH; +import static com.example.r_upgrade.common.manager.UpgradeManager.PARAMS_PACKAGE; +import static com.example.r_upgrade.common.manager.UpgradeManager.PARAMS_PATH; +import static com.example.r_upgrade.common.manager.UpgradeManager.PARAMS_PERCENT; +import static com.example.r_upgrade.common.manager.UpgradeManager.PARAMS_PLAN_TIME; +import static com.example.r_upgrade.common.manager.UpgradeManager.PARAMS_SPEED; +import static com.example.r_upgrade.common.manager.UpgradeManager.PARAMS_STATUS; + +public class UpgradeService extends Service { + public static final String DOWNLOAD_ID = "download_id"; + public static final String DOWNLOAD_URL = "download_url"; + public static final String DOWNLOAD_Header = "download_header"; + public static final String DOWNLOAD_APK_NAME = "download_apkName"; + public static final String DOWNLOAD_RESTART = "download_restart"; + + + private static final String TAG = "r_upgrade.Service"; + private final Executor mExecutor = Executors.newSingleThreadExecutor(); + private UpgradeSQLite sqLite; + private UpgradeRunnable runnable; + private UpgradeService service; + private boolean isFirst = true; + + private final BroadcastReceiver actionReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String packageName = intent.getStringExtra(PARAMS_PACKAGE); + if (packageName == null || !packageName.equals(getPackageName())) { + return; + } + if (intent != null && intent.getAction() != null && intent.getAction().equals(RECEIVER_CANCEL)) { + int id = intent.getIntExtra(PARAMS_ID, 0); + runnable.cancel(id); + } else if (intent != null && intent.getAction() != null && intent.getAction().equals(RECEIVER_PAUSE)) { + int id = intent.getIntExtra(PARAMS_ID, 0); + runnable.pause(id); + } else if (intent != null && intent.getAction() != null && intent.getAction().equals(RECEIVER_RESTART)) { + int id = intent.getIntExtra(PARAMS_ID, 0); + runnable = new UpgradeRunnable(true, (long) id, null, null, null, service, sqLite); + mExecutor.execute(runnable); + } else if (intent != null && intent.getAction() != null && intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) { + ConnectivityManager conMgr = (ConnectivityManager) context.getSystemService(CONNECTIVITY_SERVICE); + NetworkInfo info = ConnectivityManagerCompat.getNetworkInfoFromBroadcast(conMgr, intent); + handleNetworkChange(info != null && info.isConnected()); + } + } + }; + private ConnectivityManager.NetworkCallback networkCallback; + + public static final String RECEIVER_CANCEL = "com.example.r_upgrade.RECEIVER_CANCEL"; + public static final String RECEIVER_PAUSE = "com.example.r_upgrade.RECEIVER_PAUSE"; + public static final String RECEIVER_RESTART = "com.example.r_upgrade.RECEIVER_RESTART"; + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public void onCreate() { + super.onCreate(); + service = this; + sqLite = UpgradeSQLite.getInstance(this); + IntentFilter filter = new IntentFilter(); + filter.addAction(RECEIVER_CANCEL); + filter.addAction(RECEIVER_RESTART); + filter.addAction(RECEIVER_PAUSE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + ConnectivityManager conMgr = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE); + networkCallback = new ConnectivityManager.NetworkCallback() { + @Override + public void onAvailable(Network network) { + super.onAvailable(network); + ConnectivityManager conMgr = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE); + NetworkInfo info = conMgr.getNetworkInfo(network); + handleNetworkChange(info != null && info.isConnected()); + } + + @Override + public void onLost(Network network) { + super.onLost(network); + ConnectivityManager conMgr = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE); + NetworkInfo info = conMgr.getNetworkInfo(network); + handleNetworkChange(info != null && info.isConnected()); + } + }; + conMgr.registerDefaultNetworkCallback(networkCallback); + } else { + filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); + } + registerReceiver(actionReceiver, filter); + + } + + private void handleNetworkChange(boolean isConnected) { + if (runnable == null || runnable.isFinish) { + return; + } + if (isConnected) { + RUpgradeLogger.get().d(TAG, "onReceive: 当前网络正在连接"); + if (isFirst) { + isFirst = false; + return; + } + long id = runnable.id; + runnable = new UpgradeRunnable(true, (long) id, runnable.url, runnable.header, runnable.apkName, service, sqLite); + mExecutor.execute(runnable); + } else { + if (isFirst) { + isFirst = false; + return; + } + runnable.handlerDownloadFinish(); + runnable.pause(-1); + isFirst = false; + RUpgradeLogger.get().d(TAG, "onReceive: 当前网络已断开"); + } + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + if (intent == null) return super.onStartCommand(null, flags, startId); + Bundle bundle = intent.getExtras(); + assert (bundle != null); + String url = bundle.getString(DOWNLOAD_URL); + int id = bundle.getInt(DOWNLOAD_ID); + + Map header; + if (bundle.get(DOWNLOAD_Header) != null && bundle.get(DOWNLOAD_Header) instanceof String) { + header = getMapForJson(bundle.getString(DOWNLOAD_Header)); + } else { + header = (Map) bundle.getSerializable(DOWNLOAD_Header); + } + String apkName = bundle.getString(DOWNLOAD_APK_NAME); + boolean isReStart = bundle.getBoolean(DOWNLOAD_RESTART); + + runnable = new UpgradeRunnable( + isReStart, + (long) id, + url, + header, + apkName, service, sqLite); + + mExecutor.execute(runnable); + + return super.onStartCommand(intent, flags, startId); + } + + @Override + public void onDestroy() { + unregisterReceiver(actionReceiver); + if (runnable != null) { + runnable.handlerDownloadPause(); + } + super.onDestroy(); + } + + private static class UpgradeRunnable implements Runnable { + private String url; + private Long id; + private Map header; + private String apkName; + private UpgradeService upgradeService; + + private long maxLength = 0; + private long currentLength = 0; + private long lastCurrentLength = 0; + private long lastTime = System.currentTimeMillis(); + private File downloadFile = null; + private UpgradeSQLite sqLite; + private HttpURLConnection httpURLConnection; + private HttpsURLConnection httpsURLConnection; + + private Timer timer; + private boolean isRunning = true; + private boolean isReStart; + + private boolean isNewDownload; + + private boolean isFinish; + + UpgradeRunnable(boolean isReStart, Long id, String url, Map header, String apkName, UpgradeService upgradeService, UpgradeSQLite sqLite) { + this.id = id; + this.url = url; + this.header = header; + this.apkName = apkName == null ? "release.apk" : apkName; + this.upgradeService = upgradeService; + this.sqLite = sqLite; + this.isReStart = isReStart; + this.isFinish = false; + } + + /** + * cancel timer + */ + private void cancelTimer() { + if (timer != null) { + timer.cancel(); + } + } + + private void cancel(int id) { + if (isFinish) return; + if (this.id == id) { + cancelTimer(); + if (httpsURLConnection != null) { + httpsURLConnection.disconnect(); + } + if (httpURLConnection != null) { + httpURLConnection.disconnect(); + } + isRunning = false; + handlerDownloadCancel(); + boolean isSuccess = downloadFile.delete(); + RUpgradeLogger.get().d(TAG, "cancel: delete download file " + isSuccess); + + } + } + + private void pause(int id) { + if (isFinish) return; + + if (id == -1 || this.id == id) { + if (httpsURLConnection != null) { + httpsURLConnection.disconnect(); + } + if (httpURLConnection != null) { + httpURLConnection.disconnect(); + } + isRunning = false; + handlerDownloadPause(); + } + } + + private File getDownloadDirectory() { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { + return upgradeService.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS); + } + return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); + } + + private boolean handlerDownloadPending() { + if (isReStart) { + SQLiteDatabase readableDatabase = sqLite.getReadableDatabase(); + Cursor cursor = readableDatabase.rawQuery("select * from " + UpgradeSQLite.VERSION_MANAGER + " where " + UpgradeSQLite.ID + "=?", new String[]{String.valueOf(id)}); + boolean canMove = cursor.moveToNext(); + if (!canMove) { + // 重新下载 + File parentFile = getDownloadDirectory(); + if (!parentFile.exists()) { + parentFile.mkdir(); + } + downloadFile = new File(parentFile.getPath(), apkName); + JSONObject object = null; + if (header != null) { + object = new JSONObject(header); + } + //更新一条SQL + sqLite.update(id, url, downloadFile.getPath(), apkName, object == null ? "" : object.toString(), currentLength, maxLength, DownloadStatus.STATUS_PENDING.getValue()); + cursor.close(); + return true; + } else { + boolean isNewDownload = false; + + //续传 + String path = cursor.getString(cursor.getColumnIndex(UpgradeSQLite.PATH)); + downloadFile = new File(path); + //下载的文件已被删除 + if (!downloadFile.exists()) { + try { + boolean isSuccess = downloadFile.createNewFile(); + RUpgradeLogger.get().d(TAG, "handlerDownloadPending: download file create " + isSuccess); + } catch (IOException e) { + e.printStackTrace(); + } + currentLength = 0; + lastCurrentLength = currentLength; + isNewDownload = true; + } else { + currentLength = cursor.getLong(cursor.getColumnIndex(UpgradeSQLite.CURRENT_LENGTH)); + lastCurrentLength = currentLength; + maxLength = cursor.getLong(cursor.getColumnIndex(UpgradeSQLite.MAX_LENGTH)); + } + apkName = cursor.getString(cursor.getColumnIndex(UpgradeSQLite.APK_NAME)); + url = cursor.getString(cursor.getColumnIndex(UpgradeSQLite.URL)); + String header = cursor.getString(cursor.getColumnIndex(UpgradeSQLite.HEADER)); + this.header = getMapForJson(header); + cursor.close(); + //更新一条SQL + sqLite.update(id, currentLength, maxLength, DownloadStatus.STATUS_PENDING.getValue()); + + return isNewDownload; + } + } else { + // 重新下载 + File parentFile = getDownloadDirectory(); + downloadFile = new File(parentFile.getPath(), apkName); + JSONObject object = null; + if (header != null) { + object = new JSONObject(header); + } + //更新一条SQL + sqLite.update(id, url, downloadFile.getPath(), apkName, object == null ? "" : object.toString(), currentLength, maxLength, DownloadStatus.STATUS_PENDING.getValue()); + return true; + } + } + + private void handlerDownloadRunning() { + try { + if (currentLength - lastCurrentLength > 0) { + double percent = new BigDecimal((currentLength * 1.0f / maxLength) * 100).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue(); + double speed = ((currentLength - lastCurrentLength) * 1000f / (System.currentTimeMillis() - lastTime)) / 1024; + //计划完成时间 + double planTime = (maxLength - currentLength) / (speed * 1024f); + Intent intent = new Intent(); + intent.setAction(DOWNLOAD_STATUS); + intent.putExtra(PARAMS_CURRENT_LENGTH, currentLength); + intent.putExtra(PARAMS_STATUS, DownloadStatus.STATUS_RUNNING.getValue()); + intent.putExtra(PARAMS_PERCENT, percent); + intent.putExtra(PARAMS_MAX_LENGTH, maxLength); + intent.putExtra(PARAMS_SPEED, speed); + intent.putExtra(PARAMS_PLAN_TIME, planTime); + intent.putExtra(PARAMS_PATH, downloadFile.getPath()); + intent.putExtra(PARAMS_ID, id); + intent.putExtra(PARAMS_APK_NAME, apkName); + intent.putExtra(PARAMS_PACKAGE, upgradeService.getPackageName()); + upgradeService.sendBroadcast(intent); + sqLite.update(id, currentLength, maxLength, DownloadStatus.STATUS_RUNNING.getValue()); + RUpgradeLogger.get().d(TAG, "handlerDownloadRunning: running queryTask: 下载中\n" + + "url: " + + url + + "\n============>" + + "total:" + + maxLength + + "," + + "progress:" + + currentLength + + "," + + String.format("%.2f", percent) + + "% , " + + String.format("%.2f", speed) + + "kb/s , " + + "预计:" + + String.format("%.0f", planTime) + + "s"); + lastCurrentLength = currentLength; + lastTime = System.currentTimeMillis(); + } + } catch (NumberFormatException e) { + e.printStackTrace(); + } + } + + private void handlerDownloadCancel() { + RUpgradeLogger.get().d(TAG, "handlerDownloadCancel: "); + cancelTimer(); + Intent intent = new Intent(); + intent.setAction(DOWNLOAD_STATUS); + intent.putExtra(PARAMS_ID, id); + intent.putExtra(PARAMS_APK_NAME, apkName); + intent.putExtra(PARAMS_PATH, downloadFile.getPath()); + intent.putExtra(PARAMS_STATUS, DownloadStatus.STATUS_CANCEL.getValue()); + intent.putExtra(PARAMS_PACKAGE, upgradeService.getPackageName()); + upgradeService.sendBroadcast(intent); + sqLite.delete(id); + } + + private void handlerDownloadPause() { + RUpgradeLogger.get().d(TAG, "handlerDownloadPause: downloadFile:" + downloadFile); + cancelTimer(); + Intent intent = new Intent(); + intent.setAction(DOWNLOAD_STATUS); + intent.putExtra(PARAMS_ID, id); + intent.putExtra(PARAMS_APK_NAME, apkName); + if (downloadFile != null) { + intent.putExtra(PARAMS_PATH, downloadFile.getPath()); + } + intent.putExtra(PARAMS_STATUS, DownloadStatus.STATUS_PAUSED.getValue()); + if (upgradeService != null) { + intent.putExtra(PARAMS_PACKAGE, upgradeService.getPackageName()); + upgradeService.sendBroadcast(intent); + } + if (sqLite != null) { + sqLite.update(id, currentLength, maxLength, DownloadStatus.STATUS_PAUSED.getValue()); + } + } + + private void handlerDownloadFinish() { + RUpgradeLogger.get().d(TAG, "handlerDownloadFinish: finish"); + cancelTimer(); + Intent intent = new Intent(); + intent.setAction(DOWNLOAD_STATUS); + intent.putExtra(PARAMS_ID, id); + intent.putExtra(PARAMS_APK_NAME, apkName); + intent.putExtra(PARAMS_PATH, downloadFile.getPath()); + intent.putExtra(PARAMS_STATUS, DownloadStatus.STATUS_SUCCESSFUL.getValue()); + intent.putExtra(PARAMS_PACKAGE, upgradeService.getPackageName()); + upgradeService.sendBroadcast(intent); + sqLite.update(id, null, null, DownloadStatus.STATUS_SUCCESSFUL.getValue()); + lastCurrentLength = 0; + isFinish = true; + } + + private void handlerDownloadFailure() { + RUpgradeLogger.get().d(TAG, "handlerDownloadFailure: failure"); + Intent intent = new Intent(DOWNLOAD_STATUS); + intent.putExtra(PARAMS_ID, id); + intent.putExtra(PARAMS_APK_NAME, apkName); + intent.putExtra(PARAMS_PATH, downloadFile.getPath()); + intent.putExtra(PARAMS_STATUS, DownloadStatus.STATUS_FAILED.getValue()); + sqLite.update(id, null, null, DownloadStatus.STATUS_FAILED.getValue()); + intent.putExtra(PARAMS_PACKAGE, upgradeService.getPackageName()); + upgradeService.sendBroadcast(intent); + } + + private long getMaxLength(HttpURLConnection connection) { + long maxLength = connection.getContentLength(); + if (maxLength < 0) { + List values = connection.getHeaderFields().get("content-Length"); + if (values != null && !values.isEmpty()) { + String sLength = values.get(0); + if (sLength != null) { + maxLength = Long.parseLong(sLength, 10); + } + } + } + return maxLength; + } + + private InputStream getInputStreamFromUrl(String inputUrl) throws IOException { + InputStream is = null; + URL url = new URL(inputUrl); + int code; + if (inputUrl.startsWith("https")) { + HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + connection.setReadTimeout(6 * 60 * 1000); + connection.setConnectTimeout(6 * 60 * 1000); + if (header != null && !header.isEmpty()) { + for (Map.Entry entry : header.entrySet()) { + connection.setRequestProperty(entry.getKey(), (String) entry.getValue()); + } + } + if (!isNewDownload) { + connection.setRequestProperty("range", "bytes=" + currentLength + "-"); + } + connection.setSSLSocketFactory((SSLSocketFactory) SSLSocketFactory.getDefault()); + connection.setDoInput(true); + code = connection.getResponseCode(); + RUpgradeLogger.get().d(TAG, "run: code=" + code); + if (code == 200 || code == 206) { + connection.connect(); + is = connection.getInputStream(); + if (isNewDownload) { + maxLength = getMaxLength(connection); + } + } else if (code == 301 || code == 302) { + URL redirectUrl = connection.getURL(); + RUpgradeLogger.get().d(TAG, "redirect to: " + redirectUrl.toString()); + is = getInputStreamFromUrl(redirectUrl.toString()); + } + } else { + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + connection.setConnectTimeout(6 * 60 * 1000); + connection.setReadTimeout(6 * 60 * 1000); + connection.setRequestProperty("Accept-Encoding", "identity"); + if (header != null && !header.isEmpty()) { + for (Map.Entry entry : header.entrySet()) { + connection.setRequestProperty(entry.getKey(), (String) entry.getValue()); + } + } + if (!isNewDownload) { + connection.setRequestProperty("range", "bytes=" + currentLength + "-"); + } + connection.setDoInput(true); + code = connection.getResponseCode(); + RUpgradeLogger.get().d(TAG, "run: code=" + code); + if (code == 200 || code == 206) { + connection.connect(); + is = connection.getInputStream(); + if (isNewDownload) { + maxLength = getMaxLength(connection); + } + } else if (code == 301 || code == 302) { + URL redirectUrl = connection.getURL(); + RUpgradeLogger.get().d(TAG, "redirect to: " + redirectUrl.toString()); + is = getInputStreamFromUrl(redirectUrl.toString()); + } + } + return is; + } + + @Override + public void run() { + isNewDownload = handlerDownloadPending(); + //下载文件不存在,但是需要续下载? + if (!downloadFile.exists() && !isNewDownload) { + isNewDownload = true; + currentLength = 0; + + } + timer = new Timer(); + timer.schedule(new TimerTask() { + @Override + public void run() { + handlerDownloadRunning(); + } + }, 500, 500); + + InputStream is = null; + FileOutputStream fos = null; + RandomAccessFile raf = null; + + try { + if (isNewDownload) { + if (downloadFile.exists()) { + downloadFile.delete(); + } + downloadFile.createNewFile(); + fos = new FileOutputStream(downloadFile); + } else { + raf = new RandomAccessFile(downloadFile, "rwd"); + raf.seek(currentLength); + } + is = getInputStreamFromUrl(this.url); + if (is == null) { + handlerDownloadFailure(); + return; + } + assert (is != null); + RUpgradeLogger.get().d(TAG, "run: maxLength:" + maxLength); + + byte[] buff = new byte[1024]; + + int len = 0; + while ((len = is.read(buff)) != -1) { + if (!isRunning) { + break; + } + if (isNewDownload) { + assert (fos != null); + fos.write(buff, 0, len); + } else { + assert (raf != null); + raf.write(buff, 0, len); + } + currentLength += len; + } + if (isNewDownload) { + assert (fos != null); + fos.flush(); + fos.close(); + } else { + assert (raf != null); + raf.close(); + } + is.close(); + if (isRunning) { + handlerDownloadFinish(); + } + } catch (Exception e) { + timer.cancel(); + e.printStackTrace(); + if (is != null) { + try { + is.close(); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + if (fos != null) { + try { + fos.close(); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + if (raf != null) { + try { + raf.close(); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + //防止断网的情况,出现下载失败,而不是下载暂停的问题 + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + if (isRunning) { + handlerDownloadFailure(); + } + } + } + + private Map getMapForJson(String jsonStr) { + if (jsonStr == null || jsonStr.isEmpty()) return null; + + JSONObject jsonObject; + try { + jsonObject = new JSONObject(jsonStr); + + Iterator keyIter = jsonObject.keys(); + String key; + Object value; + Map valueMap = new HashMap(); + while (keyIter.hasNext()) { + key = keyIter.next(); + value = jsonObject.get(key); + valueMap.put(key, value); + } + return valueMap; + } catch (Exception e) { + e.printStackTrace(); + } + return new HashMap(); + } + + } + + private Map getMapForJson(String jsonStr) { + if (jsonStr == null || jsonStr.isEmpty()) return null; + + JSONObject jsonObject; + try { + jsonObject = new JSONObject(jsonStr); + + Iterator keyIter = jsonObject.keys(); + String key; + Object value; + Map valueMap = new HashMap(); + while (keyIter.hasNext()) { + key = keyIter.next(); + value = jsonObject.get(key); + valueMap.put(key, value); + } + return valueMap; + } catch (Exception e) { + e.printStackTrace(); + } + return new HashMap(); + } + + +} + diff --git a/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/install/BaseInstallFactory.java b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/install/BaseInstallFactory.java new file mode 100644 index 000000000..e5c239470 --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/install/BaseInstallFactory.java @@ -0,0 +1,14 @@ +package com.example.r_upgrade.common.install; + +import android.content.Context; +import android.content.ContextWrapper; +import android.net.Uri; + +public abstract class BaseInstallFactory extends ContextWrapper { + + public BaseInstallFactory(Context base) { + super(base); + } + + public abstract boolean install(Uri uri); +} diff --git a/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/install/NormalInstallFactory.java b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/install/NormalInstallFactory.java new file mode 100644 index 000000000..4c0fade84 --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/install/NormalInstallFactory.java @@ -0,0 +1,32 @@ +package com.example.r_upgrade.common.install; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Build; + +import com.example.r_upgrade.common.RUpgradeLogger; + +public class NormalInstallFactory extends BaseInstallFactory { + + private static final String TAG = "NormalInstall"; + + public NormalInstallFactory(Context base) { + super(base); + } + + @Override + public boolean install(Uri uri) { + Intent install = new Intent(Intent.ACTION_VIEW); + RUpgradeLogger.get().d(TAG, uri.toString()); + install.setDataAndType(uri, "application/vnd.android.package-archive"); + install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + } else { + install.addCategory(Intent.CATEGORY_DEFAULT); + } + startActivity(install); + return true; + } +} diff --git a/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/install/SilentInstallFactory.java b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/install/SilentInstallFactory.java new file mode 100644 index 000000000..04d02f1f9 --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/install/SilentInstallFactory.java @@ -0,0 +1,150 @@ +package com.example.r_upgrade.common.install; + +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageInstaller; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Build; + +import androidx.annotation.RequiresApi; + +import com.example.r_upgrade.common.RUpgradeLogger; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class SilentInstallFactory extends BaseInstallFactory { + private static final String TAG = "SilentInstall"; + + public SilentInstallFactory(Context base) { + super(base); + } + + @Override + public boolean install(Uri uri) { + if (uri == null) return false; + File apkFile = new File(uri.getPath()); + if (!apkFile.exists()) { + return false; + } + RUpgradeLogger.get().d(TAG, "startSilentInstall:file path" + apkFile.getPath()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + PackageInstaller installer = getPackageManager().getPackageInstaller(); + PackageInfo packageInfo = getPackageManager().getPackageArchiveInfo(apkFile.getPath(), PackageManager.GET_ACTIVITIES | + PackageManager.GET_SERVICES); + PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( + PackageInstaller.SessionParams.MODE_FULL_INSTALL + ); + if (packageInfo != null) { + String packageName = packageInfo.packageName; + params.setAppPackageName(packageName); + } + params.setSize(apkFile.length()); + int sessionId = createSession(installer, params); + + PackageInstaller.Session session = copyInstallFile(installer, sessionId, apkFile.getPath()); + if (session != null) { + PendingIntent broadCastTest = PendingIntent.getBroadcast( + this, + sessionId, + new Intent(Intent.ACTION_PACKAGE_ADDED), + getPendingIntentFlag()); + session.commit(broadCastTest.getIntentSender()); + session.close(); + } + } + return true; + } + + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + private void closeQuietly(Object obj) { + if (obj != null) { + if (obj instanceof PackageInstaller.Session) { + ((PackageInstaller.Session) obj).close(); + } else if (obj instanceof FileOutputStream) { + try { + ((FileOutputStream) obj).close(); + } catch (IOException e) { + e.printStackTrace(); + } + } else if (obj instanceof FileInputStream) { + try { + ((FileInputStream) obj).close(); + } catch (IOException e) { + e.printStackTrace(); + } + } else if (obj instanceof OutputStream) { + try { + ((OutputStream) obj).close(); + } catch (IOException e) { + e.printStackTrace(); + } + } else if (obj instanceof InputStream) { + try { + ((InputStream) obj).close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + private int createSession(PackageInstaller packageInstaller, + PackageInstaller.SessionParams sessionParams) { + int sessionId = -1; + try { + sessionId = packageInstaller.createSession(sessionParams); + } catch (IOException e) { + e.printStackTrace(); + } + return sessionId; + } + + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + private PackageInstaller.Session copyInstallFile(PackageInstaller packageInstaller, + int sessionId, String apkFilePath) { + InputStream in = null; + OutputStream out = null; + PackageInstaller.Session session = null; + try { + File apkFile = new File(apkFilePath); + session = packageInstaller.openSession(sessionId); + out = session.openWrite("app_store_session", 0, apkFile.length()); + in = new FileInputStream(apkFile); + int total = 0; + byte[] buffer = new byte[65536]; + int len; + while ((len = in.read(buffer)) != -1) { + total += len; + out.write(buffer, 0, len); + RUpgradeLogger.get().d(TAG, "Write install session:" + len); + } + RUpgradeLogger.get().d(TAG, "Finish session total size:" + total); + session.fsync(out); + return session; + } catch (IOException e) { + e.printStackTrace(); + } finally { + closeQuietly(in); + closeQuietly(session); + closeQuietly(out); + } + return null; + } + + private static int getPendingIntentFlag() { + int pendingFlag = PendingIntent.FLAG_UPDATE_CURRENT; + if (Build.VERSION.SDK_INT >= 31) { + pendingFlag = PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT; + } + return pendingFlag; + } +} diff --git a/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/manager/HotUpgradeManager.java b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/manager/HotUpgradeManager.java new file mode 100644 index 000000000..52629c6fa --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/manager/HotUpgradeManager.java @@ -0,0 +1,140 @@ +package com.example.r_upgrade.common.manager; + +import android.content.Context; +import android.content.ContextWrapper; +import android.net.Uri; +import android.os.ParcelFileDescriptor; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Enumeration; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import io.flutter.util.PathUtils; + + +public class HotUpgradeManager extends ContextWrapper { + private static final String TAG = "HotUpgradeManager"; + private static final String FLUTTER_ASSETS = "flutter_assets"; + + public HotUpgradeManager(Context context) { + super(context); + } + + private File getFlutterAssets() { + return new File(PathUtils.getDataDirectory(this)); + } + + private File getHotAssets() { + File file; + file = new File(getFlutterAssets(), System.currentTimeMillis() + ".zip"); + if (!file.exists()) { + try { + boolean isSuccess = file.createNewFile(); + return isSuccess ? file : null; + } catch (IOException e) { + e.printStackTrace(); + } + } + return file; + } + + private void deleteFlutterAssets() { + File file = new File(getFlutterAssets(), FLUTTER_ASSETS); + if (file.exists()) { + if (file.isDirectory()) { + for (File item : file.listFiles()) { + if (item.exists()) { + item.delete(); + } + } + } + file.delete(); + } + } + + public Boolean hotUpgrade(Uri uri) { + //获取文件流 + try { + //复制下载的文件到资源文件中 + ParcelFileDescriptor descriptor = getContentResolver().openFileDescriptor(uri, "r"); + FileDescriptor fileDescriptor = descriptor.getFileDescriptor(); + if (fileDescriptor == null) return false; + FileInputStream stream = new FileInputStream(fileDescriptor); + File zipFile = getHotAssets(); + FileOutputStream outputStream = new FileOutputStream(zipFile); + byte[] buffer = new byte[1024]; + int byteRead; + while (-1 != (byteRead = stream.read(buffer))) { + outputStream.write(buffer, 0, byteRead); + } + stream.close(); + outputStream.flush(); + outputStream.close(); + + deleteFlutterAssets(); + unZipFile(zipFile.getPath(), getFlutterAssets().getPath() + File.separator + FLUTTER_ASSETS, true); + return true; + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + return false; + } + + /** + * @param archive 解压文件得路径 + * @param decompressDir 解压文件目标路径 + * @param isDeleteZip 解压完毕是否删除解压文件 + * @throws IOException + */ + public static void unZipFile(String archive, String decompressDir, boolean isDeleteZip) throws IOException { + BufferedInputStream bi; + ZipFile zf = new ZipFile(archive); + Enumeration e = zf.entries(); + while (e.hasMoreElements()) { + ZipEntry ze2 = (ZipEntry) e.nextElement(); + String entryName = ze2.getName(); + String path = decompressDir + "/" + entryName; + if (ze2.isDirectory()) { + File decompressDirFile = new File(path); + if (!decompressDirFile.exists()) { + decompressDirFile.mkdirs(); + } + } else { + if (decompressDir.endsWith(".zip")) { + decompressDir = decompressDir.substring(0, decompressDir.lastIndexOf(".zip")); + } + File fileDirFile = new File(decompressDir); + if (!fileDirFile.exists()) { + fileDirFile.mkdirs(); + } + String substring = entryName.substring(entryName.lastIndexOf("/") + 1, entryName.length()); + BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(decompressDir + "/" + substring)); + bi = new BufferedInputStream(zf.getInputStream(ze2)); + byte[] readContent = new byte[1024]; + int readCount = bi.read(readContent); + while (readCount != -1) { + bos.write(readContent, 0, readCount); + readCount = bi.read(readContent); + } + bos.close(); + } + } + zf.close(); + if (isDeleteZip) { + File zipFile = new File(archive); + if (zipFile.exists() && zipFile.getName().endsWith(".zip")) { + zipFile.delete(); + } + } + } +} diff --git a/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/manager/IncrementUpgradeManager.java b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/manager/IncrementUpgradeManager.java new file mode 100644 index 000000000..dfd71826c --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/manager/IncrementUpgradeManager.java @@ -0,0 +1,50 @@ +package com.example.r_upgrade.common.manager; + +import android.content.Context; +import android.content.ContextWrapper; +import android.os.Environment; +import android.util.Log; + + +import com.example.r_upgrade.common.RUpgradeLogger; +import com.example.r_upgrade_lib.RUpgradeLib; + +import java.io.File; + +public class IncrementUpgradeManager extends ContextWrapper { + private static final String TAG = "r_upgrade.Increment"; + private String oldApkPath; + private RUpgradeLib rUpgradeLib; + + public IncrementUpgradeManager(Context base) { + super(base); + oldApkPath = base.getPackageResourcePath(); + rUpgradeLib = new RUpgradeLib(); + } + + public String mixinAndGetNewApk(String patchPath) { + File parentFile = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); + File newApkFile = new File(parentFile, oldApkPath.substring(oldApkPath.lastIndexOf("/") + 1)); + try { + File patchFile = new File(patchPath); + if (!patchFile.exists()) { + return null; + } + if (patchFile.length() == 0) { + return null; + } + if (newApkFile.exists()) { + newApkFile.delete(); + } + newApkFile.createNewFile(); + rUpgradeLib.mixinPatch(oldApkPath, patchPath, newApkFile.getPath()); + RUpgradeLogger.get().d(TAG, "mixinAndGetNewApk" + newApkFile.getPath()); + return newApkFile.getPath(); + } catch (Exception e) { + RUpgradeLogger.get().d(TAG, "合成失败:"); + e.printStackTrace(); + } + return null; + } + +} diff --git a/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/manager/UpgradeManager.java b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/manager/UpgradeManager.java new file mode 100644 index 000000000..1f8efb9bf --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/manager/UpgradeManager.java @@ -0,0 +1,591 @@ +package com.example.r_upgrade.common.manager; + +import android.app.Activity; +import android.app.DownloadManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.ContextWrapper; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.os.Environment; +import android.text.TextUtils; + +import androidx.annotation.Nullable; + +import com.example.r_upgrade.common.DownloadStatus; +import com.example.r_upgrade.common.RUpgradeLogger; +import com.example.r_upgrade.common.ResultMap; +import com.example.r_upgrade.common.DownloadPermissions; +import com.example.r_upgrade.common.UpgradeNotification; +import com.example.r_upgrade.common.UpgradeNotificationStyle; +import com.example.r_upgrade.common.UpgradeSQLite; +import com.example.r_upgrade.common.UpgradeService; +import com.example.r_upgrade.common.install.BaseInstallFactory; +import com.example.r_upgrade.common.install.NormalInstallFactory; +import com.example.r_upgrade.common.install.SilentInstallFactory; +import com.example.r_upgrade.common.tasks.CheckGooglePlayVersionTask; +import com.example.r_upgrade.common.tasks.CheckTencentStoreVersionTask; +import com.example.r_upgrade.common.tasks.CheckXiaoMiStoreVersionTask; +import com.example.r_upgrade.common.tasks.GenerateAndInstallAsyncTask; +import com.example.r_upgrade.common.tasks.GenerateAndInstallByPathAsyncTask; +import com.example.r_upgrade.common.tasks.VersionCallBack; + +import org.json.JSONObject; + +import java.io.File; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; + +import io.flutter.plugin.common.MethodChannel; + + +public class UpgradeManager extends ContextWrapper { + private static final String TAG = "r_upgrade.Manager"; + //广播的action + public static final String DOWNLOAD_STATUS = "com.example.r_upgrade.DOWNLOAD_STATUS"; + public static final String DOWNLOAD_INSTALL = "com.example.r_upgrade.DOWNLOAD_INSTALL"; + + public static final String PARAMS_ID = "id"; + public static final String PARAMS_STATUS = "status"; + public static final String PARAMS_CURRENT_LENGTH = "current_length"; + public static final String PARAMS_MAX_LENGTH = "max_length"; + public static final String PARAMS_PLAN_TIME = "plan_time"; + public static final String PARAMS_SPEED = "speed"; + public static final String PARAMS_PERCENT = "percent"; + public static final String PARAMS_PATH = "path"; + public static final String PARAMS_APK_NAME = "apk_name"; + public static final String PARAMS_PACKAGE = "packages"; + //速度 + private double lastProgress = 0; + //最后更新的时间 + private long lastTime = 0; + + private Timer timer; + + private BaseInstallFactory installFactory; + + private boolean isUseDownloadManager = false; + + private Integer notificationVisibility = 0; + private UpgradeNotificationStyle notificationStyle = UpgradeNotificationStyle.none; + + + private BroadcastReceiver downloadReceiver; + + private MethodChannel channel; + + private DownloadPermissions.PermissionsRegistry permissionsRegistry; + private DownloadPermissions downloadPermissions; + private Activity activity; + + + public void dispose() { + unregisterReceiver(downloadReceiver); + } + + public UpgradeManager(Activity base, MethodChannel channel, DownloadPermissions storagePermissions, DownloadPermissions.PermissionsRegistry permissionsRegistry) { + super(base); + this.activity = base; + this.downloadPermissions = storagePermissions; + this.permissionsRegistry = permissionsRegistry; + this.channel = channel; + UpgradeSQLite.getInstance(this).pauseDownloading(); + IntentFilter filter = new IntentFilter(); + filter.addAction(DownloadManager.ACTION_DOWNLOAD_COMPLETE); + filter.addAction(UpgradeManager.DOWNLOAD_STATUS); + filter.addAction(UpgradeManager.DOWNLOAD_INSTALL); + downloadReceiver = createBroadcastReceiver(); + registerReceiver(downloadReceiver, filter); + } + + + public void upgrade(final String url, final Map header, final String apkName, final Integer notificationVisibility, Integer notificationStyle, Integer installType, Boolean useDownloadManager, final Integer upgradeFlavor, final MethodChannel.Result result) { + installFactory = installTypeToFactory(installType); + this.isUseDownloadManager = Boolean.TRUE == useDownloadManager; + if (notificationStyle != null) { + this.notificationStyle = UpgradeNotificationStyle.values()[notificationStyle]; + } else { + this.notificationStyle = UpgradeNotificationStyle.none; + } + this.notificationVisibility = notificationVisibility; + + downloadPermissions.requestPermissions(activity, permissionsRegistry, notificationVisibility,new DownloadPermissions.ResultCallback() { + @Override + public void onResult(String errorCode, String errorDescription) { + if (errorCode == null) { + long id = 0; + + if (isUseDownloadManager) { + DownloadManager manager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE); + DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url)); + if (header != null) { + for (Map.Entry entry : header.entrySet()) { + request.addRequestHeader(entry.getKey(), entry.getValue()); + } + } + if (notificationVisibility != null) { + request.setNotificationVisibility(notificationVisibility); + } else { + request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); + } + request.setMimeType("application/vnd.android.package-archive"); + + request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, apkName == null ? "release.apk" : apkName); + + request.setTitle(apkName == null ? "upgradePackage.apk" : apkName); + id = manager.enqueue(request); + if (timer != null) { + timer.cancel(); + } + timer = new Timer(); + final long finalId = id; + timer.schedule(new TimerTask() { + @Override + public void run() { + queryTask(finalId); + } + }, 0, 500); + RUpgradeLogger.get().d(TAG, "upgrade: " + id); + } else { + id = UpgradeSQLite.getInstance(activity).createRecord(activity, url, apkName, header == null ? "" : new JSONObject(header).toString(), DownloadStatus.STATUS_PENDING.getValue(), upgradeFlavor); + + Intent intent = new Intent(activity, UpgradeService.class); + Bundle bundle = new Bundle(); + bundle.putBoolean(UpgradeService.DOWNLOAD_RESTART, false); + bundle.putInt(UpgradeService.DOWNLOAD_ID, (int) id); + bundle.putString(UpgradeService.DOWNLOAD_URL, url); + bundle.putString(UpgradeService.DOWNLOAD_APK_NAME, apkName); + bundle.putSerializable(UpgradeService.DOWNLOAD_Header, (Serializable) header); + intent.putExtras(bundle); + startService(intent); + } + result.success(id); + } else { + result.error(errorCode, errorDescription, null); + } + } + }); + } + + //取消下载 + public boolean cancel(Integer id) { + if (id == null) return false; + if (isUseDownloadManager) { + DownloadManager manager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE); + return manager.remove(id) == 1; + } else { + Intent intent = new Intent(UpgradeService.RECEIVER_CANCEL); + intent.putExtra(PARAMS_PACKAGE, getPackageName()); + intent.putExtra(PARAMS_ID, id); + sendBroadcast(intent); + return true; + } + } + + //暂停下载 + public boolean pause(Integer id) { + if (id == null) return false; + Intent intent = new Intent(UpgradeService.RECEIVER_PAUSE); + intent.putExtra(PARAMS_PACKAGE, getPackageName()); + intent.putExtra(PARAMS_ID, id); + sendBroadcast(intent); + return true; + } + + + //查询进度 + public void queryTask(long id) { + DownloadManager manager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE); + DownloadManager.Query query = new DownloadManager.Query(); + Cursor cursor = manager.query(query.setFilterById(id)); + if (cursor != null && cursor.moveToFirst()) { + int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)); + Intent intent = new Intent(); + switch (status) { + case DownloadManager.STATUS_PAUSED: + RUpgradeLogger.get().d(TAG, "queryTask: 下载被暂停"); + intent.setAction(DOWNLOAD_STATUS); + intent.putExtra(PARAMS_STATUS, DownloadStatus.STATUS_PAUSED.getValue()); + intent.putExtra(PARAMS_ID, id); + intent.putExtra(PARAMS_PACKAGE, getPackageName()); + sendBroadcast(intent); + break; + case DownloadManager.STATUS_PENDING: + RUpgradeLogger.get().d(TAG, "queryTask: 下载延迟==========>总大小:"); + intent.setAction(DOWNLOAD_STATUS); + intent.putExtra(PARAMS_STATUS, DownloadStatus.STATUS_PENDING.getValue()); + intent.putExtra(PARAMS_ID, id); + intent.putExtra(PARAMS_PACKAGE, getPackageName()); + sendBroadcast(intent); + break; + case DownloadManager.STATUS_RUNNING: + //已经下载的字节数 + int progress = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)); + if (lastProgress == 0) { + lastProgress = progress; + lastTime = System.currentTimeMillis(); + } + //下载的文件到本地的目录 + String address = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)); + //总需下载的字节数 + int total = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)); + //下载速度 + double speed = ((progress - lastProgress) * 1000f / (System.currentTimeMillis() - lastTime)) / 1024; + //下载文件的URL链接 + String url = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_URI)); + + //计划完成时间 + double planTime = (total - progress) / (speed * 1024f); + //当前进度 + double percent = new BigDecimal((progress * 1.0f / total) * 100).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue(); + if (progress - lastProgress > 0) { + RUpgradeLogger.get().d(TAG, "queryTask: 下载中\n" + + "url: " + + url + + "\n============>" + + "total:" + + total + + "," + + "progress:" + + progress + + "," + + String.format(Locale.getDefault(), "%.2f", percent) + + "% , " + + String.format(Locale.getDefault(), "%.2f", speed) + + "kb/s , " + + "预计:" + + String.format(Locale.getDefault(), "%.0f", planTime) + + "s"); + intent.setAction(DOWNLOAD_STATUS); + + intent.putExtra(PARAMS_ID, id); + intent.putExtra(PARAMS_CURRENT_LENGTH, progress); + intent.putExtra(PARAMS_STATUS, DownloadStatus.STATUS_RUNNING.getValue()); + intent.putExtra(PARAMS_PERCENT, percent); + intent.putExtra(PARAMS_MAX_LENGTH, total); + intent.putExtra(PARAMS_SPEED, speed); + intent.putExtra(PARAMS_PLAN_TIME, planTime); + intent.putExtra(PARAMS_PATH, address); + intent.putExtra(PARAMS_PACKAGE, getPackageName()); + sendBroadcast(intent); + lastProgress = progress; + lastTime = System.currentTimeMillis(); + } + break; + case DownloadManager.STATUS_SUCCESSFUL: + RUpgradeLogger.get().d(TAG, "queryTask: 下载成功"); + if (timer != null) { + timer.cancel(); + timer = null; + } + installApkById((int) id); + intent.setAction(DOWNLOAD_STATUS); + intent.putExtra(PARAMS_STATUS, DownloadStatus.STATUS_SUCCESSFUL.getValue()); + intent.putExtra(PARAMS_ID, id); + intent.putExtra(PARAMS_PACKAGE, getPackageName()); + sendBroadcast(intent); + lastProgress = 0; + break; + case DownloadManager.STATUS_FAILED: + RUpgradeLogger.get().d(TAG, "queryTask: 下载失败"); + intent.setAction(DOWNLOAD_STATUS); + intent.putExtra(PARAMS_STATUS, DownloadStatus.STATUS_FAILED.getValue()); + intent.putExtra(PARAMS_ID, id); + intent.putExtra(PARAMS_PACKAGE, getPackageName()); + sendBroadcast(intent); + lastProgress = 0; + break; + } + } + if (cursor != null) { + cursor.close(); + } + } + + + public void installApkById(int id) { + installApkById(id, -1, null); + } + + public void installApkById(final int id, int installType, final MethodChannel.Result result) { + if (installType != -1) { + installFactory = installTypeToFactory(installType); + } + if (installFactory == null) return; + downloadPermissions.requestPermissions(activity, permissionsRegistry,notificationVisibility, new DownloadPermissions.ResultCallback() { + @Override + public void onResult(String errorCode, String errorDescription) { + if (errorCode == null) { + new GenerateAndInstallAsyncTask(activity, isUseDownloadManager, result, installFactory).execute(id); + } else { + if (result != null) { + result.error(errorCode, errorDescription, null); + } + } + } + }); + } + + private BaseInstallFactory installTypeToFactory(int installType) { + BaseInstallFactory installFactory; + if (installType == 0) { + installFactory = new NormalInstallFactory(this); + } else if (installType == 1) { + installFactory = new SilentInstallFactory(this); + } else { + installFactory = null; + } + return installFactory; + } + + + public void installApkByPath(final String path, final int upgradeFlavor, int installType, final MethodChannel.Result result) { + installFactory = installTypeToFactory(installType); + + downloadPermissions.requestPermissions(activity, permissionsRegistry,notificationVisibility, new DownloadPermissions.ResultCallback() { + @Override + public void onResult(String errorCode, String errorDescription) { + if (errorCode == null) { + File installPath = new File(path); + if (installPath.exists()) { + new GenerateAndInstallByPathAsyncTask(activity, path, upgradeFlavor, result, installFactory).execute(); + + } else { + result.error("file not exists", "file path:" + path + " is not exists", null); + } + } else { + if (result != null) { + result.error(errorCode, errorDescription, null); + } + } + } + }); + } + + + public BroadcastReceiver createBroadcastReceiver() { + return new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String packageName = intent.getStringExtra(PARAMS_PACKAGE); + if (packageName == null || !packageName.equals(getPackageName())) { + return; + } + if (intent != null && intent.getAction() != null && intent.getAction().equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) { + if (timer != null) { + timer.cancel(); + timer = null; + } + long id = intent.getLongExtra("extra_download_id", 0); + queryTask(id); + } else if (intent != null && intent.getAction() != null && intent.getAction().equals(UpgradeManager.DOWNLOAD_STATUS)) { + + final long current_length = intent.getLongExtra(PARAMS_CURRENT_LENGTH, 0); + final long max_length = intent.getLongExtra(PARAMS_MAX_LENGTH, 0); + + double percent = intent.getDoubleExtra(PARAMS_PERCENT, 0); + + double speed = intent.getDoubleExtra(PARAMS_SPEED, 0); + + double planTime = intent.getDoubleExtra(PARAMS_PLAN_TIME, 0); + + int status = intent.getIntExtra(PARAMS_STATUS, 1); + + String apkName = intent.getStringExtra(PARAMS_APK_NAME); + + String path = intent.getStringExtra(PARAMS_PATH); + + long id = intent.getLongExtra(PARAMS_ID, 0L); + + if (!isUseDownloadManager) { + String contentText = notificationStyle == null ? "" : notificationStyle.getNotificationStyleString(context, speed, planTime); + if ((status == DownloadStatus.STATUS_RUNNING.getValue() || status == DownloadStatus.STATUS_SUCCESSFUL.getValue()) && notificationVisibility == 1) { + UpgradeNotification.createNotification(context, (int) id, apkName, max_length == -1, percent, contentText, status); + } else if (notificationVisibility == 0) { + UpgradeNotification.createNotification(context, (int) id, apkName, max_length == -1, percent, contentText, status); + } else if (status == DownloadStatus.STATUS_SUCCESSFUL.getValue() && notificationVisibility == 3) { + UpgradeNotification.createNotification(context, (int) id, apkName, max_length == -1, percent, contentText, status); + } + if (status == DownloadStatus.STATUS_SUCCESSFUL.getValue()) { + installApkById((int) id); + } + } + if (channel != null) + channel.invokeMethod("update", ResultMap.getInstance() + .pubClear(PARAMS_CURRENT_LENGTH, current_length) + .put(PARAMS_ID, id) + .put(PARAMS_PERCENT, percent) + .put(PARAMS_PLAN_TIME, planTime) + .put(PARAMS_STATUS, status) + .put(PARAMS_SPEED, speed) + .put(PARAMS_MAX_LENGTH, max_length) + .put(PARAMS_PATH, path) + .getMap()); + + } else if (intent != null && intent.getAction() != null && intent.getAction().equals(UpgradeManager.DOWNLOAD_INSTALL)) { + int id = intent.getIntExtra(UpgradeService.DOWNLOAD_ID, 0); + installApkById(id); + UpgradeNotification.removeNotification(context, id); + } + } + }; + } + + public Integer getLastUpgradedId() { + String versionName = ""; + int versionCode = 0; + try { + PackageManager manager = this.getPackageManager(); + PackageInfo info = manager.getPackageInfo(this.getPackageName(), 0); + versionName = info.versionName; + versionCode = info.versionCode; + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + return UpgradeSQLite.getInstance(this).queryIdByVersionNameAndVersionCode(versionName, versionCode); + } + + public void upgradeWithId(final Integer id, Integer notificationVisibility, int installType, final MethodChannel.Result methodResult) { + this.notificationVisibility = notificationVisibility; + installFactory = installTypeToFactory(installType); + + final Map result = UpgradeSQLite.getInstance(this).queryById(id); + if (result == null) { + methodResult.success(false); + return; + } + String path = (String) result.get(UpgradeSQLite.PATH); + File downloadFile = new File(path); + + int status = (int) result.get(UpgradeSQLite.STATUS); + if (status == DownloadStatus.STATUS_PAUSED.getValue() || status == DownloadStatus.STATUS_FAILED.getValue() + || status == DownloadStatus.STATUS_CANCEL.getValue() || !downloadFile.exists()) { + downloadPermissions.requestPermissions(activity, permissionsRegistry, notificationVisibility,new DownloadPermissions.ResultCallback() { + @Override + public void onResult(String errorCode, String errorDescription) { + if (errorCode == null) { + Intent intent = new Intent(activity, UpgradeService.class); + Bundle bundle = new Bundle(); + bundle.putBoolean(UpgradeService.DOWNLOAD_RESTART, true); + bundle.putInt(UpgradeService.DOWNLOAD_ID, (int) id); + bundle.putString(UpgradeService.DOWNLOAD_URL, (String) result.get(UpgradeSQLite.URL)); + bundle.putString(UpgradeService.DOWNLOAD_APK_NAME, (String) result.get(UpgradeSQLite.APK_NAME)); + bundle.putSerializable(UpgradeService.DOWNLOAD_Header, (Serializable) result.get(UpgradeSQLite.HEADER)); + intent.putExtras(bundle); + startService(intent); + methodResult.success(true); + } else { + methodResult.error(errorCode, errorDescription, null); + } + } + }); + } else if (status == DownloadStatus.STATUS_SUCCESSFUL.getValue()) { + installApkById(id, -1, methodResult); + } else { + // not known + methodResult.success(false); + } + } + + public Integer getDownloadStatus(Integer id) { + return UpgradeSQLite.getInstance(this).queryStatusById(id); + + } + + public boolean upgradeFromUrl(String url) { + Uri uri = Uri.parse(url); + try { + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); + return true; + } catch (Exception e) { + return false; + } + } + + public boolean upgradeFromAndroidStore(String store) { + Uri uri = Uri.parse("market://details?id=" + this.getApplicationInfo().packageName); + try { + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + if (store != null) { + intent.setPackage(store); + } + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); + return true; + } catch (Exception e) { + return false; + } + } + + public List getAndroidStores() { + List pkgs = new ArrayList<>(); + Intent intent = new Intent(); + intent.setAction("android.intent.action.VIEW"); + intent.addCategory(Intent.CATEGORY_DEFAULT); + intent.setData(Uri.parse("market://details?id=")); + PackageManager pm = this.getPackageManager(); + // 通过queryIntentActivities获取ResolveInfo对象 + List infoList = pm.queryIntentActivities(intent, + 0); + if (infoList == null || infoList.size() == 0) + return pkgs; + int size = infoList.size(); + for (int i = 0; i < size; i++) { + String pkgName = ""; + try { + ActivityInfo activityInfo = infoList.get(i).activityInfo; + pkgName = activityInfo.packageName; + } catch (Exception e) { + e.printStackTrace(); + } + if (!TextUtils.isEmpty(pkgName)) + pkgs.add(pkgName); + } + return pkgs; + } + + public void getVersionFromAndroidStore(@Nullable String store, final MethodChannel.Result result) { + if (store == null) { + result.error("-1", "Please enter the package name.", null); + return; + } + VersionCallBack callBack = new VersionCallBack() { + @Override + public void versionName(String version) { + result.success(version); + } + }; + switch (store) { + case "com.android.vending": + new CheckGooglePlayVersionTask(getPackageName(), callBack).execute(); + break; + case "com.xiaomi.market": + new CheckXiaoMiStoreVersionTask(getPackageName(), callBack).execute(); + break; + case "com.tencent.android.qqdownloader": + new CheckTencentStoreVersionTask(getPackageName(), callBack).execute(); + break; + default: + result.error("-2", "Not Found AndroidStore.", null); + break; + } + } +} + + + diff --git a/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/tasks/CheckGooglePlayVersionTask.java b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/tasks/CheckGooglePlayVersionTask.java new file mode 100644 index 000000000..67519e07e --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/tasks/CheckGooglePlayVersionTask.java @@ -0,0 +1,43 @@ +package com.example.r_upgrade.common.tasks; + +import android.os.AsyncTask; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Element; + +import java.io.IOException; + +public class CheckGooglePlayVersionTask extends AsyncTask { + private final String packageName; + private final VersionCallBack callBack; + + public CheckGooglePlayVersionTask(String packageName, VersionCallBack callBack) { + this.packageName = packageName; + this.callBack = callBack; + } + + @Override + protected String doInBackground(String... strings) { + String latestVersion = ""; + try { + Element element = Jsoup.connect("https://play.google.com/store/apps/details?id=" + packageName) + .timeout(30000) + .referrer("http://www.google.com") + .get() + .select("div.hAyfc:nth-child(4) > span:nth-child(2) > div:nth-child(1) > span:nth-child(1)") + .first(); + if (element != null) { + latestVersion = element.ownText(); + } + } catch (IOException e) { + e.printStackTrace(); + } + return latestVersion; + } + + @Override + protected void onPostExecute(String s) { + super.onPostExecute(s); + callBack.versionName(s); + } +} diff --git a/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/tasks/CheckTencentStoreVersionTask.java b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/tasks/CheckTencentStoreVersionTask.java new file mode 100644 index 000000000..f178f9efa --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/tasks/CheckTencentStoreVersionTask.java @@ -0,0 +1,43 @@ +package com.example.r_upgrade.common.tasks; + +import android.os.AsyncTask; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; + +import java.io.IOException; + +public class CheckTencentStoreVersionTask extends AsyncTask { + private final String packageName; + private final VersionCallBack callBack; + + public CheckTencentStoreVersionTask(String packageName, VersionCallBack callBack) { + this.packageName = packageName; + this.callBack = callBack; + } + + @Override + protected String doInBackground(String... strings) { + String latestVersion = ""; + try { + Document document = Jsoup.connect("https://a.app.qq.com/o/simple.jsp?pkgname="+packageName) + .timeout(30000) + .get(); + Element element = document.select("div.pp-comp > p.pp-comp-extra-p:eq(1)") + .first(); + if (element != null) { + latestVersion = element.ownText().split(":")[1]; + } + } catch (IOException e) { + e.printStackTrace(); + } + return latestVersion; + } + + @Override + protected void onPostExecute(String s) { + super.onPostExecute(s); + callBack.versionName(s); + } +} diff --git a/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/tasks/CheckXiaoMiStoreVersionTask.java b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/tasks/CheckXiaoMiStoreVersionTask.java new file mode 100644 index 000000000..a60f94f6d --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/tasks/CheckXiaoMiStoreVersionTask.java @@ -0,0 +1,43 @@ +package com.example.r_upgrade.common.tasks; + +import android.os.AsyncTask; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Element; + +import java.io.IOException; + +public class CheckXiaoMiStoreVersionTask extends AsyncTask { + private final String packageName; + private final VersionCallBack callBack; + + public CheckXiaoMiStoreVersionTask(String packageName, VersionCallBack callBack) { + this.packageName = packageName; + this.callBack = callBack; + } + + @Override + protected String doInBackground(String... strings) { + String latestVersion = ""; + try { + Element element = Jsoup.connect("https://app.mi.com/details?id="+packageName) + .timeout(30000) + .referrer("https://app.mi.com") + .get() + .select("div.container:nth-child(1) > div:nth-child(6) > div:nth-child(1) > div:nth-child(2)") + .first(); + if (element != null) { + latestVersion = element.ownText(); + } + } catch (IOException e) { + e.printStackTrace(); + } + return latestVersion; + } + + @Override + protected void onPostExecute(String s) { + super.onPostExecute(s); + callBack.versionName(s); + } +} diff --git a/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/tasks/GenerateAndInstallAsyncTask.java b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/tasks/GenerateAndInstallAsyncTask.java new file mode 100644 index 000000000..4ff0e7d8a --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/tasks/GenerateAndInstallAsyncTask.java @@ -0,0 +1,133 @@ +package com.example.r_upgrade.common.tasks; + +import android.app.DownloadManager; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Build; + +import com.example.r_upgrade.RUpgradeFileProvider; +import com.example.r_upgrade.common.install.BaseInstallFactory; +import com.example.r_upgrade.common.install.SilentInstallFactory; +import com.example.r_upgrade.common.manager.HotUpgradeManager; +import com.example.r_upgrade.common.manager.IncrementUpgradeManager; +import com.example.r_upgrade.common.RUpgradeLogger; +import com.example.r_upgrade.common.UpgradeSQLite; + +import java.io.File; +import java.lang.ref.WeakReference; +import java.util.Map; + +import io.flutter.plugin.common.MethodChannel; + +import static android.content.Context.DOWNLOAD_SERVICE; + +public class GenerateAndInstallAsyncTask extends AsyncTask { + private static final String TAG = "r_upgrade.AsyncTask"; + final WeakReference contextWrapper; + boolean isUserDownloadManager; + MethodChannel.Result result; + private BaseInstallFactory installFactory; + + public GenerateAndInstallAsyncTask(Context context, boolean isUserDownloadManager, MethodChannel.Result result, + BaseInstallFactory installFactory) { + this.contextWrapper = new WeakReference(context); + this.isUserDownloadManager = isUserDownloadManager; + this.result = result; + this.installFactory = installFactory; + } + + @Override + protected Uri doInBackground(Integer... integers) { + Uri uri = null; + try { + int id = integers[0]; + if (this.isUserDownloadManager) { + DownloadManager manager = (DownloadManager) contextWrapper.get().getSystemService(DOWNLOAD_SERVICE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + uri = manager.getUriForDownloadedFile(id); + } else { + DownloadManager.Query query = new DownloadManager.Query(); + Cursor cursor = manager.query(query.setFilterById(id)); + cursor.moveToNext(); + String address = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)); + uri = Uri.parse(address); + cursor.close(); + } + } else { + Map map = UpgradeSQLite.getInstance(contextWrapper.get()).queryById(id); + if (map == null) return null; + int upgradeFlavor = (int) map.get(UpgradeSQLite.UPGRADE_FLAVOR); + String path = (String) map.get(UpgradeSQLite.PATH); + File file = new File(path); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + uri = RUpgradeFileProvider.getUriForFile(contextWrapper.get(), contextWrapper.get().getApplicationInfo().packageName + ".fileProvider", file); + } else { + uri = Uri.fromFile(file); + } + if (installFactory != null && installFactory instanceof SilentInstallFactory) { + uri = Uri.parse(file.getPath()); + } + if (upgradeFlavor == UpgradeSQLite.UPGRADE_FLAVOR_INCREMENT) { + String newPath = new IncrementUpgradeManager(contextWrapper.get()).mixinAndGetNewApk(path); + RUpgradeLogger.get().d(TAG, "合成成功,新的安装包路径:" + newPath); + if (newPath == null) return null; + file = new File(newPath); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + uri = RUpgradeFileProvider.getUriForFile(contextWrapper.get(), contextWrapper.get().getApplicationInfo().packageName + ".fileProvider", file); + } else { + uri = Uri.fromFile(file); + } + if (installFactory != null && installFactory instanceof SilentInstallFactory) { + uri = Uri.parse(file.getPath()); + } + } else if (upgradeFlavor == UpgradeSQLite.UPGRADE_FLAVOR_HOT_UPDATE) { + boolean isSuccess = new HotUpgradeManager(contextWrapper.get()).hotUpgrade(uri); + if (isSuccess) { + return Uri.parse(""); + } else { + return null; + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + + return uri; + } + + @Override + protected void onPostExecute(Uri uri) { + super.onPostExecute(uri); + try { + if (uri != null) { + if (uri.toString().isEmpty()) { + //热更新实现 + postResult(true); + } else { + postResult(installApk(uri)); + } + } else { + postResult(false); + } + } catch (Exception e) { + e.printStackTrace(); + postResult(false); + + } + } + + private void postResult(boolean isSuccess) { + if (result != null) { + result.success(isSuccess); + } + } + + //安装apk + private boolean installApk(Uri uri) { + if (installFactory == null) return true; + return installFactory.install(uri); + } +} diff --git a/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/tasks/GenerateAndInstallByPathAsyncTask.java b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/tasks/GenerateAndInstallByPathAsyncTask.java new file mode 100644 index 000000000..9edfcf89a --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/tasks/GenerateAndInstallByPathAsyncTask.java @@ -0,0 +1,110 @@ +package com.example.r_upgrade.common.tasks; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Build; + +import com.example.r_upgrade.RUpgradeFileProvider; +import com.example.r_upgrade.common.install.BaseInstallFactory; +import com.example.r_upgrade.common.install.SilentInstallFactory; +import com.example.r_upgrade.common.manager.HotUpgradeManager; +import com.example.r_upgrade.common.manager.IncrementUpgradeManager; +import com.example.r_upgrade.common.RUpgradeLogger; +import com.example.r_upgrade.common.UpgradeSQLite; + +import java.io.File; +import java.lang.ref.WeakReference; + +import io.flutter.plugin.common.MethodChannel; + +public class GenerateAndInstallByPathAsyncTask extends AsyncTask { + private static final String TAG = "r_upgrade.AsyncTask"; + final WeakReference contextWrapper; + MethodChannel.Result result; + final String path; + final int upgradeFlavor; + private BaseInstallFactory installFactory; + + public GenerateAndInstallByPathAsyncTask(Context context, String path, int upgradeFlavor, MethodChannel.Result result, BaseInstallFactory installFactory) { + this.contextWrapper = new WeakReference(context); + this.path = path; + this.upgradeFlavor = upgradeFlavor; + this.result = result; + this.installFactory = installFactory; + } + + @Override + protected Uri doInBackground(String... integers) { + Uri uri = null; + try { + File file = new File(path); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + uri = RUpgradeFileProvider.getUriForFile(contextWrapper.get(), contextWrapper.get().getApplicationInfo().packageName + ".fileProvider", file); + } else { + uri = Uri.fromFile(file); + } + if(installFactory != null && installFactory instanceof SilentInstallFactory){ + uri= Uri.parse(file.getPath()); + } + if (upgradeFlavor == UpgradeSQLite.UPGRADE_FLAVOR_INCREMENT) { + String newPath = new IncrementUpgradeManager(contextWrapper.get()).mixinAndGetNewApk(path); + RUpgradeLogger.get().d(TAG, "合成成功,新的安装包路径:" + newPath); + if (newPath == null) return null; + file = new File(newPath); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + uri = RUpgradeFileProvider.getUriForFile(contextWrapper.get(), contextWrapper.get().getApplicationInfo().packageName + ".fileProvider", file); + } else { + uri = Uri.fromFile(file); + } + if(installFactory != null && installFactory instanceof SilentInstallFactory){ + uri= Uri.parse(file.getPath()); + } + } else if (upgradeFlavor == UpgradeSQLite.UPGRADE_FLAVOR_HOT_UPDATE) { + boolean isSuccess = new HotUpgradeManager(contextWrapper.get()).hotUpgrade(uri); + if (isSuccess) { + return Uri.parse(""); + } else { + return null; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return uri; + } + + @Override + protected void onPostExecute(Uri uri) { + super.onPostExecute(uri); + try { + if (uri != null) { + if (uri.toString().isEmpty()) { + //热更新实现 + postResult(true); + } else { + postResult(installApk(uri)); + } + } else { + postResult(false); + } + } catch (Exception e) { + e.printStackTrace(); + postResult(false); + + } + } + + private void postResult(boolean isSuccess) { + if (result != null) { + result.success(isSuccess); + } + } + + //安装apk + private boolean installApk(Uri uri) { + if (installFactory == null) return true; + return installFactory.install(uri); + } +} diff --git a/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/tasks/VersionCallBack.java b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/tasks/VersionCallBack.java new file mode 100644 index 000000000..3b959e262 --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/common/tasks/VersionCallBack.java @@ -0,0 +1,5 @@ +package com.example.r_upgrade.common.tasks; + +public interface VersionCallBack { + void versionName(String version); +} diff --git a/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/method/IRUpgradeMethodHandler.java b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/method/IRUpgradeMethodHandler.java new file mode 100644 index 000000000..07075d238 --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/method/IRUpgradeMethodHandler.java @@ -0,0 +1,13 @@ +package com.example.r_upgrade.method; + + +import com.example.r_upgrade.common.manager.UpgradeManager; + +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; + +public interface IRUpgradeMethodHandler { + + void handler(UpgradeManager upgradeManager, MethodCall call, MethodChannel.Result result); + +} diff --git a/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/method/RUpgradeMethodCallHandler.java b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/method/RUpgradeMethodCallHandler.java new file mode 100644 index 000000000..3d4ccd2ec --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/method/RUpgradeMethodCallHandler.java @@ -0,0 +1,22 @@ +package com.example.r_upgrade.method; + +import androidx.annotation.NonNull; + +import com.example.r_upgrade.common.manager.UpgradeManager; + +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; + +public class RUpgradeMethodCallHandler implements MethodChannel.MethodCallHandler { + private final UpgradeManager upgradeManager; + + public RUpgradeMethodCallHandler(UpgradeManager upgradeManager) { + this.upgradeManager = upgradeManager; + } + + @Override + public void onMethodCall(MethodCall call, @NonNull MethodChannel.Result result) { + RUpgradeMethodEnum methodEnum = RUpgradeMethodEnum.valueOf(call.method); + methodEnum.handler(upgradeManager,call, result); + } +} diff --git a/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/method/RUpgradeMethodEnum.java b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/method/RUpgradeMethodEnum.java new file mode 100644 index 000000000..15a0a5ed2 --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/android/src/main/java/com/example/r_upgrade/method/RUpgradeMethodEnum.java @@ -0,0 +1,103 @@ +package com.example.r_upgrade.method; + + +import com.example.r_upgrade.common.RUpgradeLogger; +import com.example.r_upgrade.common.manager.UpgradeManager; + +import java.util.Map; + +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; + + +public enum RUpgradeMethodEnum implements IRUpgradeMethodHandler { + setDebug { + @Override + public void handler(UpgradeManager upgradeManager, MethodCall call, MethodChannel.Result result) { + RUpgradeLogger.get().setDebug((Boolean) call.argument("isDebug") == Boolean.TRUE); + result.success(null); + } + }, + upgrade { + @Override + public void handler(UpgradeManager upgradeManager, MethodCall call, MethodChannel.Result result) { + upgradeManager.upgrade((String) call.argument("url"), + (Map) call.argument("header"), + (String) call.argument("fileName"), + (Integer) call.argument("notificationVisibility"), + (Integer) call.argument("notificationStyle"), + (Integer) call.argument("installType"), + (Boolean) call.argument("useDownloadManager"), (Integer) call.argument("upgradeFlavor"), result); + } + }, + upgradeFromUrl { + @Override + public void handler(UpgradeManager upgradeManager, MethodCall call, MethodChannel.Result result) { + result.success(upgradeManager.upgradeFromUrl((String) call.argument("url"))); + } + }, + + cancel { + @Override + public void handler(UpgradeManager upgradeManager, MethodCall call, MethodChannel.Result result) { + result.success(upgradeManager.cancel((Integer) call.argument("id"))); + + } + }, + install { + @Override + public void handler(UpgradeManager upgradeManager, MethodCall call, MethodChannel.Result result) { + upgradeManager.installApkById((Integer) call.argument("id"),(Integer) call.argument("installType"), result); + } + }, + installByPath { + @Override + public void handler(UpgradeManager upgradeManager, MethodCall call, MethodChannel.Result result) { + upgradeManager.installApkByPath((String) call.argument("path"), (Integer) call.argument("flavor"), (Integer) call.argument("installType"), result); + } + }, + pause { + @Override + public void handler(UpgradeManager upgradeManager, MethodCall call, MethodChannel.Result result) { + result.success(upgradeManager.pause((Integer) call.argument("id"))); + } + }, + upgradeWithId { + @Override + public void handler(UpgradeManager upgradeManager, MethodCall call, MethodChannel.Result result) { + upgradeManager.upgradeWithId((Integer) call.argument("id"), (Integer) call.argument("notificationVisibility"), + (Integer) call.argument("installType"), result); + } + }, + getDownloadStatus { + @Override + public void handler(UpgradeManager upgradeManager, MethodCall call, MethodChannel.Result result) { + result.success(upgradeManager.getDownloadStatus((Integer) call.argument("id"))); + } + }, + getLastUpgradedId { + @Override + public void handler(UpgradeManager upgradeManager, MethodCall call, MethodChannel.Result result) { + result.success(upgradeManager.getLastUpgradedId()); + } + }, + upgradeFromAndroidStore { + @Override + public void handler(UpgradeManager upgradeManager, MethodCall call, MethodChannel.Result result) { + result.success(upgradeManager.upgradeFromAndroidStore((String) call.argument("store"))); + } + }, + androidStores { + @Override + public void handler(UpgradeManager upgradeManager, MethodCall call, MethodChannel.Result result) { + result.success(upgradeManager.getAndroidStores()); + } + }, + getVersionFromAndroidStore { + @Override + public void handler(UpgradeManager upgradeManager, MethodCall call, MethodChannel.Result result) { + upgradeManager.getVersionFromAndroidStore((String) call.argument("store"), result); + } + } + +} diff --git a/modules/ability/r_upgrade-0.4.2/android/src/main/res/values/r_upgrade_value.xml b/modules/ability/r_upgrade-0.4.2/android/src/main/res/values/r_upgrade_value.xml new file mode 100644 index 000000000..2561b25fc --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/android/src/main/res/values/r_upgrade_value.xml @@ -0,0 +1,8 @@ + + + %.2f kb/s + %.0fs left + Download finished + Download paused + Download failed + \ No newline at end of file diff --git a/modules/ability/r_upgrade-0.4.2/android/src/main/res/xml/provider_paths.xml b/modules/ability/r_upgrade-0.4.2/android/src/main/res/xml/provider_paths.xml new file mode 100644 index 000000000..01ddeb6f9 --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/android/src/main/res/xml/provider_paths.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/modules/ability/r_upgrade-0.4.2/ios/Classes/RUpgradePlugin.h b/modules/ability/r_upgrade-0.4.2/ios/Classes/RUpgradePlugin.h new file mode 100644 index 000000000..9fb532823 --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/ios/Classes/RUpgradePlugin.h @@ -0,0 +1,4 @@ +#import + +@interface RUpgradePlugin : NSObject +@end diff --git a/modules/ability/r_upgrade-0.4.2/ios/Classes/RUpgradePlugin.m b/modules/ability/r_upgrade-0.4.2/ios/Classes/RUpgradePlugin.m new file mode 100644 index 000000000..72a970d27 --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/ios/Classes/RUpgradePlugin.m @@ -0,0 +1,8 @@ +#import "RUpgradePlugin.h" +#import + +@implementation RUpgradePlugin ++ (void)registerWithRegistrar:(NSObject*)registrar { + [SwiftRUpgradePlugin registerWithRegistrar:registrar]; +} +@end diff --git a/modules/ability/r_upgrade-0.4.2/ios/Classes/SwiftRUpgradePlugin.swift b/modules/ability/r_upgrade-0.4.2/ios/Classes/SwiftRUpgradePlugin.swift new file mode 100644 index 000000000..1bfc3df1a --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/ios/Classes/SwiftRUpgradePlugin.swift @@ -0,0 +1,132 @@ +import Flutter +import UIKit + +public class SwiftRUpgradePlugin: NSObject, FlutterPlugin { + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel(name: "com.rhyme/r_upgrade_method", binaryMessenger: registrar.messenger()) + let instance = SwiftRUpgradePlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + switch call.method { + case "setDebug": + result(nil) + break + case "upgradeFromUrl": + print(call.arguments ?? "null") + guard let url = (call.arguments as? Dictionary)?["url"] as? String else { + result(FlutterError(code: "参数url不能为空", message: nil, details: nil)) + return + } + result(openUrl(url: url)) + break + case "upgradeFromAppStore": + print(call.arguments ?? "null") + guard let appId = (call.arguments as? Dictionary)?["appId"] as? String else { + result(FlutterError(code: "参数appId不能为空", message: nil, details: nil)) + return + } + guard let isChina = (call.arguments as? Dictionary)?["isChina"] as? Bool else { + result(FlutterError(code: "参数isChina不能为空", message: nil, details: nil)) + return + } + upgradeFromAppStore(appId: appId,isChina: isChina,result: result) + break; + case "getVersionFromAppStore": + print(call.arguments ?? "null") + guard let appId = (call.arguments as? Dictionary)?["appId"] as? String else{ + result(FlutterError(code: "参数appId不能为空", message: nil, details: nil)) + return + } + guard let isChina = (call.arguments as? Dictionary)?["isChina"] as? Bool else { + result(FlutterError(code: "参数isChina不能为空", message: nil, details: nil)) + return + } + getVersionFromAppStore(appId: appId,isChina:isChina,result: result) + break; + default: + result(FlutterMethodNotImplemented) + } + } + func openUrl(url:String) ->Bool{ + if let url = URL(string: url) { + //根据iOS系统版本,分别处理 + if #available(iOS 10, *) { + UIApplication.shared.open(url, options: [:],completionHandler: {(success) in }) + } else { + UIApplication.shared.openURL(url) + } + return true; + } + return false; + } + + //跳转到应用的AppStore页页面 + func upgradeFromAppStore(appId: String, isChina: Bool,result: @escaping FlutterResult) { + getInfoFromAppStore(appId: appId,isChina: isChina) { dict in + if dict == nil{ + result(false) + }else{ + let res = dict!["results"] as! NSArray + let xx = res[0] as! NSDictionary + let urlString = xx["trackViewUrl"] as! String + result(self.openUrl(url: urlString)) + } + + } error: { error in + result(false) + } + } + + //获取应用信息 + func getInfoFromAppStore(appId:String,isChina:Bool,complete:@escaping(_ result:NSDictionary?)->Void,error:@escaping(_ error:Error?)->Void) { + let checkLink = isChina == true ? "https://itunes.apple.com/cn/lookup?id=" : "https://itunes.apple.com/lookup?id=" + let appUrl = checkLink + appId + let url = NSURL(string: appUrl)! as URL + let request = NSMutableURLRequest(url: url, cachePolicy: NSURLRequest.CachePolicy.reloadIgnoringLocalCacheData, timeoutInterval: 30) + request.httpMethod = "GET" + + let session = URLSession.shared + + let task = session.dataTask(with: request as URLRequest){(data,response,err) in + if err == nil{ + if data == nil{ + NSLog("获取appId:%@ 对应的appStore信息失败",appId) + complete(nil) + } + let httpRequest = response as! HTTPURLResponse + if httpRequest.statusCode == 200 { + let json = try! JSONSerialization.jsonObject(with: data! as Data, options: JSONSerialization.ReadingOptions.mutableLeaves) as! NSDictionary + if json["resultCount"] as! Int > 0{ + complete(json) + }else{ + NSLog("获取appId:%@ 对应的appStore信息失败,未上架或ID不存在", appId) + complete(nil) + } + }else{ + NSLog("获取appId:%@ 对应的appStore信息失败,未上架或ID不存在", appId) + complete(nil) + } + }else{ + NSLog("获取appId:%@ 对应的appStore信息失败",appId) + error(err) + } + } + task.resume() + } + + func getVersionFromAppStore(appId:String,isChina:Bool,result: @escaping FlutterResult){ + getInfoFromAppStore(appId: appId,isChina: isChina) {dict in + if dict != nil { + let res = dict!["results"] as! NSArray + let xx = res[0] as! NSDictionary + result(xx["version"]! as! String) + }else{ + result(nil) + } + } error: { err in + result(nil) + } + } +} diff --git a/modules/ability/r_upgrade-0.4.2/ios/r_upgrade.podspec b/modules/ability/r_upgrade-0.4.2/ios/r_upgrade.podspec new file mode 100644 index 000000000..d0df6ee3f --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/ios/r_upgrade.podspec @@ -0,0 +1,22 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html +# +Pod::Spec.new do |s| + s.name = 'r_upgrade' + s.version = '0.0.1' + s.summary = 'A new Flutter project.' + s.description = <<-DESC +A new Flutter project. + DESC + s.homepage = 'http://example.com' + s.license = { :file => '../LICENSE' } + s.author = { 'Your Company' => 'email@example.com' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.public_header_files = 'Classes/**/*.h' + s.dependency 'Flutter' + + s.ios.deployment_target = '8.0' + s.swift_version='5.0' +end + diff --git a/modules/ability/r_upgrade-0.4.2/lib/r_upgrade.dart b/modules/ability/r_upgrade-0.4.2/lib/r_upgrade.dart new file mode 100644 index 000000000..8b109288f --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/lib/r_upgrade.dart @@ -0,0 +1,484 @@ +// Copyright 2019 The rhyme_lph Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:io'; +import 'package:flutter/services.dart'; + +class RUpgrade { + static MethodChannel? __methodChannel; + + /// single + static MethodChannel? get _methodChannel { + if (__methodChannel == null) { + __methodChannel = MethodChannel('com.rhyme/r_upgrade_method'); + __methodChannel!.setMethodCallHandler(_methodCallHandler); + } + return __methodChannel; + } + + /// handle download info call back. + static Future _methodCallHandler(MethodCall call) async { + if (call.method == 'update') { + _downloadInfo.add(DownloadInfo.formMap(call.arguments)); + } + return null; + } + + /// [isDebug] is true will print log. + static Future setDebug(bool isDebug) async { + return _methodChannel!.invokeMethod('setDebug', { + 'isDebug': isDebug, + }); + } + + /// Android and IOS + /// + /// if you want to upgrade in your website + /// + /// [url] your website url + /// + static Future upgradeFromUrl(String url) async { + return await _methodChannel!.invokeMethod("upgradeFromUrl", { + 'url': url, + }); + } + + /// Android + /// + /// if you want to upgrade form your store. + /// + /// [stores] if null,show all store list + /// + static Future upgradeFromAndroidStore(AndroidStore store) async { + assert(Platform.isAndroid, 'This method only support android application'); + return await _methodChannel!.invokeMethod("upgradeFromAndroidStore", { + 'store': store._packageName, + }); + } + + /// Android + /// + /// get android store list. + static Future?> get androidStores async { + assert(Platform.isAndroid, 'This method only support android application'); + return (await _methodChannel!.invokeListMethod('androidStores')) + ?.map((e) => AndroidStore.internal(e)) + .toList(); + } + + /// Android + /// + /// get version from android store. + static Future getVersionFromAndroidStore(AndroidStore store) async { + assert(Platform.isAndroid, 'This method only support android application'); + return await _methodChannel!.invokeMethod('getVersionFromAndroidStore', { + 'store': store._packageName, + }); + } + + /// Android + /// + /// download broadcast + /// + static StreamController _downloadInfo = + StreamController.broadcast(); + + /// Android + /// + /// Download info stream . this will listen your upgrade progress and more info. + /// + static Stream get stream { + assert(Platform.isAndroid, 'This method only support android application'); + return _downloadInfo.stream; + } + + /// Android + /// + /// You can use this method upgrade your android application.If your application is ios. Oh,so sorry... + /// + /// * [url] download url. + /// * [header] download request header. + /// * [fileName] download filename and notification title name. + /// * [notificationVisibility] download running notification visibility mode. + /// * [notificationStyle] download notification show style about content text, only support [useDownloadManager]==false. + /// * [installType] download completed will install apk, use [RUpgradeInstallType.none] can not install. + /// * [useDownloadManager] if true will use DownloadManager,false will use my service , + /// * if true will no use [pause] , [upgradeWithId] , [getDownloadStatus] , [getLastUpgradedId] methods. + /// * [upgradeFlavor] you can use [RUpgradeFlavor.normal] , [RUpgradeFlavor.hotUpgrade] , [RUpgradeFlavor.incrementUpgrade] flavor + static Future upgrade( + String url, { + Map? header, + String? fileName, + NotificationVisibility notificationVisibility = + NotificationVisibility.VISIBILITY_VISIBLE, + NotificationStyle notificationStyle = NotificationStyle.planTime, + RUpgradeInstallType installType = RUpgradeInstallType.normal, + bool useDownloadManager = false, + RUpgradeFlavor upgradeFlavor = RUpgradeFlavor.normal, + }) { + assert(Platform.isAndroid, 'This method only support android application'); + return _methodChannel!.invokeMethod('upgrade', { + 'url': url, + "header": header, + "fileName": fileName, + "notificationVisibility": notificationVisibility.value, + "notificationStyle": notificationStyle.index, + "installType": installType.index, + "useDownloadManager": useDownloadManager, + "upgradeFlavor": upgradeFlavor.index, + }); + } + + /// Android + /// + /// Cancel by the [id] download task . + /// + static Future cancel(int id) { + assert(Platform.isAndroid, 'This method only support android application'); + return _methodChannel!.invokeMethod('cancel', { + 'id': id, + }); + } + + /// Android + /// + /// Install your apk by [id]. + /// + static Future install( + int id, { + RUpgradeInstallType installType = RUpgradeInstallType.normal, + }) async { + assert(Platform.isAndroid, 'This method only support android application'); + return await _methodChannel!.invokeMethod("install", { + 'id': id, + 'installType': installType.index, + }); + } + + /// Android + /// + /// Install your apk by [path]. + /// + static Future installByPath( + String path, { + RUpgradeFlavor flavor = RUpgradeFlavor.normal, + RUpgradeInstallType installType = RUpgradeInstallType.normal, + }) async { + assert(Platform.isAndroid, 'This method only support android application'); + return await _methodChannel!.invokeMethod("installByPath", { + 'path': path, + 'flavor': flavor.index, + 'installType': installType.index, + }); + } + + /// Android + /// + /// Pause by the [id] download task ,only use to [upgrade] params [useDownloadManager] is false. + /// + static Future pause(int id) { + assert(Platform.isAndroid, 'This method only support android application'); + return _methodChannel!.invokeMethod('pause', { + 'id': id, + }); + } + + /// Android + /// + /// Upgrade with ID ,only use to [upgrade] params [useDownloadManager] is false. + /// + /// * if download status is [STATUS_PAUSED] or [STATUS_FAILED] or [STATUS_CANCEL], will restart running. + /// * if download status is [STATUS_RUNNING] or [STATUS_PENDING], nothing happened. + /// * if download status is [STATUS_SUCCESSFUL] , will install apk. + /// + /// * if not found the id , will return [false]. + static Future upgradeWithId( + int id, { + NotificationVisibility notificationVisibility = + NotificationVisibility.VISIBILITY_VISIBLE, + RUpgradeInstallType installType = RUpgradeInstallType.normal, + }) async { + assert(Platform.isAndroid, 'This method only support android application'); + return await _methodChannel!.invokeMethod("upgradeWithId", { + "id": id, + "notificationVisibility": notificationVisibility.value, + "installType": installType.index, + }); + } + + /// Android + /// + /// Get download status by ID , only use to [upgrade] params [useDownloadManager] is false. + /// + static Future getDownloadStatus(int id) async { + assert(Platform.isAndroid, 'This method only support android application'); + int? result = await _methodChannel!.invokeMethod("getDownloadStatus", { + "id": id, + }); + return result == null ? null : DownloadStatus._internal(result); + } + + /// Android + /// + /// Get the ID of the last upgrade by version name and version code , only use to [upgrade] params [useDownloadManager] is false. + /// + static Future getLastUpgradedId() async { + assert(Platform.isAndroid, 'This method only support android application'); + return await _methodChannel!.invokeMethod('getLastUpgradedId'); + } + + /// IOS + /// + /// [appId] your appId in appStore + /// [isChina] if true ,will check this link https://itunes.apple.com/cn/lookup. + /// + static Future upgradeFromAppStore(String appId, + [bool isChina = true]) async { + assert(Platform.isIOS, 'This method only support ios application'); + return await _methodChannel!.invokeMethod("upgradeFromAppStore", { + 'appId': appId, + 'isChina': isChina, + }); + } + + /// IOS + /// + /// [id] your appId in appStore + /// [isChina] if true ,will check this link https://itunes.apple.com/cn/lookup. + /// + static Future getVersionFromAppStore(String appId, + [bool isChina = true]) async { + assert(Platform.isIOS, 'This method only support ios application'); + return await _methodChannel!.invokeMethod("getVersionFromAppStore", { + 'appId': appId, + 'isChina': isChina, + }); + } +} + +/// +/// A model class is download info +/// +/// * [maxLength] download max bytes length +/// * [currentLength] download current bytes length +/// * [status] download status . you can watch [DownloadStatus] +/// * [planTime] download plan time /s +/// * [path] download file path +/// * [percent] download percent 0-100 +/// * [id] download id +/// * [speed] download speed kb/s +/// +class DownloadInfo { + final int? maxLength; + final int? currentLength; + final String? path; + final double? planTime; + final double? percent; + final int? id; + final double? speed; + final DownloadStatus? status; + + DownloadInfo( + {this.maxLength, + this.path, + this.planTime, + this.currentLength, + this.percent, + this.id, + this.status, + this.speed}); + + factory DownloadInfo.formMap(dynamic map) => DownloadInfo( + maxLength: map['max_length'], + currentLength: map['current_length'], + path: map['path'], + planTime: map['plan_time'], + percent: map['percent'], + id: map['id'], + status: DownloadStatus._internal(map['status']), + speed: map['speed'], + ); + + @override + String toString() { + return 'DownloadInfo{total: $maxLength, address: $path, planTime: $planTime, progress: $currentLength, percent: $percent, id: $id, speed: $speed, status: $status}'; + } + + String getSpeedString() { + double _speed = speed ?? 0; + String unit = 'kb/s'; + String result = _speed.toStringAsFixed(2); + if (_speed > 1024 * 1024) { + unit = 'gb/s'; + result = (_speed / (1024 * 1024)).toStringAsFixed(2); + } else if (_speed > 1024) { + unit = 'mb/s'; + result = (_speed / 1024).toStringAsFixed(2); + } + return '$result$unit'; + } +} + +/// +/// A model class is download status +/// +/// * [STATUS_PAUSED] download paused +/// * [STATUS_PENDING] download pending +/// * [STATUS_RUNNING] download running +/// * [STATUS_SUCCESSFUL] download successful +/// * [STATUS_FAILED] download failed +/// * [STATUS_CANCEL] download cancel +/// +class DownloadStatus { + final int? _value; + + int? get value => _value; + + const DownloadStatus._internal(this._value); + + static DownloadStatus from(int value) => DownloadStatus._internal(value); + + static const STATUS_PAUSED = const DownloadStatus._internal(0); + static const STATUS_PENDING = const DownloadStatus._internal(1); + static const STATUS_RUNNING = const DownloadStatus._internal(2); + static const STATUS_SUCCESSFUL = const DownloadStatus._internal(3); + static const STATUS_FAILED = const DownloadStatus._internal(4); + static const STATUS_CANCEL = const DownloadStatus._internal(5); + + @override + bool operator ==(Object other) => + identical(this, other) || + other is DownloadStatus && + runtimeType == other.runtimeType && + _value == other._value; + + @override + int get hashCode => _value.hashCode; + + toString() => 'DownloadStatus($_value)'; +} + +/// +/// A model class is Notification Visibility +/// +/// * [VISIBILITY_VISIBLE] This download is visible but only shows in the notifications +/// * [VISIBILITY_VISIBLE_NOTIFY_COMPLETED] This download is visible and shows in the notifications while +/// * [VISIBILITY_HIDDEN] This download doesn't show in the UI or in the notifications. +/// * [VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION] This download shows in the notifications after completion ONLY. +/// +class NotificationVisibility { + final int _value; + + const NotificationVisibility._internal(this._value); + + int get value => _value; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is NotificationVisibility && + runtimeType == other.runtimeType && + _value == other._value; + + @override + int get hashCode => _value.hashCode; + + toString() => 'NotificationVisibility($_value)'; + + static NotificationVisibility from(int value) => + NotificationVisibility._internal(value); + + /// This download is visible but only shows in the notifications + /// while it's in progress. + static const VISIBILITY_VISIBLE = const NotificationVisibility._internal(0); + + /// This download is visible and shows in the notifications while + /// in progress and after completion. + static const VISIBILITY_VISIBLE_NOTIFY_COMPLETED = + const NotificationVisibility._internal(1); + + /// This download doesn't show in the UI or in the notifications. + static const VISIBILITY_HIDDEN = const NotificationVisibility._internal(2); + + /// + /// This download shows in the notifications after completion ONLY. + /// + static const VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION = + const NotificationVisibility._internal(3); +} + +/// Notification show style about content text +enum NotificationStyle { + speechAndPlanTime, + planTimeAndSpeech, + speech, + planTime, + none, +} + +/// Upgrade Flavor +enum RUpgradeFlavor { + normal, + hotUpgrade, + incrementUpgrade, +} + +/// Install Type +enum RUpgradeInstallType { + normal, + silent, + none, +} + +/// +/// Android application store. +/// +/// [packageName] store package name. +class AndroidStore { + final String _packageName; + + String get packageName => _packageName; + + const AndroidStore.internal(this._packageName); + + //google play + static const GOOGLE_PLAY = const AndroidStore.internal('com.android.vending'); + + //应用宝 + static const TENCENT = + const AndroidStore.internal('com.tencent.android.qqdownloader'); + + //360手机助手 + static const QIHOO = const AndroidStore.internal('com.qihoo.appstore'); + + //百度手机助手 + static const BAIDU = const AndroidStore.internal('com.baidu.appsearch'); + + //小米应用商店 + static const XIAOMI = const AndroidStore.internal('com.xiaomi.market'); + + //豌豆荚 + static const WANDOU = const AndroidStore.internal('com.wandoujia.phoenix2'); + + //华为应用市场 + static const HUAWEI = const AndroidStore.internal('com.huawei.appmarket'); + + //淘宝手机助手 + static const TAOBAO = const AndroidStore.internal('com.taobao.appcenter'); + + //安卓市场 + static const HIAPK = const AndroidStore.internal('com.hiapk.marketpho'); + + //安智市场 + static const GOAPK = const AndroidStore.internal('cn.goapk.market'); + + //酷安 + static const COOLAPK = const AndroidStore.internal('com.coolapk.market'); + + @override + String toString() { + return 'AndroidStore{_packageName: $_packageName}'; + } +} diff --git a/modules/ability/r_upgrade-0.4.2/pubspec.lock b/modules/ability/r_upgrade-0.4.2/pubspec.lock new file mode 100644 index 000000000..461388fed --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/pubspec.lock @@ -0,0 +1,189 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf + url: "https://pub.dev" + source: hosted + version: "1.19.0" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" + url: "https://pub.dev" + source: hosted + version: "10.0.7" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" + url: "https://pub.dev" + source: hosted + version: "3.0.8" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + matcher: + dependency: transitive + description: + name: matcher + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + url: "https://pub.dev" + source: hosted + version: "0.12.16+1" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" + source: hosted + version: "0.11.1" + meta: + dependency: transitive + description: + name: meta + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + url: "https://pub.dev" + source: hosted + version: "1.15.0" + path: + dependency: transitive + description: + name: path + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + url: "https://pub.dev" + source: hosted + version: "1.9.0" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" + url: "https://pub.dev" + source: hosted + version: "1.12.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" + url: "https://pub.dev" + source: hosted + version: "0.7.3" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b + url: "https://pub.dev" + source: hosted + version: "14.3.0" +sdks: + dart: ">=3.4.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/modules/ability/r_upgrade-0.4.2/pubspec.yaml b/modules/ability/r_upgrade-0.4.2/pubspec.yaml new file mode 100644 index 000000000..63957e8e3 --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/pubspec.yaml @@ -0,0 +1,64 @@ +name: r_upgrade +description: A plugin for upgrade and install application ,Support Android and IOS. +version: 0.4.2 +homepage: https://github.com/rhymelph/r_upgrade + +environment: + sdk: '>=2.12.0 <3.0.0' + flutter: ">=1.12.0" + +dependencies: + flutter: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter. +flutter: + # This section identifies this Flutter project as a plugin project. + # The androidPackage and pluginClass identifiers should not ordinarily + # be modified. They are used by the tooling to maintain consistency when + # adding or updating assets for this project. + plugin: + platforms: + android: + package: com.example.r_upgrade + pluginClass: RUpgradePlugin + ios: + pluginClass: RUpgradePlugin + + # To add assets to your plugin package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware. + + # To add custom fonts to your plugin package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/modules/ability/r_upgrade-0.4.2/test/r_upgrade_test.dart b/modules/ability/r_upgrade-0.4.2/test/r_upgrade_test.dart new file mode 100644 index 000000000..0f2805b6a --- /dev/null +++ b/modules/ability/r_upgrade-0.4.2/test/r_upgrade_test.dart @@ -0,0 +1,20 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + const MethodChannel channel = MethodChannel('r_upgrade'); + + setUp(() { + channel.setMockMethodCallHandler((MethodCall methodCall) async { + return '42'; + }); + }); + + tearDown(() { + channel.setMockMethodCallHandler(null); + }); + + test('getPlatformVersion', () async { +// expect(await RUpgrade.upgrade(''), '42'); + }); +} diff --git a/packages/algorithm/.gitignore b/modules/basic_system/app/.gitignore similarity index 100% rename from packages/algorithm/.gitignore rename to modules/basic_system/app/.gitignore diff --git a/packages/app/.metadata b/modules/basic_system/app/.metadata similarity index 100% rename from packages/app/.metadata rename to modules/basic_system/app/.metadata diff --git a/packages/algorithm/CHANGELOG.md b/modules/basic_system/app/CHANGELOG.md similarity index 100% rename from packages/algorithm/CHANGELOG.md rename to modules/basic_system/app/CHANGELOG.md diff --git a/packages/algorithm/LICENSE b/modules/basic_system/app/LICENSE similarity index 100% rename from packages/algorithm/LICENSE rename to modules/basic_system/app/LICENSE diff --git a/packages/algorithm/README.md b/modules/basic_system/app/README.md similarity index 100% rename from packages/algorithm/README.md rename to modules/basic_system/app/README.md diff --git a/packages/algorithm/analysis_options.yaml b/modules/basic_system/app/analysis_options.yaml similarity index 100% rename from packages/algorithm/analysis_options.yaml rename to modules/basic_system/app/analysis_options.yaml diff --git a/modules/basic_system/app/lib/app.dart b/modules/basic_system/app/lib/app.dart new file mode 100644 index 000000000..3849a58ee --- /dev/null +++ b/modules/basic_system/app/lib/app.dart @@ -0,0 +1,31 @@ +library app; + +export 'app_config/bloc/bloc.dart'; +export 'app_config/repository/repository.dart'; +export 'app/cons/cons.dart'; +export 'app/cons/global_value.dart'; +export 'app/cons/path_unit.dart'; +export 'app/cons/sp.dart'; +export 'app/cons/str_unit.dart'; + +export 'app/res/toly_icon.dart'; +export 'app/theme/size_unit.dart'; +export 'app/theme/app_theme.dart'; +export 'app/style/unit_text_style.dart'; +export 'app/style/unit_color.dart'; +export 'app/style/gap.dart'; +export 'app/style/shape/coupon_shape_border.dart'; +export 'app/style/shape/techno_shape.dart'; +export 'app/style/behavior/no_scroll_behavior.dart'; + +export 'package:fx_platform_adapter/fx_platform_adapter.dart'; +export 'package:fx_go_router_ext/fx_go_router_ext.dart'; +export 'app_config/app_config.dart'; + +export 'app/action/action.dart'; +export 'app/router/app_route.dart'; +export 'view/view.dart'; +export 'http/http.dart'; +export 'event/api.dart'; +export 'news/news_bloc.dart'; +export 'news/cacheable.dart'; diff --git a/modules/basic_system/app/lib/app/action/action.dart b/modules/basic_system/app/lib/app/action/action.dart new file mode 100644 index 000000000..a49af1206 --- /dev/null +++ b/modules/basic_system/app/lib/app/action/action.dart @@ -0,0 +1 @@ +export 'url.dart'; \ No newline at end of file diff --git a/modules/basic_system/app/lib/app/action/url.dart b/modules/basic_system/app/lib/app/action/url.dart new file mode 100644 index 000000000..fd8b82970 --- /dev/null +++ b/modules/basic_system/app/lib/app/action/url.dart @@ -0,0 +1,17 @@ +// Copyright 2014 The 张风捷特烈 . All rights reserved. + +// Author: 张风捷特烈 +// CreateTime: 2024-07-07 +// Contact Me: 1981462002@qq.com + +import 'package:flutter/foundation.dart'; +import 'package:url_launcher/url_launcher.dart'; + +void jumpURL(String url) async { + Uri uri = Uri.parse(url); + if (await canLaunchUrl(Uri.parse(url))) { + await launchUrl(uri,mode: LaunchMode.externalApplication); + } else { + debugPrint('Could not launch $url'); + } +} \ No newline at end of file diff --git a/modules/basic_system/app/lib/app/cons/cons.dart b/modules/basic_system/app/lib/app/cons/cons.dart new file mode 100644 index 000000000..28212cbc0 --- /dev/null +++ b/modules/basic_system/app/lib/app/cons/cons.dart @@ -0,0 +1,92 @@ + +import 'package:flutter/material.dart'; +import 'package:l10n/l10n.dart'; +import 'package:toly_ui/toly_ui.dart'; +import 'package:widget_module/widget_module.dart'; + +import '../res/toly_icon.dart'; + + +class Cons { + + static const List tabColors = [ + Color(0xff44D1FD), + Color(0xffFD4F43), + Color(0xffB375FF), + Color(0xFF4CAF50), + Color(0xFFFF9800), + Color(0xFF00F1F1), + Color(0xFFDBD83F), + ]; + + + + static const List kFontFamilySupport = [ + 'local', + 'ComicNeue', + 'IndieFlower', + 'BalooBhai2', + 'Inconsolata', + 'Neucha' + ]; + + static const Map kWidgetFamilyLabelMap = { + WidgetFamily.stateless: "Stateless", + WidgetFamily.stateful: "Stateful", + WidgetFamily.singleChildRender: "SingleChild", + WidgetFamily.multiChildRender: "MultiChild", + WidgetFamily.sliver: "Sliver", + WidgetFamily.proxy: "Proxy", + WidgetFamily.other: "Other", + }; + + static Map codeThemeSupport = { + HighlighterStyle.fromColors(HighlighterStyle.gitHub):"GitHub - Power By 张风捷特烈", + HighlighterStyle.fromColors(HighlighterStyle.darkColor):"捷特黑 - Power By 张风捷特烈", + HighlighterStyle.fromColors(HighlighterStyle.lightColor):"捷特白 - Power By 张风捷特烈", + HighlighterStyle.fromColors(HighlighterStyle.zenburn):"zenburn - Power By 张风捷特烈", + HighlighterStyle.fromColors(HighlighterStyle.mf):"mf - Power By MF", + HighlighterStyle.fromColors(HighlighterStyle.solarized):"cst - Power By cst", + }; + +} + +enum ThemeColor { + red(Colors.red), + orange(Colors.orange), + yellow(Colors.yellow), + green(Colors.green), + blue(Colors.blue), + indigo(Colors.indigo), + purple(Colors.purple), + dark(MaterialColor(0xff2D2D2D, { + 50: Color(0xFF8A8A8A), + 100: Color(0xFF747474), + 200: Color(0xFF616161), + 300: Color(0xFF484848), + 400: Color(0xFF3D3D3D), + 500: Color(0xff2D2D2D), + 600: Color(0xFF252525), + 700: Color(0xFF141414), + 800: Color(0xFF050505), + 900: Color(0xff000000), + })); + + final MaterialColor color; + + const ThemeColor(this.color); + + String label(BuildContext context){ + return switch(this){ + ThemeColor.red => context.l10n.destructionRed, + ThemeColor.orange => context.l10n.rageOrange, + ThemeColor.yellow => context.l10n.warningYellow, + ThemeColor.green => context.l10n.camouflageGreen, + ThemeColor.blue => context.l10n.coldBlue, + ThemeColor.indigo => context.l10n.infiniteBlue, + ThemeColor.purple => context.l10n.mysteryPurple, + ThemeColor.dark => context.l10n.destinyBlack, + }; + } + +} diff --git a/modules/basic_system/app/lib/app/cons/global_value.dart b/modules/basic_system/app/lib/app/cons/global_value.dart new file mode 100644 index 000000000..36c8608b2 --- /dev/null +++ b/modules/basic_system/app/lib/app/cons/global_value.dart @@ -0,0 +1,42 @@ +import 'dart:io'; +import 'dart:ui'; +import 'package:storage/storage.dart'; +import 'package:flutter/foundation.dart'; +import 'package:uuid/uuid.dart'; + +double px1 = 1 / window.devicePixelRatio; + +String get kAppVersion => "3.2.3"; + +bool kIsDesk = + kIsWeb || Platform.isMacOS || Platform.isWindows || Platform.isLinux; + +late AppMeta kAppMeta; + +class AppMeta { + final String appVersion; + final String appId; + final String uuid; + + String get platform => Platform.operatingSystem; + + AppMeta(this.appVersion, this.appId, this.uuid); + + Map toHeaderJson() => { + 'X-App-Version': appVersion, + 'X-App-Id': appId, + 'X-Platform': platform, + 'X-Uuid': uuid, + }; +} + +Future initAppMeta() async { + String? uuid = SpStorage().spf.getString('uuid'); + if (uuid == null) { + uuid = const Uuid().v4(); + SpStorage().spf.setString('uuid', uuid); + } + String version = kAppVersion; + String appId = '1'; + kAppMeta = AppMeta(version, appId, uuid); +} diff --git a/modules/basic_system/app/lib/app/cons/path_unit.dart b/modules/basic_system/app/lib/app/cons/path_unit.dart new file mode 100644 index 000000000..274128ace --- /dev/null +++ b/modules/basic_system/app/lib/app/cons/path_unit.dart @@ -0,0 +1,16 @@ +/// create by 张风捷特烈 on 2021/1/17 +/// contact me by email 1981462002@qq.com +/// 说明: + +class PathUnit { + static const baseUrl = 'http://82.157.176.209:8080/api/v1'; + + static const sendEmail = '/sendEmail/'; + static const register = '/register'; + + static const categoryDataSync = '/categoryData/sync'; + static const categoryData = '/categoryData'; + static const appInfo = '/appInfo/name'; + + static const login = '/login'; +} diff --git a/packages/app/lib/app/cons/sp.dart b/modules/basic_system/app/lib/app/cons/sp.dart similarity index 100% rename from packages/app/lib/app/cons/sp.dart rename to modules/basic_system/app/lib/app/cons/sp.dart diff --git a/modules/basic_system/app/lib/app/cons/str_unit.dart b/modules/basic_system/app/lib/app/cons/str_unit.dart new file mode 100644 index 000000000..abfa191a2 --- /dev/null +++ b/modules/basic_system/app/lib/app/cons/str_unit.dart @@ -0,0 +1,40 @@ +import 'package:flutter/cupertino.dart'; +import 'package:l10n/l10n.dart'; + +/// create by 张风捷特烈 on 2020/11/29 +/// contact me by email 1981462002@qq.com +/// 说明: + +class StrUnit { + // 小文字大小 + static const String version = 'V3.2.2'; + static const String appName = 'Flutter Unit'; + + static String galleryDesc(BuildContext context) => """ +[ + { + "image":"assets/images/anim_draw.webp", + "name": "${context.l10n.basicDrawing}", + "info": "${context.l10n.basicDrawingDesc}" + }, + { + "image":"assets/images/draw_bg3.webp", + "name": "${context.l10n.animationGesture}", + "info": "${context.l10n.animationGestureDesc}" + }, + { + "image":"assets/images/base_draw.webp", + "name": "${context.l10n.particleDrawing}", + "info": "${context.l10n.particleDrawingDesc}" + }, + { + "image":"assets/images/draw_bg4.webp", + "name": "${context.l10n.interestingDrawing}", + "info": "${context.l10n.interestingDrawingDesc}"}, + { + "image":"assets/images/caver.webp", + "name": "${context.l10n.artGallery}", + "info": "${context.l10n.artGalleryDesc}"} +] +"""; +} diff --git a/packages/app/lib/app/res/toly_icon.dart b/modules/basic_system/app/lib/app/res/toly_icon.dart similarity index 100% rename from packages/app/lib/app/res/toly_icon.dart rename to modules/basic_system/app/lib/app/res/toly_icon.dart diff --git a/modules/basic_system/app/lib/app/router/app_route.dart b/modules/basic_system/app/lib/app/router/app_route.dart new file mode 100644 index 000000000..626c0133f --- /dev/null +++ b/modules/basic_system/app/lib/app/router/app_route.dart @@ -0,0 +1,42 @@ +enum AppRoute { + home('/', url: '/'), + splash('splash', url: '/splash'), + startError('start_error', url: '/start_error'), + globalError('404', url: '/404'), + + /// widget module + widget('widget', url: '/widget'), + widgetDetail('detail/:name', url: '/widget/detail/'), + collection('collection', url: '/collection'), + collectionDetail('widgets/:id', url: '/collection/widgets/'), + + note('note', url: '/note'), + moreNews('more_news', url: '/more_news'), + painter('painter', url: '/painter'), + knowledge('knowledge', url: '/knowledge'), + tools('tools', url: '/tools'), + + /// user/app + aboutApp('about_app', url: '/about_app'), + account('account', url: '/account'), + dataManage('data_manage', url: '/data_manage'), + aboutMe('about_me', url: '/about_me'), + supportMe('support_me', url: '/support_me'), + + /// settings + settings('settings', url: '/settings'), + darkModel('dark_mode', url: '/setting/dark_mode'), + codeStyle('code_style', url: '/setting/code_style'), + themeColor('theme_color', url: '/setting/theme_color'), + fontSetting('font_setting', url: '/setting/font_setting'), + version('version', url: '/settings/version'), + ; + + final String path; + final String url; + + const AppRoute( + this.path, { + required this.url, + }); +} diff --git a/modules/basic_system/app/lib/app/style/behavior/no_scroll_behavior.dart b/modules/basic_system/app/lib/app/style/behavior/no_scroll_behavior.dart new file mode 100644 index 000000000..82e89fe01 --- /dev/null +++ b/modules/basic_system/app/lib/app/style/behavior/no_scroll_behavior.dart @@ -0,0 +1,11 @@ +import 'package:flutter/material.dart'; + +/// create by 张风捷特烈 on 2020/6/16 +/// contact me by email 1981462002@qq.com +/// 说明: + +class NoScrollBehavior extends ScrollBehavior { + @override + Widget buildOverscrollIndicator( + BuildContext context, Widget child, ScrollableDetails details) => child; +} \ No newline at end of file diff --git a/packages/app/lib/app/style/gap.dart b/modules/basic_system/app/lib/app/style/gap.dart similarity index 100% rename from packages/app/lib/app/style/gap.dart rename to modules/basic_system/app/lib/app/style/gap.dart diff --git a/packages/app/lib/app/style/shape/coupon_shape_border.dart b/modules/basic_system/app/lib/app/style/shape/coupon_shape_border.dart similarity index 100% rename from packages/app/lib/app/style/shape/coupon_shape_border.dart rename to modules/basic_system/app/lib/app/style/shape/coupon_shape_border.dart diff --git a/packages/app/lib/app/style/shape/techno_shape.dart b/modules/basic_system/app/lib/app/style/shape/techno_shape.dart similarity index 100% rename from packages/app/lib/app/style/shape/techno_shape.dart rename to modules/basic_system/app/lib/app/style/shape/techno_shape.dart diff --git a/packages/app/lib/app/style/unit_color.dart b/modules/basic_system/app/lib/app/style/unit_color.dart similarity index 100% rename from packages/app/lib/app/style/unit_color.dart rename to modules/basic_system/app/lib/app/style/unit_color.dart diff --git a/packages/app/lib/app/style/unit_text_style.dart b/modules/basic_system/app/lib/app/style/unit_text_style.dart similarity index 98% rename from packages/app/lib/app/style/unit_text_style.dart rename to modules/basic_system/app/lib/app/style/unit_text_style.dart index 545663d36..1fdf7c193 100644 --- a/packages/app/lib/app/style/unit_text_style.dart +++ b/modules/basic_system/app/lib/app/style/unit_text_style.dart @@ -15,7 +15,7 @@ class UnitTextStyle { shadows: [ Shadow( color: Colors.black, - blurRadius: 1, + blurRadius: 0.5, offset: Offset(0.1, 0.1)) ], fontSize: 12); diff --git a/modules/basic_system/app/lib/app/theme/app_theme.dart b/modules/basic_system/app/lib/app/theme/app_theme.dart new file mode 100644 index 000000000..125bd7147 --- /dev/null +++ b/modules/basic_system/app/lib/app/theme/app_theme.dart @@ -0,0 +1,137 @@ +import 'package:app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +ThemeData darkTheme(AppConfig state) { + const Color scaffoldBackgroundColor = Color(0xff010201); + + SystemUiOverlayStyle overlayStyle = const SystemUiOverlayStyle( + statusBarColor: Colors.transparent, + statusBarBrightness: Brightness.dark, + statusBarIconBrightness: Brightness.light, + systemNavigationBarColor: Color(0xff181818)); + + return ThemeData( + scaffoldBackgroundColor: scaffoldBackgroundColor, + pageTransitionsTheme: const PageTransitionsTheme(builders: { + TargetPlatform.android: SlidePageTransitionsBuilder(), + TargetPlatform.iOS: SlidePageTransitionsBuilder(), + TargetPlatform.macOS: FadePageTransitionsBuilder(), + TargetPlatform.windows: FadePageTransitionsBuilder(), + TargetPlatform.linux: FadePageTransitionsBuilder(), + }), + tabBarTheme: const TabBarThemeData( + dividerColor: Colors.transparent, + ), + fontFamily: state.fontFamily, + useMaterial3: true, + brightness: Brightness.dark, + primaryColor: const Color(0xff4699FB), + listTileTheme: const ListTileThemeData( + tileColor: Color(0xff181818), + textColor: Color(0xffD6D6D6), + ), + + ///设置选中的文本颜色 + textSelectionTheme: TextSelectionThemeData( + selectionColor: Colors.blue.withOpacity(0.3), + ), + appBarTheme: AppBarTheme( + systemOverlayStyle: overlayStyle, + elevation: 0, + centerTitle: true, + backgroundColor: const Color(0xff181818), + iconTheme: const IconThemeData(color: Color(0xffCCCCCC)), + titleTextStyle: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Color(0xffCCCCCC))), + floatingActionButtonTheme: const FloatingActionButtonThemeData( + foregroundColor: Colors.white, backgroundColor: Color(0xff4699FB)), + dividerTheme: DividerThemeData( + color: const Color(0xff2F2F2F), + space: px1, + thickness: divHeight, + ), + bottomNavigationBarTheme: const BottomNavigationBarThemeData( + backgroundColor: Color(0xff181818), + selectedItemColor: Color(0xff4699FB)), + ); +} + +double get divHeight { + if (kAppEnv.isAndroid) { + return 0.2; + } + return px1; +} + +ThemeData lightTheme(AppConfig state) { + SystemUiOverlayStyle overlayStyle = const SystemUiOverlayStyle( + statusBarColor: Colors.transparent, + statusBarBrightness: Brightness.light, + statusBarIconBrightness: Brightness.dark, + systemNavigationBarColor: Colors.transparent); + + String fontFamily = state.fontFamily; + if (kAppEnv.isWindows) { + fontFamily = '宋体'; + } + + return ThemeData( + fontFamily: fontFamily, + primaryColor: state.themeColor.color, + scaffoldBackgroundColor: const Color(0xffF3F4F6), + useMaterial3: true, + // Android 使用 Material3 + chipTheme: + const ChipThemeData(padding: EdgeInsets.symmetric(horizontal: 10)), + listTileTheme: const ListTileThemeData( + tileColor: Colors.white, textColor: Color(0xff333333)), + + ///设置选中的文本颜色 + textSelectionTheme: TextSelectionThemeData( + selectionColor: Colors.blue.withOpacity(0.3), + ), + dividerTheme: DividerThemeData( + color: const Color(0xffDEE0E2), + space: px1, + thickness: divHeight, + ), + pageTransitionsTheme: const PageTransitionsTheme(builders: { + TargetPlatform.android: SlidePageTransitionsBuilder(), + TargetPlatform.iOS: SlidePageTransitionsBuilder(), + TargetPlatform.macOS: FadePageTransitionsBuilder(), + TargetPlatform.windows: FadePageTransitionsBuilder(), + TargetPlatform.linux: FadePageTransitionsBuilder(), + }), + tabBarTheme: TabBarThemeData( + dividerColor: Colors.transparent, + // labelStyle: TextStyle(fontFamily: fontFamily), + // unselectedLabelStyle: TextStyle(fontFamily: fontFamily), + + splashFactory: NoSplash.splashFactory, + overlayColor: WidgetStateProperty.resolveWith( + (Set states) { + return states.contains(WidgetState.focused) + ? null + : Colors.transparent; + }, + ), + ), + bottomNavigationBarTheme: + const BottomNavigationBarThemeData(backgroundColor: Colors.white), + appBarTheme: AppBarTheme( + systemOverlayStyle: overlayStyle, + elevation: 0, + centerTitle: true, + backgroundColor: Colors.white, + titleTextStyle: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.black, + fontFamily: fontFamily, + ), + ), + ); +} diff --git a/packages/app/lib/app/theme/size_unit.dart b/modules/basic_system/app/lib/app/theme/size_unit.dart similarity index 100% rename from packages/app/lib/app/theme/size_unit.dart rename to modules/basic_system/app/lib/app/theme/size_unit.dart diff --git a/modules/basic_system/app/lib/app_config/app_config.dart b/modules/basic_system/app/lib/app_config/app_config.dart new file mode 100644 index 000000000..0f31eb5da --- /dev/null +++ b/modules/basic_system/app/lib/app_config/app_config.dart @@ -0,0 +1,3 @@ +export 'bloc/state.dart'; +export 'bloc/bloc.dart'; +export 'repository/repository.dart'; \ No newline at end of file diff --git a/modules/basic_system/app/lib/app_config/bloc/bloc.dart b/modules/basic_system/app/lib/app_config/bloc/bloc.dart new file mode 100644 index 000000000..0380a6c2d --- /dev/null +++ b/modules/basic_system/app/lib/app_config/bloc/bloc.dart @@ -0,0 +1,89 @@ +import 'dart:async'; + +import 'package:app/app.dart'; +// import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:l10n/l10n.dart'; +import 'package:storage/storage.dart'; + +/// create by 张风捷特烈 on 2020-03-22 +/// contact me by email 1981462002@qq.com +/// 说明: 全局信息的bloc + +class AppConfigBloc extends Cubit { + AppConfigBloc() : super(const AppConfig()); + + @override + Future close() async { + // _subscription.cancel(); + super.close(); + } + + void init(AppConfig state) { + emit(state); + } + + AppConfigCao get cao => SpStorage().appConfig; + + // 切换字体事件处理 : 固化索引 + 产出新状态 + void switchFontFamily(String family) async { + AppConfig newState = state.copyWith(fontFamily: family); + cao.write(newState.toAppConfigPo()); + emit(newState); + } + + // 切换语言事件处理 : 固化索引 + 产出新状态 + void switchLanguage(Language language) async { + AppConfig newState = state.copyWith(language: language); + cao.write(newState.toAppConfigPo()); + emit(newState); + } + + // 切换主题色事件处理 : 固化索引 + 产出新状态 + void switchThemeColor(ThemeColor color) async { + AppConfig newState = state.copyWith(themeColor: color); + cao.write(newState.toAppConfigPo()); + emit(newState); + } + + // 切换背景显示事件处理 : 固化数据 + 产出新状态 + void switchShowBg(bool show) async { + AppConfig newState = state.copyWith(showBackGround: show); + cao.write(newState.toAppConfigPo()); + emit(newState); + } + + // 切换背景显示事件处理 : 产出新状态 + void switchShowOver(bool show) async { + AppConfig newState = state.copyWith(showPerformanceOverlay: show); + cao.write(newState.toAppConfigPo()); + emit(newState); + } + + // 切换code样式事件处理 : 固化索引 + 产出新状态 + void switchCoderTheme(int codeStyleIndex) async { + AppConfig newState = state.copyWith(codeStyleIndex: codeStyleIndex); + cao.write(newState.toAppConfigPo()); + emit(newState); + } + + // 切换item样式事件处理 : 固化索引 + 产出新状态 + void changeItemStyle(int index) async { + AppConfig newState = state.copyWith(itemStyleIndex: index); + cao.write(newState.toAppConfigPo()); + emit(newState); + } + + void changeThemeMode(ThemeMode style) async { + AppConfig newState = state.copyWith(themeMode: style); + cao.write(newState.toAppConfigPo()); + emit(newState); + } + + void switchShowTool(bool show) async { + AppConfig newState = state.copyWith(showOverlayTool: show); + cao.write(newState.toAppConfigPo()); + emit(newState); + } +} diff --git a/modules/basic_system/app/lib/app_config/bloc/state.dart b/modules/basic_system/app/lib/app_config/bloc/state.dart new file mode 100644 index 000000000..7da9703ca --- /dev/null +++ b/modules/basic_system/app/lib/app_config/bloc/state.dart @@ -0,0 +1,136 @@ +import 'dart:io'; + +import 'package:app/app.dart'; +import 'package:app/app/cons/cons.dart'; +// import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; +import 'package:l10n/l10n.dart'; +import 'package:storage/storage.dart'; +import 'package:toly_ui/code/code.dart'; + +/// create by 张风捷特烈 on 2020-04-11 +/// contact me by email 1981462002@qq.com +/// 说明: 全局状态类 + +class AppConfig extends Equatable { + /// [fontFamily] 文字字体 + final String fontFamily; + + /// [themeColor] 主题色 + final ThemeColor themeColor; + + /// [showBackGround] 是否显示主页背景图 + final bool showBackGround; + + /// [codeStyleIndex] 代码样式 索引 + final int codeStyleIndex; + + /// [itemStyleIndex] 主页item样式 索引 + final int itemStyleIndex; + + /// [showPerformanceOverlay] 是否显示性能浮层 + final bool showPerformanceOverlay; + + /// [showOverlayTool] 是否显示浮动工具 + final bool showOverlayTool; + + /// [appStyle] app 深色样式; + final ThemeMode themeMode; + + // final ConnectivityResult netConnect; + + final Language language; + + const AppConfig({ + this.fontFamily = '宋体', + this.language = Language.zh_CN, + this.themeColor = ThemeColor.indigo, + this.themeMode = ThemeMode.system, + this.showBackGround = true, + this.codeStyleIndex = 0, + this.itemStyleIndex = 0, + this.showPerformanceOverlay = false, + this.showOverlayTool = true, + // this.netConnect = ConnectivityResult.none, + }); + + String get localeValue => language.locale.toString(); + + @override + List get props => [ + fontFamily, + themeColor, + showBackGround, + codeStyleIndex, + itemStyleIndex, + themeMode, + showOverlayTool, + showPerformanceOverlay, + // netConnect, + language, + ]; + + AppConfig copyWith({ + String? fontFamily, + String? dbPath, + ThemeColor? themeColor, + bool? showBackGround, + Language? language, + int? codeStyleIndex, + int? itemStyleIndex, + bool? showPerformanceOverlay, + bool? showOverlayTool, + ThemeMode? themeMode, + // ConnectivityResult? netConnect, + }) => + AppConfig( + fontFamily: fontFamily ?? this.fontFamily, + language: language ?? this.language, + themeColor: themeColor ?? this.themeColor, + showBackGround: showBackGround ?? this.showBackGround, + codeStyleIndex: codeStyleIndex ?? this.codeStyleIndex, + showOverlayTool: showOverlayTool ?? this.showOverlayTool, + itemStyleIndex: itemStyleIndex ?? this.itemStyleIndex, + themeMode: themeMode ?? this.themeMode, + showPerformanceOverlay: + showPerformanceOverlay ?? this.showPerformanceOverlay, + // netConnect: netConnect ?? this.netConnect, + ); + + // 将 AppState 状态数据转换为配置对象,以便存储 + AppConfigPo toAppConfigPo() => AppConfigPo( + showBackGround: showBackGround, + showOverlayTool: showOverlayTool, + showPerformanceOverlay: showPerformanceOverlay, + fontFamilyIndex: Cons.kFontFamilySupport.indexOf(fontFamily), + themeColorIndex: themeColor.index, + codeStyleIndex: codeStyleIndex, + themeModeIndex: themeMode.index, + itemStyleIndex: itemStyleIndex, + languageIndex: language.index, + ); + + // 根据存储的配置信息对象,形成 AppState 状态数据 + factory AppConfig.fromPo(AppConfigPo po) { + return AppConfig( + fontFamily: Cons.kFontFamilySupport[po.fontFamilyIndex], + themeColor: ThemeColor.values[po.themeColorIndex], + showBackGround: po.showBackGround, + language: Language.values[po.languageIndex], + codeStyleIndex: po.codeStyleIndex, + itemStyleIndex: po.itemStyleIndex, + showPerformanceOverlay: po.showPerformanceOverlay, + showOverlayTool: po.showOverlayTool, + themeMode: ThemeMode.values[po.themeModeIndex], + ); + } + + HighlighterStyle get codeStyle => + Cons.codeThemeSupport.keys.toList()[codeStyleIndex]; + + @override + String toString() { + return 'AppState{fontFamily: $fontFamily, themeColor: $themeColor, showBackGround: $showBackGround, codeStyleIndex: $codeStyleIndex, itemStyleIndex: $itemStyleIndex, showPerformanceOverlay: $showPerformanceOverlay}'; + } +} diff --git a/packages/app/lib/model/app_style.dart b/modules/basic_system/app/lib/app_config/repository/repository.dart similarity index 100% rename from packages/app/lib/model/app_style.dart rename to modules/basic_system/app/lib/app_config/repository/repository.dart diff --git a/modules/basic_system/app/lib/event/api.dart b/modules/basic_system/app/lib/event/api.dart new file mode 100644 index 000000000..04dce8d94 --- /dev/null +++ b/modules/basic_system/app/lib/event/api.dart @@ -0,0 +1,13 @@ +import 'package:app/app.dart'; +import 'package:flutter/foundation.dart'; +import 'package:fx_dio/fx_dio.dart'; + +void sendEvent(int id, {String? extra}) async { + if (kDebugMode) return; + Host host = FxDio()(); + ApiRet ret = await host.post( + '/event', + data: {"event": id}, + convertor: (data) => data, + ); +} diff --git a/modules/basic_system/app/lib/http/flutter_unit/api/upgrade_api.dart b/modules/basic_system/app/lib/http/flutter_unit/api/upgrade_api.dart new file mode 100644 index 000000000..3250b8d39 --- /dev/null +++ b/modules/basic_system/app/lib/http/flutter_unit/api/upgrade_api.dart @@ -0,0 +1,21 @@ +import 'dart:async'; +import 'package:app_update/app_update.dart'; +import 'package:fx_dio/fx_dio.dart'; +import 'package:app/app.dart'; + +class UnitUpgradeApi implements UpgradeApi { + @override + Future> fetch(int appId, String locale) async { + Host host = FxDio()(); + String path = ScienceApi.appVersion.path; + return host.get( + path, + queryParameters: { + 'app_id': 1, + 'os': kAppEnv.os.name, + 'locale': locale, + }, + convertor: (data) => AppInfo.fromMap(data), + ); + } +} diff --git a/modules/basic_system/app/lib/http/flutter_unit/unit_host.dart b/modules/basic_system/app/lib/http/flutter_unit/unit_host.dart new file mode 100644 index 000000000..a5dcce2ef --- /dev/null +++ b/modules/basic_system/app/lib/http/flutter_unit/unit_host.dart @@ -0,0 +1,35 @@ +import 'package:fx_dio/fx_dio.dart'; + +class UnitHost extends Host { + const UnitHost(); + + @override + Map get value => { + HostEnv.release: 'api.toly1994.com', + HostEnv.dev: '127.0.0.1', + }; + + @override + HostConfig get config => const HostConfig( + scheme: 'http', + port: 8080, + apiNest: '/api/v1', + ); + + @override + HostEnv get env => HostEnv.dev; +} + +enum UnitApi { + hello("/hello"), + repository("/repository/name/FlutterUnit"), + point("/point"), + pointComment("/pointComment/"), + appInfo("/appInfo/name/"), + ; + + final String path; + final Method? method; + + const UnitApi(this.path, [this.method = Method.get]); +} diff --git a/modules/basic_system/app/lib/http/http.dart b/modules/basic_system/app/lib/http/http.dart new file mode 100644 index 000000000..f14739529 --- /dev/null +++ b/modules/basic_system/app/lib/http/http.dart @@ -0,0 +1,5 @@ +export 'flutter_unit/api/upgrade_api.dart'; +export 'flutter_unit/unit_host.dart'; +export 'science/science_host.dart'; +export 'science/science_rep_interceptor.dart'; +export 'register.dart'; diff --git a/modules/basic_system/app/lib/http/register.dart b/modules/basic_system/app/lib/http/register.dart new file mode 100644 index 000000000..9b53dcdd8 --- /dev/null +++ b/modules/basic_system/app/lib/http/register.dart @@ -0,0 +1,18 @@ +import 'dart:async'; + +import 'package:app/app.dart'; +import 'package:fx_dio/fx_dio.dart'; +import 'http.dart'; + +void registerHttpClient() { + FxDio() + .register(const ScienceHost(), repInterceptor: ScienceRepInterceptor()); + FxDio().register(const UnitHost()); + + FxDio().auth(ScienceAuth()); +} + +class ScienceAuth extends ApiAuth { + @override + FutureOr> get buildHeaders => kAppMeta.toHeaderJson(); +} diff --git a/modules/basic_system/app/lib/http/science/science_host.dart b/modules/basic_system/app/lib/http/science/science_host.dart new file mode 100644 index 000000000..4f129ae2c --- /dev/null +++ b/modules/basic_system/app/lib/http/science/science_host.dart @@ -0,0 +1,33 @@ +import 'package:flutter/foundation.dart'; +import 'package:fx_dio/fx_dio.dart'; + +class ScienceHost extends Host { + const ScienceHost(); + + @override + Map get value => { + HostEnv.release: 'toly1994.com', + HostEnv.dev: '172.20.10.4', + // HostEnv.dev: '192.168.1.107', + }; + + @override + HostConfig get config => const HostConfig( + scheme: 'http', + port: 3000, + apiNest: '/api/v1', + ); + + @override + HostEnv get env => HostEnv.release; +} + +enum ScienceApi { + appVersion("/app_version"), + ; + + final String path; + final Method? method; + + const ScienceApi(this.path, [this.method = Method.get]); +} diff --git a/modules/basic_system/app/lib/http/science/science_rep_interceptor.dart b/modules/basic_system/app/lib/http/science/science_rep_interceptor.dart new file mode 100644 index 000000000..87848ae6a --- /dev/null +++ b/modules/basic_system/app/lib/http/science/science_rep_interceptor.dart @@ -0,0 +1,48 @@ +import 'package:dio/dio.dart'; +import 'package:fx_trace/fx_trace.dart'; + +class ScienceRepInterceptor extends InterceptorsWrapper { + @override + void onResponse(Response response, ResponseInterceptorHandler handler) { + handleResponse(response); + super.onResponse(response, handler); + } + + void handleResponse(Response response) { + if (response.statusCode == HttpCode.ok.value) { + bool success = response.data?['status'] == true; + String message = response.data['msg']; + if (success) { + response.data = response.data['data']; + response.statusMessage = message; + } else { + throw ApiTrace(message: message, error: response.data); + } + return; + } + throw ApiTrace(message: response.statusMessage ?? '', error: response.data); + } +} + +class ApiTrace with Code, Trace { + @override + final int? value; + + @override + final String message; + + @override + final Object error; + + ApiTrace({ + this.value, + required this.message, + required this.error, + }); + + @override + Code? get code => this; + + @override + StackTrace? get stack => null; +} diff --git a/modules/basic_system/app/lib/news/cacheable.dart b/modules/basic_system/app/lib/news/cacheable.dart new file mode 100644 index 000000000..5b033ed9d --- /dev/null +++ b/modules/basic_system/app/lib/news/cacheable.dart @@ -0,0 +1,84 @@ +import 'dart:async'; +import 'dart:convert'; +import 'package:storage/storage.dart'; +import 'package:fx_dao/fx_dao.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +mixin Cacheable { + FutureOr save(V po); + + FutureOr find({bool shouldRemove = true}); + + FutureOr remove(); +} + +mixin TimeoutCache on Cacheable { + String get cacheKey; + + int get maxCacheMs => 1000 * 60 * 30; + + SharedPreferences get spf => SpStorage().spf; + + ConvertorList get convertor; + + @override + FutureOr find({bool shouldRemove = true}) async { + String? data = spf.getString(cacheKey); + if (data == null || data.isEmpty) return null; + try { + dynamic map = jsonDecode(data); + dynamic jsonValue = map['value']; + if (jsonValue == null) return null; + + int time = map['time'] ?? 0; + int nowMs = DateTime.now().millisecondsSinceEpoch; + int deadMs = time + maxCacheMs; + if (nowMs > deadMs && shouldRemove) { + await remove(); + return null; + } + List v = jsonDecode(jsonValue); + return convertor(v); + } catch (e) { + print(e); + } + + return null; + } + + @override + FutureOr save(V po) async { + String data = json.encode(TimeoutPo.value(json.encode(po))); + return spf.setString(cacheKey, data); + } + + @override + FutureOr remove() { + return spf.remove(cacheKey); + } +} + +class TimeoutPo extends Po { + final int time; + final String data; + + TimeoutPo.value(this.data) : time = DateTime.now().millisecondsSinceEpoch; + + TimeoutPo({ + required this.time, + required this.data, + }); + + @override + Map toJson() => { + 'time': time, + 'value': data, + }; + + factory TimeoutPo.fromJson(dynamic map) { + return TimeoutPo( + time: map['time'], + data: map['value'], + ); + } +} diff --git a/modules/basic_system/app/lib/news/news_bloc.dart b/modules/basic_system/app/lib/news/news_bloc.dart new file mode 100644 index 000000000..7fe8ccdd1 --- /dev/null +++ b/modules/basic_system/app/lib/news/news_bloc.dart @@ -0,0 +1,48 @@ +// import 'package:flutter_bloc/flutter_bloc.dart'; +// import 'package:fx_dao/src/model/po.dart'; +// import 'package:fx_dao/src/table/dao.dart'; +// import 'package:note/note.dart'; +// import 'package:fx_dio/fx_dio.dart'; +// import 'cacheable.dart'; +// +// class NewsBloc extends Cubit +// with Cacheable>, TimeoutCache> { +// NewsBloc() : super(NewsState(headerNews: [])); +// +// ArticleRepository _repository = HttpArticleRepository(); +// +// @override +// String get cacheKey => 'flutter.unit.news'; +// +// void load() async { +// List? retCache = await find(); +// if (retCache != null) { +// print("=====load in cache========="); +// emit(NewsState(headerNews: retCache)); +// return; +// } +// refreshFromNet(); +// } +// +// Future refreshFromNet() async { +// ApiRet> ret = await _repository.getArticlesByTag(1); +// print("=====load in net========="); +// if (ret.success) { +// save(ret.data); +// emit(NewsState(headerNews: ret.data)); +// } +// } +// +// @override +// ConvertorList> get convertor => (e) { +// return e.map(ArticlePo.fromCache).toList(); +// }; +// } +// +// class NewsState { +// final List headerNews; +// +// NewsState({ +// required this.headerNews, +// }); +// } diff --git a/lib/app/views/about/about_app_page.dart b/modules/basic_system/app/lib/view/about/about_app_page.dart similarity index 96% rename from lib/app/views/about/about_app_page.dart rename to modules/basic_system/app/lib/view/about/about_app_page.dart index 772860575..59eb83255 100644 --- a/lib/app/views/about/about_app_page.dart +++ b/modules/basic_system/app/lib/view/about/about_app_page.dart @@ -2,12 +2,13 @@ /// contact me by email 1981462002@qq.com /// 说明: ... import 'package:app/app.dart'; +import 'package:components/components.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_unit/app/views/time_line/flutter_unit_time_line.dart'; -import 'package:components/toly_ui/toly_ui.dart'; - +import 'package:fx_go_router_ext/fx_go_router_ext.dart'; +import 'package:toly_ui/toly_ui.dart'; import 'package:url_launcher/url_launcher.dart'; + class AboutAppPage extends StatelessWidget { const AboutAppPage({Key? key}) : super(key: key); @@ -180,7 +181,7 @@ class AboutAppPage extends StatelessWidget { InfoPanel( title: 'Flutter Unit 2.0 ', info: - '○ 317 的 Flutter 组件收录和详情介绍。\n' + '○ 317 个 Flutter 组件收录和详情介绍。\n' '○ 绘制集录用于收录绘制相关的优秀示例。\n' '○ 要点集录用于收录 Flutter 相关的小知识。\n' '○ 时光轴,查看 FlutterUnit 重要事件。\n' @@ -203,6 +204,7 @@ class InfoPanel extends StatelessWidget { @override Widget build(BuildContext context) { return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( crossAxisAlignment: CrossAxisAlignment.center, diff --git a/lib/app/views/about/about_me_page.dart b/modules/basic_system/app/lib/view/about/about_me_page.dart similarity index 99% rename from lib/app/views/about/about_me_page.dart rename to modules/basic_system/app/lib/view/about/about_me_page.dart index ae86b7f49..1c435bac9 100644 --- a/lib/app/views/about/about_me_page.dart +++ b/modules/basic_system/app/lib/view/about/about_me_page.dart @@ -3,7 +3,7 @@ /// 说明: ... import 'package:app/app.dart'; import 'package:flutter/material.dart'; -import 'package:components/toly_ui/toly_ui.dart'; +import 'package:toly_ui/toly_ui.dart'; import 'package:url_launcher/url_launcher.dart'; diff --git a/modules/basic_system/app/lib/view/about/version/version_shower.dart b/modules/basic_system/app/lib/view/about/version/version_shower.dart new file mode 100644 index 000000000..905c247bf --- /dev/null +++ b/modules/basic_system/app/lib/view/about/version/version_shower.dart @@ -0,0 +1,23 @@ +import 'package:app/app.dart'; +import 'package:flutter/material.dart'; + +class VersionShower extends StatefulWidget { + const VersionShower({Key? key}) : super(key: key); + + @override + _VersionShowerState createState() => _VersionShowerState(); +} + +class _VersionShowerState extends State { + String version = '1.0.0'; + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Text('Version $kAppVersion'); + } +} diff --git a/lib/app/views/about/version_info.dart b/modules/basic_system/app/lib/view/about/version_info.dart similarity index 83% rename from lib/app/views/about/version_info.dart rename to modules/basic_system/app/lib/view/about/version_info.dart index 1f26fcb62..601322da1 100644 --- a/lib/app/views/about/version_info.dart +++ b/modules/basic_system/app/lib/view/about/version_info.dart @@ -2,8 +2,10 @@ import 'package:app/app.dart'; import 'package:app_update/app_update.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:l10n/l10n.dart'; -import 'package:components/toly_ui/toly_ui.dart'; +import 'package:toly_ui/toly_ui.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -44,7 +46,7 @@ class VersionInfo extends StatelessWidget { const Spacer(), Padding( padding: const EdgeInsets.only(bottom:8.0), - child: buildBottom(), + child: buildBottom(context), ) ], @@ -77,15 +79,15 @@ class VersionInfo extends StatelessWidget { children: [ const Divider(height: 1,), ListTile( - title: const Text('应用详情',style: labelStyle,), + title: Text(context.l10n.appDetails,style: labelStyle,), trailing: _nextIcon(context), - onTap: () => Navigator.of(context).pushNamed(UnitRouter.about_app), + onTap: () => context.push('/about_app'), ), const Divider(height: 1,indent: 10), const AppUpdatePanel(), const Divider(height: 1,indent: 10), ListTile( - title: const Text('检查数据库新版本',style: labelStyle), + title: Text(context.l10n.checkDatabaseNewVersion,style: labelStyle), trailing: _nextIcon(context), onTap: () async{ @@ -101,7 +103,7 @@ class VersionInfo extends StatelessWidget { Widget _nextIcon(BuildContext context) => const Icon(Icons.chevron_right, color: Colors.grey); - Widget buildBottom() { + Widget buildBottom(BuildContext context) { return Wrap( direction: Axis.vertical, crossAxisAlignment: WrapCrossAlignment.center, @@ -111,9 +113,9 @@ class VersionInfo extends StatelessWidget { onPressed: (){ _launchURL("https://github.com/toly1994328/FlutterUnit"); }, - child: const Text('《查看本项目Github仓库》',style: TextStyle(fontSize: 12,color: Color(0xff616C84),),)), + child: Text(context.l10n.viewThisProjectGithubRepository,style: TextStyle(fontSize: 12,color: Color(0xff616C84),),)), const Text('Power By 张风捷特烈',style: TextStyle(fontSize: 12,color: Colors.grey),), - const Text('Copyright © 2008-2020 Toly1994',style: TextStyle(fontSize: 12,color: Colors.grey),), + const Text('Copyright © 2018-2024 Toly1994',style: TextStyle(fontSize: 12,color: Colors.grey),), ], ); } diff --git a/modules/basic_system/app/lib/view/account/desk/desk_account_page.dart b/modules/basic_system/app/lib/view/account/desk/desk_account_page.dart new file mode 100644 index 000000000..538dbdd84 --- /dev/null +++ b/modules/basic_system/app/lib/view/account/desk/desk_account_page.dart @@ -0,0 +1,101 @@ +import 'package:components/components.dart'; +import 'package:flutter/material.dart'; +import 'package:l10n/l10n.dart'; +import 'package:toly_ui/toly_ui.dart'; + +import 'sliver_cellection_panel.dart'; +import 'sliver_list_panel.dart'; +import 'sliver_share_panel.dart'; +import 'user_header.dart'; + +class DeskAccountPage extends StatefulWidget { + const DeskAccountPage({super.key}); + + @override + State createState() => _DeskAccountPageState(); +} + +class _DeskAccountPageState extends State + with SingleTickerProviderStateMixin { + late TabController tabController; + int activeIndex = 0; + + @override + void initState() { + super.initState(); + tabController = TabController(length: 3, vsync: this); + } + + @override + Widget build(BuildContext context) { + bool isDark = Theme.of(context).brightness == Brightness.dark; + List tabs = [ + context.l10n.homeAccountTabInfo, + context.l10n.homeAccountTabMe, + context.l10n.homeAccountSupport, + ]; + + return Scaffold( + body: Column( + children: [ + // DeskAccountTopBar( + // leading: Row( + // children: [ + // FlutterUnitText( + // text: 'Flutter Unit', + // color: Theme.of(context).primaryColor, + // fontSize: 24, + // ), + // const SizedBox( + // width: 20, + // ), + // Text( + // context.l10n.slogan, + // style: TextStyle(color: Colors.grey), + // ) + // ], + // ), + // ), + Expanded( + child: CustomScrollView( + slivers: [ + SliverToBoxAdapter( + child: UserHeader(), + ), + SliverPinnedHeader( + color: isDark ? Color(0xff2C3036) : Colors.white, + child: TabBar( + onTap: (i) { + setState(() { + activeIndex = i; + }); + }, + tabAlignment: TabAlignment.start, + indicatorSize: TabBarIndicatorSize.label, + isScrollable: true, + // indicator: RoundRectTabIndicator( + // borderSide: BorderSide(color: themeColor, width: 3), + // ), + labelStyle: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + ), + controller: tabController, + // labelColor: themeColor, + indicatorWeight: 3, + unselectedLabelColor: Colors.grey, + // indicatorColor: themeColor, + tabs: tabs + .map((String name) => Tab(text: name)) + .toList(), + )), + if (activeIndex == 0) const SliverCollectionPanel(), + if (activeIndex == 1) const SliverSharePanel(), + if (activeIndex == 2) const SliverListPanel(), + ], + )) + ], + ), + ); + } +} diff --git a/modules/basic_system/app/lib/view/account/desk/sliver_cellection_panel.dart b/modules/basic_system/app/lib/view/account/desk/sliver_cellection_panel.dart new file mode 100644 index 000000000..9097f13e1 --- /dev/null +++ b/modules/basic_system/app/lib/view/account/desk/sliver_cellection_panel.dart @@ -0,0 +1,64 @@ +import 'package:flutter/material.dart'; + +import '../../about/about_app_page.dart'; + +class SliverCollectionPanel extends StatelessWidget { + const SliverCollectionPanel({super.key}); + + @override + Widget build(BuildContext context) { + List items = [ + InfoPanel( + title: '项目简介', + info: 'Flutter Unit 是一个非盈利性的开源项目,' + '旨在提供全面的 Flutter 学习指南及编程者的交流技术的接口。' + '由【张风捷特烈】提供技术支持和全权维护。唯一开源网站网址:\n ' + 'https://github.com/toly1994328/FlutterUnit', + ), + const SizedBox(height: 10,), + InfoPanel( + title: 'Flutter Unit 1.0', + info: 'Flutter Unit 1.0 核心计划是收录widget,即widget集录。' + '目前收录组件 283 个,均可在 app 中进行查看。' + '项目中提供widget图鉴文件可供下载参考。功能主要如下:\n' + '○ 280+的 Flutter 组件收录和详情介绍。\n' + '○ 对一些重要的组件提供操作体验。\n' + '○ link to功能,查看组件时可以切换到相关组件。\n' + '○ 组件收藏和取消收藏功能。\n' + '○ 主题、字体设置,代码风格等全局状态管理。\n' + '○ 搜索功能和组件星级分类。', + ), + const SizedBox(height: 10,), + InfoPanel( + title: 'Flutter Unit 2.0 ', + info: + '○ 317 个 Flutter 组件收录和详情介绍。\n' + '○ 绘制集录用于收录绘制相关的优秀示例。\n' + '○ 要点集录用于收录 Flutter 相关的小知识。\n' + '○ 时光轴,查看 FlutterUnit 重要事件。\n' + '○ 实现应用内更新功能,方便使用者及时更新到最新版体验。' + ), + const SizedBox(height: 10,), + InfoPanel( + title: 'Flutter Unit 3.0 ', + info: + '○ 335 个 Flutter 组件收录和详情介绍。\n' + '○ 知识宝库收录 Flutter 精品文章。\n' + '○ 算法演绎尝试基于 Flutter 可视化展示算法流程。\n' + '○ 工具宝箱,通过 Flutter 界面交互实现一些全平台辅助工具。\n' + '○ 功能全面升级,多语言、暗色模式、Navigator2.0 支持。' + ), + const SizedBox(height: 20,) + ]; + + + return SliverList( + delegate: SliverChildBuilderDelegate( + (_, index) => Padding( + padding: const EdgeInsets.symmetric(horizontal: 48.0), + child: items[index], + ), + childCount: items.length), + ); + } +} diff --git a/modules/basic_system/app/lib/view/account/desk/sliver_list_panel.dart b/modules/basic_system/app/lib/view/account/desk/sliver_list_panel.dart new file mode 100644 index 000000000..6d54fbd51 --- /dev/null +++ b/modules/basic_system/app/lib/view/account/desk/sliver_list_panel.dart @@ -0,0 +1,94 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:toly_ui/toly_ui.dart'; + +import '../../about/about_app_page.dart'; + +class SliverListPanel extends StatelessWidget { + const SliverListPanel({super.key}); + + //coffee1.webp + @override + Widget build(BuildContext context) { + List items = [ + InfoPanel( + title: '开源不易,请我喝咖啡 ~', + info: 'Flutter Unit 是一个非盈利性的开源项目,' + '旨在提供全面的 Flutter 学习指南及编程者的交流技术的接口。' + '由【张风捷特烈】提供技术支持和全权维护。唯一开源网站网址:\n ' + 'https://github.com/toly1994328/FlutterUnit', + ), + Divider( + height: 20, + ), + InfoPanel( + title: 'Flutter Unit 1.0', + info: 'Flutter Unit 1.0 核心计划是收录widget,即widget集录。' + '目前收录组件 283 个,均可在 app 中进行查看。' + '项目中提供widget图鉴文件可供下载参考。功能主要如下:\n' + '○ 280+的 Flutter 组件收录和详情介绍。\n' + '○ 对一些重要的组件提供操作体验。\n' + '○ link to功能,查看组件时可以切换到相关组件。\n' + '○ 组件收藏和取消收藏功能。\n' + '○ 主题、字体设置,代码风格等全局状态管理。\n' + '○ 搜索功能和组件星级分类。', + ), + Divider( + height: 20, + ), + InfoPanel( + title: 'Flutter Unit 2.0 ', + info: '○ 317 个 Flutter 组件收录和详情介绍。\n' + '○ 绘制集录用于收录绘制相关的优秀示例。\n' + '○ 要点集录用于收录 Flutter 相关的小知识。\n' + '○ 时光轴,查看 FlutterUnit 重要事件。\n' + '○ 实现应用内更新功能,方便使用者及时更新到最新版体验。') + ]; + + return SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 28.0), + child: Column( + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Circle(color: Theme.of(context).primaryColor), + Padding( + padding: const EdgeInsets.only(left: 15, top: 15, bottom: 15), + child: Text( + '开源不易,请我喝咖啡 ~', + style: const TextStyle( + fontSize: 16, fontWeight: FontWeight.bold), + ), + ), + ], + ), + Row( + children: [ + Expanded( + child: Image.asset( + 'assets/images/coffee_zfb.webp', + ), + ), + const SizedBox(width: 8,), + Expanded( + child: Image.asset( + 'assets/images/coffee_wx.webp', + ), + ), + const SizedBox(width: 8,), + Expanded( + child: Image.asset( + 'assets/images/coffee_wx_ac.webp', + ), + ), + ], + ) + ], + ), + ), + ); + } +} diff --git a/modules/basic_system/app/lib/view/account/desk/sliver_share_panel.dart b/modules/basic_system/app/lib/view/account/desk/sliver_share_panel.dart new file mode 100644 index 000000000..c98eba0e6 --- /dev/null +++ b/modules/basic_system/app/lib/view/account/desk/sliver_share_panel.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; + +class SliverSharePanel extends StatelessWidget { + const SliverSharePanel({super.key}); + + @override + Widget build(BuildContext context) { + List items = [ + const SizedBox(height: 12), + const Text( + '邮箱: 1981462002@qq.com\n' + '微信群: 编程技术交流圣地 -【Flutter群】\n' + '公众号: 编程之王\n' + '愿青梅煮酒,与君天涯共话。', + style: TextStyle(color: Colors.grey)), + const SizedBox(height: 10), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Image.asset( + 'assets/images/wechat.webp', + width: 200, + height: 200, + ), + ), + const Center( + child: Text( + '我的微信', + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ), + ), + ], + ), + const SizedBox(width: 20,), + Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Image.asset( + 'assets/images/wxgzh.webp', + width: 200, + height: 200, + ), + ), + const Center( + child: Text( + '我的公众号', + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ), + ), + ], + ), + ], + ), + + ]; + + + return SliverToBoxAdapter( + child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + children: items, + ), + ); + } +} diff --git a/modules/basic_system/app/lib/view/account/desk/user_header.dart b/modules/basic_system/app/lib/view/account/desk/user_header.dart new file mode 100644 index 000000000..c392fb266 --- /dev/null +++ b/modules/basic_system/app/lib/view/account/desk/user_header.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; +import 'package:toly_ui/toly_ui.dart'; + +class UserHeader extends StatelessWidget { + const UserHeader({super.key}); + + @override + Widget build(BuildContext context) { + bool isDark = Theme.of(context).brightness == Brightness.dark; + String image = isDark?'anim_draw.webp':'base_draw.webp'; + + return Stack( + // clipBehavior: Clip.none, + children: [ + Column( + children: [ + Image.asset( + 'assets/images/$image', + height: 150, + fit: BoxFit.fitWidth, + width: MediaQuery.of(context).size.width, + ), + Container( + alignment: Alignment.topLeft, + padding: EdgeInsets.only(left: 32 + 100 + 16, top: 12), + color: isDark?Color(0xff2C3036):Colors.white, + height: 86, + child: Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '张风捷特烈', + style: TextStyle( + fontSize: 20, fontWeight: FontWeight.bold), + ), + Text( + '海的彼岸有我未曾见证的风采', + style: TextStyle(fontSize: 12, color: Colors.grey), + ), + Text( + '公众号@编程之王', + style: TextStyle(fontSize: 12, color: Colors.grey), + ), + ], + ) + ], + ), + ) + ], + ), + Positioned( + bottom: 16, + left: 32, + child: CircleImage( + size: 100, + shadowColor: Theme.of(context) + .primaryColor + .withAlpha(33), // image: NetworkImage(state.user.userAvatar), + image: const AssetImage("assets/images/icon_head.webp"), + ), + ), + ], + ); + } +} \ No newline at end of file diff --git a/modules/basic_system/app/lib/view/data_manage/data_manage_page.dart b/modules/basic_system/app/lib/view/data_manage/data_manage_page.dart new file mode 100644 index 000000000..a6a38d011 --- /dev/null +++ b/modules/basic_system/app/lib/view/data_manage/data_manage_page.dart @@ -0,0 +1,150 @@ +import 'dart:convert'; +import 'dart:io'; +import 'package:app/app.dart'; +import 'package:l10n/l10n.dart'; +import 'package:storage/storage.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:authentication/views/authentic_widget.dart'; +import 'package:utils/utils.dart'; +import 'package:widget_module/blocs/blocs.dart'; + +import 'package:path/path.dart' as path; +import 'package:sqflite/sqflite.dart'; +import 'package:widget_module/views/mobile/category_page/sync/category_api.dart'; +import 'package:widget_module/widget_module.dart'; + +/// create by 张风捷特烈 on 2021/2/26 +/// contact me by email 1981462002@qq.com +/// 说明: +/// + +class DataManagePage extends StatelessWidget { + const DataManagePage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(context.l10n.dataManagement), + ), + body: Builder( + builder: (ctx) => ListView( + children: [ + const SizedBox( + height: 8, + ), + AuthenticWidget.just( + ListTile( + trailing: Icon( + TolyIcon.upload, + color: Theme.of(context).primaryColor, + ), + title: const Text('备份收藏集数据'), + onTap: () => _doUploadCategoryData(ctx), + ), + ), + AuthenticWidget.just(const Divider()), + AuthenticWidget.just(ListTile( + trailing: Icon( + TolyIcon.download, + color: Theme.of(context).primaryColor, + ), + title: const Text('同步收藏集数据'), + onTap: () => _doSync(ctx), + )), + AuthenticWidget.just(const Divider()), + ListTile( + trailing: Icon( + Icons.refresh, + color: Theme.of(context).primaryColor, + ), + title: Text(context.l10n.favoritesCollectionDataReset), + // trailing: _nextIcon(context), + onTap: () => _recallDatabase(ctx), + ), + const Divider(), + ], + ), + ), + ); + } + + _recallDatabase(BuildContext context) async { + String databasesPath = await getDatabasesPath(); + String dbPath = path.join(databasesPath, "flutter.db"); + ByteData data = await rootBundle.load(path.join("assets", "flutter.db")); + List bytes = + data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); + await File(dbPath).writeAsBytes(bytes, flush: true); + print("==== debug ===== assets ======拷贝完成===="); + BlocProvider.of(context).add(const EventLoadCategory()); + context.read().loadLikeData(); + Toast.toast(context, '重置成功!'); + } + + void _doUploadCategoryData(BuildContext context) async { + // CategoryRepository rep = BlocProvider.of(context).repository; + // List loadCategories = await rep.loadCategoryData(); + // + // List likeData = await AppStorage().flutter().likeWidgetIds(); + // + // String json = jsonEncode(loadCategories); + // String likeJson = jsonEncode(likeData); + // + // TaskResult result = + // await CategoryApi.uploadCategoryData(data: json, likeData: likeJson); + // + // if (result.success) { + // Toast.toast(context, '数据集备份成功!'); + // } else { + // Toast.toast(context, '数据集备份失败!'); + // } + } + + void _doSync(BuildContext context) async { + TaskResult result = await CategoryApi.getCategoryData(); + + if (result.success) { + // 说明请求成功 + if (result.data != null) { + //说明有后台备份数据,进行同步操作 + CategoryRepository repository = + BlocProvider.of(context).repository; + await repository.syncCategoryByData( + result.data!.data, result.data!.likeData); + BlocProvider.of(context).add(const EventLoadCategory()); + context.read().loadLikeData(); + } else { + // 说明还没有后台数据, + // 这里防止有傻孩子没点备份,就点同步,哥哥好心,给备份一下。 + // CategoryRepository rep = + // BlocProvider.of(context).repository; + // List loadCategories = await rep.loadCategoryData(); + // List likeData = await AppStorage().flutter().likeWidgetIds(); + // + // String json = jsonEncode(loadCategories); + // String likeJson = jsonEncode(likeData); + // await CategoryApi.uploadCategoryData(data: json, likeData: likeJson); + } + Toast.toast(context, '数据同步份成功!'); + } else { + Toast.toast(context, '数据同步份失败!'); + } + } +} + +// class LoadingIndicate extends StatefulWidget { +// Future Function task; +// @override +// _LoadingIndicateState createState() => _LoadingIndicateState(); +// } +// +// class _LoadingIndicateState extends State { +// @override +// Widget build(BuildContext context) { +// return Container(); +// } +// } diff --git a/lib/app/views/setting/app_style_setting.dart b/modules/basic_system/app/lib/view/setting/app_style_setting.dart similarity index 100% rename from lib/app/views/setting/app_style_setting.dart rename to modules/basic_system/app/lib/view/setting/app_style_setting.dart diff --git a/lib/app/views/setting/code_style_setting.dart b/modules/basic_system/app/lib/view/setting/code_style_setting.dart similarity index 89% rename from lib/app/views/setting/code_style_setting.dart rename to modules/basic_system/app/lib/view/setting/code_style_setting.dart index 9f3567cc5..dba01045f 100644 --- a/lib/app/views/setting/code_style_setting.dart +++ b/modules/basic_system/app/lib/view/setting/code_style_setting.dart @@ -1,6 +1,6 @@ import 'package:app/app.dart'; -import 'package:components/components.dart'; -import 'package:components/toly_ui/toly_ui.dart'; +import 'package:l10n/l10n.dart'; +import 'package:toly_ui/toly_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -31,8 +31,8 @@ class Hello { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: Text('代码高亮样式')), - body: BlocBuilder( + appBar: AppBar(title: Text(context.l10n.codeHighlightStyle)), + body: BlocBuilder( builder: (_, state) => _buildFontCell(context, Cons.codeThemeSupport.keys.toList(), state.codeStyleIndex)), ); @@ -46,7 +46,7 @@ class Hello { a: 0.95, duration: const Duration(milliseconds: 200), onPressed: (){ - BlocProvider.of(context).switchCoderTheme(i); + BlocProvider.of(context).switchCoderTheme(i); }, child: Stack( fit: StackFit.passthrough, diff --git a/lib/app/views/setting/font_setting.dart b/modules/basic_system/app/lib/view/setting/font_setting.dart similarity index 92% rename from lib/app/views/setting/font_setting.dart rename to modules/basic_system/app/lib/view/setting/font_setting.dart index b3b4938df..8321d6b9c 100644 --- a/lib/app/views/setting/font_setting.dart +++ b/modules/basic_system/app/lib/view/setting/font_setting.dart @@ -1,6 +1,5 @@ import 'package:app/app.dart'; -import 'package:components/components.dart'; -import 'package:components/toly_ui/toly_ui.dart'; +import 'package:toly_ui/toly_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -13,7 +12,7 @@ class FontSettingPage extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder( + return BlocBuilder( builder: (_, state) =>Scaffold( appBar: AppBar(title: Text('字体设置 - font setting',style: TextStyle(fontFamily:state.fontFamily ),)), body: _buildFontCell( @@ -25,7 +24,7 @@ class FontSettingPage extends StatelessWidget { BuildContext context, List fontFamilySupport, String fontFamily) { return GridView.count( padding: const EdgeInsets.only(top: 20, left: 10, right: 10), - crossAxisCount: 2, + crossAxisCount: kIsDesk?4:2, mainAxisSpacing: 10, crossAxisSpacing: 10, childAspectRatio: 1.5, @@ -34,7 +33,7 @@ class FontSettingPage extends StatelessWidget { active: fontFamily == e, fontFamily: e, onSelect: (font) { - BlocProvider.of(context).switchFontFamily(font); + BlocProvider.of(context).switchFontFamily(font); }, ); }).toList(), diff --git a/lib/app/views/setting/item_style_setting.dart b/modules/basic_system/app/lib/view/setting/item_style_setting.dart similarity index 100% rename from lib/app/views/setting/item_style_setting.dart rename to modules/basic_system/app/lib/view/setting/item_style_setting.dart diff --git a/modules/basic_system/app/lib/view/setting/language_setting.dart b/modules/basic_system/app/lib/view/setting/language_setting.dart new file mode 100644 index 000000000..cc520b143 --- /dev/null +++ b/modules/basic_system/app/lib/view/setting/language_setting.dart @@ -0,0 +1,76 @@ +import 'package:app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:l10n/l10n.dart'; + +class LanguageSettingPage extends StatelessWidget { + const LanguageSettingPage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(context.l10n.settingLanguage), + ), + body: const LanguageSetting(), + ); + } +} + +class LanguageSetting extends StatelessWidget { + const LanguageSetting({super.key}); + + @override + Widget build(BuildContext context) { + List languages = Language.values; + Language activeLanguage = + context.select((bloc) => bloc.state.language); + Color iconColor = Theme.of(context).primaryColor; + + return ListView.separated( + padding: const EdgeInsets.only(top: 8), + separatorBuilder: (_, __) => const Divider(), + itemBuilder: (_, index) { + Language language = languages[index]; + return ListTile( + title: Text(language.label), + onTap: () { + context.read().switchLanguage(language); + }, + trailing: activeLanguage == language + ? Icon(Icons.check, size: 20, color: iconColor) + : null, + ); + }, + itemCount: languages.length, + ); + } +} + +class LanguageSwitchTile extends StatelessWidget { + const LanguageSwitchTile({super.key}); + + @override + Widget build(BuildContext context) { + Language activeLanguage = + context.select((bloc) => bloc.state.language); + Color color = Theme.of(context).primaryColor; + return ListTile( + leading: Icon( + Icons.language, + color: color, + ), + title: Text(context.l10n.settingLanguageText, + style: TextStyle(fontSize: 16)), + subtitle: Text( + '${activeLanguage.label}: ${activeLanguage.locale}', + style: TextStyle(fontSize: 12), + ), + trailing: Icon(Icons.chevron_right, color: color), + onTap: () { + showModalBottomSheet( + context: context, builder: (_) => LanguageSettingPage()); + }, + ); + } +} diff --git a/modules/basic_system/app/lib/view/setting/setting_page.dart b/modules/basic_system/app/lib/view/setting/setting_page.dart new file mode 100644 index 000000000..789fc4c84 --- /dev/null +++ b/modules/basic_system/app/lib/view/setting/setting_page.dart @@ -0,0 +1,176 @@ +import 'package:app/app.dart'; +import 'package:go_router/go_router.dart'; +import 'package:l10n/l10n.dart'; +import 'package:toly_ui/toly_ui.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'language_setting.dart'; +import 'package:app_update/app_update.dart'; + +class SettingPage extends StatelessWidget { + const SettingPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + const Widget divider = Divider(height: 1); + + return DragToMoveWrapper( + child: Scaffold( + appBar: AppBar(title: Text(context.l10n.appSettings)), + body: ListView( + children: [ + Container(height: 15), + ListTile( + leading: Icon( + Icons.style, + color: Theme.of(context).primaryColor, + ), + title: + Text(context.l10n.darkMode, style: TextStyle(fontSize: 16)), + subtitle: BlocBuilder( + builder: (_, state) { + String info = switch (state.themeMode) { + ThemeMode.system => context.l10n.followSystem, + ThemeMode.light => context.l10n.lightMode, + ThemeMode.dark => context.l10n.darkMode, + }; + return Text(info, + style: const TextStyle(fontSize: 12, color: Colors.grey)); + }, + ), + trailing: _nextIcon(context), + onTap: () => context.push('/settings/dark_mode'), + ), + divider, + ListTile( + leading: Icon( + Icons.palette, + color: Theme.of(context).primaryColor, + ), + title: Text(context.l10n.themeColorSetting, + style: TextStyle(fontSize: 16)), + subtitle: BlocBuilder( + builder: (_, state) => Text( + state.themeColor.label(context), + style: TextStyle(color: state.themeColor.color, fontSize: 12), + ), + ), + trailing: _nextIcon(context), + onTap: () => context.push('/settings/theme_color'), + ), + // divider, + Container(height: 10), + ListTile( + leading: Icon( + Icons.translate, + color: Theme.of(context).primaryColor, + ), + title: Text(context.l10n.fontSetting, + style: TextStyle(fontSize: 16)), + subtitle: BlocBuilder( + builder: (_, state) => Text( + state.fontFamily, + style: TextStyle(fontSize: 12), + ), + ), + trailing: _nextIcon(context), + onTap: () => context.push('/settings/font_setting'), + ), + divider, + const LanguageSwitchTile(), + divider, + ListTile( + leading: Icon( + TolyIcon.icon_code, + color: Theme.of(context).primaryColor, + ), + title: Text(context.l10n.codeHighlightStyle, + style: TextStyle(fontSize: 16)), + trailing: _nextIcon(context), + onTap: () => context.push('/settings/code_style'), + ), + // divider, + Container( + height: 10, + ), + // _buildShowBg(context), + divider, + _buildShowOver(context), + // divider, + // _buildShowTool(context), + divider, + // Container( height: 10), + VersionTiled(), + ], + ), + ), + ); + } + + Widget _buildShowOver(BuildContext context) => + BlocBuilder( + builder: (_, state) => TolySwitchListTile( + secondary: Icon( + TolyIcon.icon_background, + color: Theme.of(context).primaryColor, + ), + title: Text(context.l10n.displayPerformanceFloatingLayer, + style: TextStyle(fontSize: 16)), + value: state.showPerformanceOverlay, + onChanged: (bool value) { + BlocProvider.of(context).switchShowOver(value); + }, + )); + + Widget _nextIcon(BuildContext context) => + Icon(Icons.chevron_right, color: Theme.of(context).primaryColor); +} + +class VersionTiled extends StatelessWidget { + const VersionTiled({super.key}); + + @override + Widget build(BuildContext context) { + Color themeColor = Theme.of(context).primaryColor; + Widget title = Text(context.l10n.versionInformation, style: TextStyle(fontSize: 16)); + + UpdateState state = context.watch().state; + + if (state is ShouldUpdateState) { + title = Wrap( + spacing: 8, + crossAxisAlignment: WrapCrossAlignment.center, + children: [title, AppUpgradeTips(state: state)], + ); + } + + return ListTile( + leading: Icon(Icons.info, color: themeColor), + title: title, + trailing: Icon(Icons.chevron_right, color: themeColor), + onTap: () => context.push('/settings/version'), + ); + } +} + +class AppUpgradeTips extends StatelessWidget { + final ShouldUpdateState state; + + const AppUpgradeTips({super.key, required this.state}); + + @override + Widget build(BuildContext context) { + Color themeColor = Theme.of(context).primaryColor; + bool downloading = state.isDownloading; + String text = downloading ? state.progressDisplay : '新版本'; + Color color = downloading ? themeColor : Colors.redAccent; + return Container( + decoration: + BoxDecoration(color: color, borderRadius: BorderRadius.circular(4)), + padding: EdgeInsets.symmetric(horizontal: 4, vertical: 4), + child: Text( + text, + style: TextStyle(color: Colors.white, fontSize: 10, height: 1), + )); + } +} diff --git a/modules/basic_system/app/lib/view/setting/theme_color_setting.dart b/modules/basic_system/app/lib/view/setting/theme_color_setting.dart new file mode 100644 index 000000000..e43a687ab --- /dev/null +++ b/modules/basic_system/app/lib/view/setting/theme_color_setting.dart @@ -0,0 +1,92 @@ +import 'package:app/app.dart'; +import 'package:components/components.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:toly_ui/toly_ui.dart'; +/// create by 张风捷特烈 on 2020-04-10 +/// contact me by email 1981462002@qq.com +/// 说明: + +class ThemeColorSettingPage extends StatelessWidget { + const ThemeColorSettingPage({Key? key}) : super(key: key); + + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: UnitColor.scaffoldBgLight, + appBar: const UnitAppbar(title:'主题色设置'), + body: BlocBuilder( + builder: (_, state) => _buildCell( + context, ThemeColor.values, state.themeColor)), + ); + } + + Widget _buildCell( + BuildContext context, List themeColorSupport, ThemeColor color) { + return GridView.count( + padding: const EdgeInsets.only(top: 20, left: 10, right: 10), + shrinkWrap: true, + crossAxisCount: kIsDesk?4:2, + mainAxisSpacing: 10, + crossAxisSpacing: 10, + childAspectRatio: 1.5, + children: themeColorSupport + .map((ThemeColor c) => FeedbackWidget( + a: 0.95, + duration: const Duration(milliseconds: 200), + onPressed: () => BlocProvider.of(context).switchThemeColor(c), + child: GridTile( + header: Container( + decoration: BoxDecoration( + borderRadius: const BorderRadius.only(topLeft: Radius.circular(10),topRight: Radius.circular(10)), + color: color == c + ? Colors.blue.withAlpha(88): + Colors.grey.withAlpha(55), + ), + padding: const EdgeInsets.only(left: 10, right: 5), + height: 30, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Spacer(), + Text(colorString(c.color), + style: const TextStyle( + color: Colors.white, + )), + const Spacer(), + if (color == c) const Padding( + padding: EdgeInsets.only(right:8.0), + child: Circle(color: Colors.white,radius: 7,), + ) + ], + ), + ), + child: Container( + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(10)), + gradient: LinearGradient(colors: [ + c.color.shade50, + c.color.shade100, + c.color.shade200, + c.color.shade300, + c.color.shade400, + c.color.shade500, + c.color.shade600, + c.color.shade700, + c.color.shade800, + c.color.shade900, + ])), + alignment: const Alignment(0,0.35), + child: Text( + c.label(context), + style: const TextStyle(fontSize: 18,color: Colors.white,fontWeight: FontWeight.bold), + )), + ))) + .toList(), + ); + } + + String colorString(Color color) => + "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; +} diff --git a/modules/basic_system/app/lib/view/setting/theme_model_setting.dart b/modules/basic_system/app/lib/view/setting/theme_model_setting.dart new file mode 100644 index 000000000..aa6450aed --- /dev/null +++ b/modules/basic_system/app/lib/view/setting/theme_model_setting.dart @@ -0,0 +1,76 @@ +import 'package:app/app.dart'; +import 'package:l10n/l10n.dart'; +import 'package:toly_ui/toly_ui.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class ThemeModelSetting extends StatelessWidget { + const ThemeModelSetting({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + ThemeMode mode = context + .select((bloc) => bloc.state.themeMode); + Color iconColor = Theme.of(context).primaryColor; + String dark = context.l10n.darkMode; + String light = context.l10n.lightMode; + String followSystem = context.l10n.followSystem; + String manualSetting = context.l10n.manualSetting; + String info = + context.l10n.afterOpeningWillFollowTheSystemToOpenOrCloseDarkMode; + return Scaffold( + appBar: AppBar(title: Text(dark)), + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + height: 15, + ), + TolySwitchListTile( + title: Text(followSystem, + style: + const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), + subtitle: Text( + info, + style: const TextStyle(fontSize: 12, color: Colors.grey), + ), + value: mode == ThemeMode.system, + onChanged: (bool value) { + ThemeMode newModel; + if (value) { + newModel = ThemeMode.system; + } else { + newModel = ThemeMode.light; + } + context.read().changeThemeMode(newModel); + }, + ), + Padding( + padding: const EdgeInsets.only(left: 10, top: 16, bottom: 6), + child: Text(manualSetting), + ), + ListTile( + title: Text(light), + onTap: () { + context.read().changeThemeMode(ThemeMode.light); + }, + trailing: mode == ThemeMode.light + ? Icon(Icons.check, size: 20, color: iconColor) + : null, + ), + const Divider(), + ListTile( + title: Text(dark), + onTap: () { + context.read().changeThemeMode(ThemeMode.dark); + }, + trailing: mode == ThemeMode.dark + ? Icon(Icons.check, size: 20, color: iconColor) + : null, + ) + ], + ), + ); + ; + } +} diff --git a/lib/app/views/unit_todo/attr_unit_page.dart b/modules/basic_system/app/lib/view/unit_todo/attr_unit_page.dart similarity index 98% rename from lib/app/views/unit_todo/attr_unit_page.dart rename to modules/basic_system/app/lib/view/unit_todo/attr_unit_page.dart index b0ffb70b9..7b58610a0 100644 --- a/lib/app/views/unit_todo/attr_unit_page.dart +++ b/modules/basic_system/app/lib/view/unit_todo/attr_unit_page.dart @@ -1,4 +1,4 @@ -import 'package:components/toly_ui/toly_ui.dart'; +import 'package:toly_ui/toly_ui.dart'; import 'package:flutter/material.dart'; class AttrUnitPage extends StatelessWidget { diff --git a/lib/app/views/unit_todo/layout_unit_page.dart b/modules/basic_system/app/lib/view/unit_todo/layout_unit_page.dart similarity index 98% rename from lib/app/views/unit_todo/layout_unit_page.dart rename to modules/basic_system/app/lib/view/unit_todo/layout_unit_page.dart index 1b329a1ef..ae7beee78 100644 --- a/lib/app/views/unit_todo/layout_unit_page.dart +++ b/modules/basic_system/app/lib/view/unit_todo/layout_unit_page.dart @@ -1,6 +1,6 @@ -import 'package:components/toly_ui/toly_ui.dart'; +import 'package:toly_ui/toly_ui.dart'; import 'package:flutter/material.dart'; diff --git a/lib/app/views/unit_todo/paint_unit_page.dart b/modules/basic_system/app/lib/view/unit_todo/paint_unit_page.dart similarity index 98% rename from lib/app/views/unit_todo/paint_unit_page.dart rename to modules/basic_system/app/lib/view/unit_todo/paint_unit_page.dart index b3dece21d..bf9ae950c 100644 --- a/lib/app/views/unit_todo/paint_unit_page.dart +++ b/modules/basic_system/app/lib/view/unit_todo/paint_unit_page.dart @@ -1,4 +1,4 @@ -import 'package:components/toly_ui/toly_ui.dart'; +import 'package:toly_ui/toly_ui.dart'; import 'package:flutter/material.dart'; diff --git a/lib/app/views/unit_todo/point_unit_page.dart b/modules/basic_system/app/lib/view/unit_todo/point_unit_page.dart similarity index 96% rename from lib/app/views/unit_todo/point_unit_page.dart rename to modules/basic_system/app/lib/view/unit_todo/point_unit_page.dart index e0df03fd8..92ac9be8b 100644 --- a/lib/app/views/unit_todo/point_unit_page.dart +++ b/modules/basic_system/app/lib/view/unit_todo/point_unit_page.dart @@ -1,6 +1,6 @@ import 'package:app/app.dart'; import 'package:flutter/material.dart'; -import 'package:components/toly_ui/toly_ui.dart'; +import 'package:toly_ui/toly_ui.dart'; class BugUnitPage extends StatelessWidget { const BugUnitPage({Key? key}) : super(key: key); @@ -26,7 +26,7 @@ class BugUnitPage extends StatelessWidget { children: [ FeedbackWidget( onPressed: (){ - Navigator.of(context).pushNamed(UnitRouter.issues_point); + // Navigator.of(context).pushNamed(UnitRouter.issues_point); }, child: const CircleImage( image: AssetImage('assets/images/icon_head.webp'), diff --git a/modules/basic_system/app/lib/view/view.dart b/modules/basic_system/app/lib/view/view.dart new file mode 100644 index 000000000..c42f28f13 --- /dev/null +++ b/modules/basic_system/app/lib/view/view.dart @@ -0,0 +1,13 @@ +export 'about/about_app_page.dart'; +export 'about/about_me_page.dart'; +export 'about/version_info.dart'; +export 'account/desk/desk_account_page.dart'; +export 'setting/setting_page.dart'; +export 'setting/app_style_setting.dart'; +export 'setting/font_setting.dart'; +export 'setting/item_style_setting.dart'; +export 'setting/language_setting.dart'; +export 'setting/theme_color_setting.dart'; +export 'setting/code_style_setting.dart'; +export 'setting/theme_model_setting.dart'; +export 'data_manage/data_manage_page.dart'; \ No newline at end of file diff --git a/lib/app/navigation/overlay_tool_wrapper.dart b/modules/basic_system/app/lib/view/wrapper/overlay_tool_wrapper.dart similarity index 94% rename from lib/app/navigation/overlay_tool_wrapper.dart rename to modules/basic_system/app/lib/view/wrapper/overlay_tool_wrapper.dart index 554519f57..0e3a3b106 100644 --- a/lib/app/navigation/overlay_tool_wrapper.dart +++ b/modules/basic_system/app/lib/view/wrapper/overlay_tool_wrapper.dart @@ -1,10 +1,6 @@ import 'package:app/app.dart'; -import 'package:components/toly_ui/toly_ui.dart'; +import 'package:toly_ui/toly_ui.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/point_system/blocs/point_system_bloc.dart'; - - /// create by 张风捷特烈 on 2020/10/21 /// contact me by email 1981462002@qq.com @@ -157,7 +153,7 @@ class OverlayToolWrapperState extends State // 处理 菜单 item 点击事件 void _toSetting() { - Navigator.of(context).pushNamed(UnitRouter.setting); + // Navigator.of(context).pushNamed(UnitRouter.setting); } void _toWidget() {} @@ -167,8 +163,8 @@ class OverlayToolWrapperState extends State } void _toPoint() { - BlocProvider.of(context).add(EventLoadPoint()); - Navigator.of(context).pushNamed(UnitRouter.point); + // BlocProvider.of(context).add(EventLoadPoint()); + // Navigator.of(context).pushNamed(UnitRouter.point); } void _doClose() { diff --git a/modules/basic_system/app/pubspec.yaml b/modules/basic_system/app/pubspec.yaml new file mode 100644 index 000000000..04484de64 --- /dev/null +++ b/modules/basic_system/app/pubspec.yaml @@ -0,0 +1,48 @@ +name: app +description: A new Flutter package project. +version: 0.0.1 +homepage: +publish_to: none + +environment: + sdk: ">=3.5.0 <4.0.0" + flutter: ">=1.17.0" + +resolution: workspace + +dependencies: + flutter: + sdk: flutter + +flutter: + + # To add assets to your package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # To add custom fonts to your package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/app/test/app_config_test.dart b/modules/basic_system/app/test/app_config_test.dart similarity index 100% rename from packages/app/test/app_config_test.dart rename to modules/basic_system/app/test/app_config_test.dart diff --git a/packages/app/.gitignore b/modules/basic_system/app_update/.gitignore similarity index 100% rename from packages/app/.gitignore rename to modules/basic_system/app_update/.gitignore diff --git a/packages/app_update/.metadata b/modules/basic_system/app_update/.metadata similarity index 100% rename from packages/app_update/.metadata rename to modules/basic_system/app_update/.metadata diff --git a/packages/app/CHANGELOG.md b/modules/basic_system/app_update/CHANGELOG.md similarity index 100% rename from packages/app/CHANGELOG.md rename to modules/basic_system/app_update/CHANGELOG.md diff --git a/packages/app/LICENSE b/modules/basic_system/app_update/LICENSE similarity index 100% rename from packages/app/LICENSE rename to modules/basic_system/app_update/LICENSE diff --git a/packages/app/README.md b/modules/basic_system/app_update/README.md similarity index 100% rename from packages/app/README.md rename to modules/basic_system/app_update/README.md diff --git a/packages/app/analysis_options.yaml b/modules/basic_system/app_update/analysis_options.yaml similarity index 100% rename from packages/app/analysis_options.yaml rename to modules/basic_system/app_update/analysis_options.yaml diff --git a/modules/basic_system/app_update/lib/app_update.dart b/modules/basic_system/app_update/lib/app_update.dart new file mode 100644 index 000000000..ee3abb437 --- /dev/null +++ b/modules/basic_system/app_update/lib/app_update.dart @@ -0,0 +1,10 @@ +library app_update; + +export 'bloc/bloc.dart'; +export 'bloc/state.dart'; +export 'bloc/event.dart'; +export 'repository/model/app_info.dart'; +export 'views/app_update_panel.dart'; +export 'views/update_red_point.dart'; + +export 'repository/api/upgrade_api.dart'; \ No newline at end of file diff --git a/modules/basic_system/app_update/lib/bloc/bloc.dart b/modules/basic_system/app_update/lib/bloc/bloc.dart new file mode 100644 index 000000000..e19fe6832 --- /dev/null +++ b/modules/basic_system/app_update/lib/bloc/bloc.dart @@ -0,0 +1,102 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:app/app.dart'; +import 'package:app_update/repository/api/upgrade_api.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:open_file/open_file.dart'; + +import 'package:path_provider/path_provider.dart'; +import 'package:r_upgrade/r_upgrade.dart'; +import 'package:fx_dio/fx_dio.dart'; +import 'package:url_launcher/url_launcher.dart'; +import '../repository/model/app_info.dart'; + +import 'event.dart'; +import 'state.dart'; +import 'package:path/path.dart' as p; + +class UpgradeBloc extends Bloc { + final UpgradeApi api; + + UpgradeBloc({required this.api}) : super(const NoUpdateState()) { + on(_onCheckUpdate); + on(_onDownloadEvent); + on(_onProgressChangeEvent); + } + + void _onCheckUpdate(CheckUpdate event, Emitter emit) async { + emit(const CheckLoadingState()); + ApiRet ret = await api.fetch(event.appId, event.locale); + if (ret.failed) { + emit(UpdateErrorState(error: ret.msg)); + return; + } + AppInfo result = ret.data; + if (result.shouldUpgrade(kAppVersion)) { + emit(ShouldUpdateState(oldVersion: kAppVersion, info: result)); + } else { + int time = DateTime.now().millisecondsSinceEpoch; + emit(NoUpdateState(isChecked: true, checkTime: time)); + } + } + + void _onDownloadEvent(DownloadEvent event, Emitter emit) async { + UpdateState curState = state; + if (curState is! ShouldUpdateState) return; + String url = event.appInfo.url; + + if (kAppEnv.isMacOS) { + launchUrl(Uri.parse(url)); + return; + } + + void onProgressChange(double progress) { + add(ProgressChangeEvent(progress: progress)); + } + + onProgressChange(0.001); + + if (kIsDesk) { + handleDesk(url, onProgressChange); + return; + } + handleAndroid(url, onProgressChange); + } + + void handleDesk(String url, OnProgressChange callback) async { + Dio dio = Dio(); + Directory dir = await getTemporaryDirectory(); + String filePath = p.join(dir.path, p.basename(url)); + Response rep = await dio.download( + url, + filePath, + onReceiveProgress: (c, t) => callback(c / t), + ); + if (rep.statusCode == 200) { + await OpenFile.open(filePath); + } + } + + late int? id; + StreamSubscription? subscription; + + void handleAndroid(String url, OnProgressChange callback) async { + id = await RUpgrade.upgrade(url, fileName: p.basename(url)); + subscription = RUpgrade.stream.listen((DownloadInfo info) { + double progress = (info.percent ?? 0) / 100; + if (info.status == DownloadStatus.STATUS_SUCCESSFUL) { + progress = 1; + subscription?.cancel(); + } + callback(progress); + }); + } + + FutureOr _onProgressChangeEvent( + ProgressChangeEvent event, Emitter emit) async { + UpdateState curState = state; + if (curState is! ShouldUpdateState) return; + emit(curState.copyWith(progress: event.progress)); + } +} diff --git a/modules/basic_system/app_update/lib/bloc/event.dart b/modules/basic_system/app_update/lib/bloc/event.dart new file mode 100644 index 000000000..f1fcfdaed --- /dev/null +++ b/modules/basic_system/app_update/lib/bloc/event.dart @@ -0,0 +1,36 @@ +import 'package:equatable/equatable.dart'; + +import '../repository/model/app_info.dart'; + +sealed class UpdateEvent extends Equatable { + const UpdateEvent(); +} + +// 检查更新 ---> 校验,转换状态 +class CheckUpdate extends UpdateEvent { + final int appId; + final String locale; + + const CheckUpdate({required this.appId,required this.locale, }); + + @override + List get props => [appId]; +} + +class DownloadEvent extends UpdateEvent { + final AppInfo appInfo; + + const DownloadEvent({required this.appInfo}); + + @override + List get props => [appInfo]; +} + +class ProgressChangeEvent extends UpdateEvent { + final double progress; + + const ProgressChangeEvent({required this.progress}); + + @override + List get props => [progress]; +} diff --git a/modules/basic_system/app_update/lib/bloc/state.dart b/modules/basic_system/app_update/lib/bloc/state.dart new file mode 100644 index 000000000..b149263d1 --- /dev/null +++ b/modules/basic_system/app_update/lib/bloc/state.dart @@ -0,0 +1,70 @@ +import 'package:equatable/equatable.dart'; + +import '../repository/model/app_info.dart'; + +sealed class UpdateState extends Equatable { + const UpdateState(); +} + +class NoUpdateState extends UpdateState { + final bool isChecked; + final int checkTime; + + const NoUpdateState({this.isChecked = false, this.checkTime = 0}); + + @override + List get props => [isChecked, checkTime]; +} + +class CheckLoadingState extends UpdateState { + const CheckLoadingState(); + + @override + List get props => []; +} + + +class UpdateErrorState extends UpdateState { + final String error; + + const UpdateErrorState({required this.error}); + + @override + List get props => [error]; + + @override + String toString() { + return 'CheckErrorState{error: $error}'; + } +} + +class ShouldUpdateState extends UpdateState { + final String oldVersion; + final double progress; + final AppInfo info; + + const ShouldUpdateState({ + required this.oldVersion, + required this.info, + this.progress = 0, + }); + + @override + List get props => [oldVersion, info,progress]; + + @override + String toString() { + return 'ShouldUpdateState{oldVersion: $oldVersion, info: $info}'; + } + + bool get isDownloading => progress > 0 && progress != 1; + String get progressDisplay => "${(progress * 100).toStringAsFixed(2)}%"; + + UpdateState copyWith({double? progress}) { + return ShouldUpdateState( + oldVersion: oldVersion, + info: info, + progress: progress ?? this.progress, + ); + } +} diff --git a/modules/basic_system/app_update/lib/repository/api/upgrade_api.dart b/modules/basic_system/app_update/lib/repository/api/upgrade_api.dart new file mode 100644 index 000000000..683c1264d --- /dev/null +++ b/modules/basic_system/app_update/lib/repository/api/upgrade_api.dart @@ -0,0 +1,12 @@ +import 'package:app/app.dart'; + +import '../model/app_info.dart'; +import 'package:fx_dio/fx_dio.dart'; + +typedef OnProgressChange = void Function(double progress); + +abstract class UpgradeApi with CheckUpgrade {} + +mixin CheckUpgrade { + Future> fetch(int appId,String locale); +} diff --git a/modules/basic_system/app_update/lib/repository/model/app_info.dart b/modules/basic_system/app_update/lib/repository/model/app_info.dart new file mode 100644 index 000000000..4be0e71e3 --- /dev/null +++ b/modules/basic_system/app_update/lib/repository/model/app_info.dart @@ -0,0 +1,71 @@ +import 'package:path/path.dart' as p; + +class AppInfo { + final String version; + final String url; + final int size; + final String? description; + final String? sha256; + + const AppInfo({ + required this.version, + required this.url, + required this.size, + required this.description, + required this.sha256, + }); + + String get appName => p.basename(url); + + factory AppInfo.fromMap(dynamic map) => AppInfo( + version: map['version'] ?? '', + url: map['url'] ?? '', + size: map['size'] ?? 0, + description: map['description'] ?? '', + sha256: map['sha256'] ?? '', + ); + + @override + String toString() { + return 'AppInfo{appName: $appName, appVersion: $version, appUrl: $url, appSize: $size}'; + } + + bool shouldUpgrade(String current) => needsUpdate(current, version); +} + +bool needsUpdate( + String oldVersion, + String newVersion, { + int versionParts = 3, // 默认三位版本号 + String prefix = '', // 默认无前缀 +}) { + // 去除版本号前缀并将其解析为整数列表 + List parseVersion(String version) { + if (prefix.isNotEmpty && version.startsWith(prefix)) { + version = version.substring(prefix.length); // 移除前缀 + } + + final parts = version.split('.'); + if (parts.length != versionParts) { + throw FormatException( + '版本号格式错误,应为包含 $versionParts 位版本号的格式,如 ${prefix}1.0.0'); + } + + return parts.map(int.parse).toList(); + } + + final oldParts = parseVersion(oldVersion); + final newParts = parseVersion(newVersion); + + // 按位比较版本号 + for (int i = 0; i < versionParts; i++) { + if (newParts[i] > oldParts[i]) { + return true; // 新版本号更大,需要更新 + } else if (newParts[i] < oldParts[i]) { + return false; // 新版本号更小,不需要更新 + } + } + + // 版本号相同,不需要更新 + return false; +} diff --git a/modules/basic_system/app_update/lib/views/app_update_panel.dart b/modules/basic_system/app_update/lib/views/app_update_panel.dart new file mode 100644 index 000000000..32e0741f9 --- /dev/null +++ b/modules/basic_system/app_update/lib/views/app_update_panel.dart @@ -0,0 +1,152 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:utils/utils.dart'; +import 'package:l10n/l10n.dart'; + +import '../bloc/bloc.dart'; +import '../bloc/event.dart'; +import '../bloc/state.dart'; +import 'dialog/update_dialog.dart'; + +class AppUpdatePanel extends StatelessWidget { + const AppUpdatePanel({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return BlocConsumer( + builder: _buildByUpdateState, + listener: _listenerByUpdateState, + ); + } + + Widget _buildProgress(BuildContext context, double progress, int appSize) { + return Wrap( + alignment: WrapAlignment.center, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + Column( + children: [ + Text( + '${(progress * 100).toStringAsFixed(2)} %', + style: const TextStyle( + height: 1, fontSize: 12, color: Colors.grey), + ), + const SizedBox( + height: 5, + ), + Text( + '${convertFileSize((appSize * progress).floor())}/${convertFileSize(appSize)}', + style: const TextStyle( + height: 1, fontSize: 10, color: Colors.grey), + ), + ], + ), + const SizedBox( + width: 15, + ), + SizedBox( + width: 20, + height: 20, + child: CircularProgressIndicator( + strokeWidth: 2, + backgroundColor: Colors.grey, + value: progress, + ), + ) + ]); + } + + Widget _buildByUpdateState(BuildContext context, UpdateState state) { + String info = context.l10n.checkUpdate; + Widget trail = const SizedBox.shrink(); + if (state is ShouldUpdateState) { + if (state.progress > 0) { + info = context.l10n.downloadingNewVersion; + trail = _buildProgress(context, state.progress, state.info.size); + } else { + info = context.l10n.downloadNewVersion; + trail = Wrap( + alignment: WrapAlignment.center, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + Text( + '${state.oldVersion} --> ${state.info.version} ', + style: const TextStyle( + height: 1, fontSize: 12, color: Colors.grey), + ), + const SizedBox(width: 5), + const Icon(Icons.update, color: Colors.green) + ]); + } + } + if (state is CheckLoadingState) { + trail = const CupertinoActivityIndicator(); + } + + return ListTile( + title: Text( + info, + style: const TextStyle(fontSize: 13), + ), + trailing: trail, + onTap: () => _tapByState(state, context), + ); + } + + void _tapByState(UpdateState state, BuildContext context) { + if (state is NoUpdateState) { + String locale = Localizations.localeOf(context).toString(); + context.read().add( CheckUpdate(appId: 1,locale: locale)); + } + + if (state is ShouldUpdateState) { + showDialog( + barrierDismissible: false, + context: context, + builder: (ctx) => UpdateDialog( + result: state.info, + onConfirm: () { + context + .read() + .add(DownloadEvent(appInfo: state.info)); + }, + )); + // if(Platform.isIOS){ + // // ios 跳转应用商店 + // RUpgrade.upgradeFromAppStore('6450545123', false); + // return; + // } + // // 处理下载的事件 + // BlocProvider.of(context).add(DownloadEvent(appInfo: state.info)); + } + } + + void _listenerByUpdateState(BuildContext context, UpdateState state) { + if (state is NoUpdateState) { + if (state.isChecked) { + Toast.success(context, context.l10n.currentIsNew); + } + } + // if (state is ShouldUpdateState) { + // showDialog( + // barrierDismissible: false, + // context: context, + // builder: (ctx) => FeiShuUpdateDialog( + // result: state.info, + // onConfirm: () {}, + // )); + // } + } + + String convertFileSize(int size) { + double result = size / 1024.0; + if (result < 1024) { + return "${result.toStringAsFixed(2)} Kb"; + } else if (result > 1024 && result < 1024 * 1024) { + return "${(result / 1024).toStringAsFixed(2)} Mb"; + } else { + return "${(result / 1024 / 1024).toStringAsFixed(2)} Gb"; + } + } +} diff --git a/modules/basic_system/app_update/lib/views/dialog/top_bar.dart b/modules/basic_system/app_update/lib/views/dialog/top_bar.dart new file mode 100644 index 000000000..29ff58ec5 --- /dev/null +++ b/modules/basic_system/app_update/lib/views/dialog/top_bar.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; + +class UpdateTopBar extends StatelessWidget { + final String text; + const UpdateTopBar({Key? key, required this.text}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + height: 68, + decoration: BoxDecoration( + color: Colors.blue, + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment(0.8, 1), + colors: [ + Color(0xff4181b4), + Color(0xff1fbcfd), + Color(0xff46d1fd), + ], + // Gradient from https://learnui.design/tools/gradient-generator.html + tileMode: TileMode.mirror, + ), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(8), topRight: Radius.circular(8))), + alignment: Alignment.center, + child: Text( + text, + style: TextStyle( + color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold), + ), + ); + } +} diff --git a/modules/basic_system/app_update/lib/views/dialog/update_dialog.dart b/modules/basic_system/app_update/lib/views/dialog/update_dialog.dart new file mode 100644 index 000000000..2fc3a81b0 --- /dev/null +++ b/modules/basic_system/app_update/lib/views/dialog/update_dialog.dart @@ -0,0 +1,262 @@ +import 'dart:ui'; +import 'package:app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../bloc/bloc.dart'; +import '../../bloc/state.dart'; +import '../../repository/model/app_info.dart'; +import 'top_bar.dart'; + +class UpdateDialog extends StatefulWidget { + final AppInfo result; + final ValueChanged? onDownloadSuccess; + final VoidCallback? onBackHide; + final VoidCallback? onConfirm; + + const UpdateDialog({ + super.key, + required this.result, + this.onDownloadSuccess, + this.onBackHide, + this.onConfirm, + }); + + @override + State createState() => _UpdateDialogState(); +} + +class _UpdateDialogState extends State { + final TextStyle noticeStyle = + const TextStyle(color: Colors.grey, fontSize: 16); + final TextStyle cancelTextStyle = + const TextStyle(color: Colors.grey, fontSize: 16); + + final TextStyle subTextStyle = const TextStyle( + color: Colors.black, fontSize: 16, fontWeight: FontWeight.bold); + + @override + Widget build(BuildContext context) { + UpdateState state = context.watch().state; + return WillPopScope( + onWillPop: () async { + // UpdateCubit.isShowDialog = false; + return true; + }, + child: Dialog( + elevation: 0, + backgroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(8))), + child: Container( + height: 360, + alignment: Alignment.topLeft, + width: kIsDesk ? 400 : 320, + // color: Colors.green, + child: Column( + mainAxisSize: MainAxisSize.min, + // crossAxisAlignment: CrossAxisAlignment.start, + children: [ + //大小为 ${(widget.result.appSize!/1024/1024).toStringAsFixed(1)}M + buildTitle(state), + Expanded(child: buildContent(state)), + buildButtons(state), + ], + ), + ), + ), + ); + } + + Widget buildContent(UpdateState state) { + if (state is ShouldUpdateState) { + if (state.progress > 0) { + return downloadProgress(state.progress); + } + return _buildUpdateInfo(); + } + return const SizedBox.shrink(); + } + + Widget downloadProgress(double progress) { + return Center( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 20), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + progress == 1 ? "文件校验中..." : '下载中...', + style: TextStyle(height: 1, fontSize: 12, color: Colors.grey), + ), + const SizedBox( + height: 10, + ), + LinearProgressIndicator( + minHeight: 10, + value: progress, + ), + const SizedBox( + height: 10, + ), + Row( + children: [ + Text("${(progress * 100).toStringAsFixed(1)}%", + style: + TextStyle(height: 1, fontSize: 12, color: Colors.grey)), + Spacer(), + Text( + "${convertFileSize(((widget.result.size) * (progress)).toInt())}/${convertFileSize(widget.result.size)}", + style: TextStyle(height: 1, fontSize: 12, color: Colors.grey), + ) + ], + ) + ], + ), + ), + ); + } + + String convertFileSize(int? size) { + if (size == null) return '0 KB'; + double result = size / 1024.0; + if (result < 1024) { + return "${result.toStringAsFixed(2)} KB"; + } else if (result > 1024 && result < 1024 * 1024) { + return "${(result / 1024).toStringAsFixed(2)} MB"; + } else { + return "${(result / 1024 / 1024).toStringAsFixed(2)} GB"; + } + } + + Widget _buildUpdateInfo() { + return SingleChildScrollView( + child: Container( + width: 500, + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '更新内容: ', + style: TextStyle(fontWeight: FontWeight.bold), + ), + const SizedBox( + height: 6, + ), + Text( + widget.result.description ?? '', + style: TextStyle(color: Colors.grey), + ), + ], + ), + ), + ); + } + + Widget buildTitle(UpdateState state) { + String text = ''; + if (state is ShouldUpdateState) { + text = "FlutterUnit v${state.info.version} 准备就绪"; + if (state.progress > 0) { + text = "正在下载更新.."; + } + } + return UpdateTopBar(text: text); + } + + Widget buildButtons(UpdateState state) { + if (state is ShouldUpdateState) { + if(state.progress>0){ + // 下载中 + return Row( + children: [ + Expanded( + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + // widget.onBackHide?.call(); + Navigator.of(context).pop(); + // UpdateCubit.isShowDialog = false; + }, + child: Container( + alignment: Alignment.center, + decoration: BoxDecoration( + border: Border( + top: BorderSide( + color: Colors.grey.withAlpha(88), + width: 1 / window.devicePixelRatio))), + height: 50, + child: Text( + '后台执行', + style: TextStyle( + color: Theme.of(context).primaryColor, fontSize: 16), + ), + ), + )), + ], + ); + } + return Row( + children: [ + Expanded( + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + Navigator.of(context).pop(false); + // UpdateCubit.isShowDialog = false; + }, + child: Container( + decoration: BoxDecoration( + border: Border( + top: BorderSide( + color: Colors.grey.withAlpha(88), + width: 1 / window.devicePixelRatio), + right: BorderSide( + color: Colors.grey.withAlpha(88), + width: 1 / window.devicePixelRatio))), + alignment: Alignment.center, + height: 50, + child: Text( + '稍后再说', + style: cancelTextStyle, + ), + ), + ), + ), + Expanded( + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + widget.onConfirm?.call(); + }, + child: Container( + decoration: BoxDecoration( + border: Border( + top: BorderSide( + color: Colors.grey.withAlpha(88), + width: 1 / window.devicePixelRatio), + right: BorderSide( + color: Colors.grey.withAlpha(88), + width: 1 / window.devicePixelRatio))), + alignment: Alignment.center, + height: 50, + child: Text( + '立即升级', + style: TextStyle( + color: Theme.of(context).primaryColor, fontSize: 16), + ), + ), + )), + ], + ); + } + + return const SizedBox.shrink(); + } + + void upgradeWindows() async { + // context.read().doUpdate(); + } +} diff --git a/packages/app_update/lib/views/update_red_point.dart b/modules/basic_system/app_update/lib/views/update_red_point.dart similarity index 85% rename from packages/app_update/lib/views/update_red_point.dart rename to modules/basic_system/app_update/lib/views/update_red_point.dart index 9108d97a4..607b7df14 100644 --- a/packages/app_update/lib/views/update_red_point.dart +++ b/modules/basic_system/app_update/lib/views/update_red_point.dart @@ -7,7 +7,7 @@ import '../bloc/state.dart'; class UpdateRedPoint extends StatelessWidget { - const UpdateRedPoint({Key? key}) : super(key: key); + const UpdateRedPoint({super.key}); @override Widget build(BuildContext context) { @@ -16,7 +16,7 @@ class UpdateRedPoint extends StatelessWidget { height: 8, decoration: const BoxDecoration(color: Colors.red, shape: BoxShape.circle), ); - return BlocBuilder( + return BlocBuilder( builder: (BuildContext context, UpdateState state) { if (state is ShouldUpdateState) { return radPoint; diff --git a/modules/basic_system/app_update/pubspec.yaml b/modules/basic_system/app_update/pubspec.yaml new file mode 100644 index 000000000..21876a662 --- /dev/null +++ b/modules/basic_system/app_update/pubspec.yaml @@ -0,0 +1,69 @@ +name: app_update +description: A new Flutter package project. +version: 0.0.1 +homepage: +publish_to: none +environment: + sdk: ">=3.5.0 <4.0.0" + flutter: ">=1.17.0" + +resolution: workspace + +dependencies: + flutter: + sdk: flutter + flutter_bloc: ^8.1.6 # 状态管理 + dio: ^5.7.0 + convert: ^3.1.2 + equatable: ^2.0.5 # 相等辅助 + shared_preferences: ^2.5.1 # xml 固化 + r_upgrade: + path: ../../ability/r_upgrade-0.4.2 + open_file: ^3.5.9 # 打开文件 + url_launcher: ^6.3.0 # url + package_info_plus: ^8.1.4 + path_provider: ^2.1.5 + fx_dio: 0.0.4+3 + + utils: + path: ../utils + app: + path: ../app + + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # To add assets to your package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # To add custom fonts to your package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/modules/basic_system/app_update/test/app_update_test.dart b/modules/basic_system/app_update/test/app_update_test.dart new file mode 100644 index 000000000..41b0517bd --- /dev/null +++ b/modules/basic_system/app_update/test/app_update_test.dart @@ -0,0 +1,15 @@ +import 'package:app/app.dart'; +import 'package:app_update/app_update.dart'; +import 'package:fx_dio/fx_dio.dart'; + +void main() async { + FxDio().register(const UnitHost()); + + Host host = FxDio()(); + String path = UnitApi.appInfo.path; + ApiRet info = await host.get(path, queryParameters: { + 'app_id': 1, + 'os': kAppEnv.os.name, + }, convertor: (dynamic data) => AppInfo.fromMap(data['data'])); + print(info.data); +} diff --git a/packages/app_update/.gitignore b/modules/basic_system/authentication/.gitignore similarity index 100% rename from packages/app_update/.gitignore rename to modules/basic_system/authentication/.gitignore diff --git a/packages/authentication/.metadata b/modules/basic_system/authentication/.metadata similarity index 100% rename from packages/authentication/.metadata rename to modules/basic_system/authentication/.metadata diff --git a/packages/app_update/CHANGELOG.md b/modules/basic_system/authentication/CHANGELOG.md similarity index 100% rename from packages/app_update/CHANGELOG.md rename to modules/basic_system/authentication/CHANGELOG.md diff --git a/packages/app_update/LICENSE b/modules/basic_system/authentication/LICENSE similarity index 100% rename from packages/app_update/LICENSE rename to modules/basic_system/authentication/LICENSE diff --git a/packages/app_update/README.md b/modules/basic_system/authentication/README.md similarity index 100% rename from packages/app_update/README.md rename to modules/basic_system/authentication/README.md diff --git a/packages/app_update/analysis_options.yaml b/modules/basic_system/authentication/analysis_options.yaml similarity index 100% rename from packages/app_update/analysis_options.yaml rename to modules/basic_system/authentication/analysis_options.yaml diff --git a/packages/authentication/lib/authentication.dart b/modules/basic_system/authentication/lib/authentication.dart similarity index 93% rename from packages/authentication/lib/authentication.dart rename to modules/basic_system/authentication/lib/authentication.dart index 3fc9d4797..0147e563a 100644 --- a/packages/authentication/lib/authentication.dart +++ b/modules/basic_system/authentication/lib/authentication.dart @@ -13,5 +13,6 @@ export 'blocs/user/state.dart'; export 'views/mobile/user/page_item.dart'; export 'views/mobile/user/unit_drawer_header.dart'; export 'views/mobile/user/user_page.dart'; +export 'views/mobile/user/support_me.dart'; export 'views/mobile/login/login_page.dart'; export 'views/mobile/register/register_page.dart'; \ No newline at end of file diff --git a/packages/authentication/lib/blocs/authentic/bloc.dart b/modules/basic_system/authentication/lib/blocs/authentic/bloc.dart similarity index 100% rename from packages/authentication/lib/blocs/authentic/bloc.dart rename to modules/basic_system/authentication/lib/blocs/authentic/bloc.dart diff --git a/packages/authentication/lib/blocs/authentic/event.dart b/modules/basic_system/authentication/lib/blocs/authentic/event.dart similarity index 100% rename from packages/authentication/lib/blocs/authentic/event.dart rename to modules/basic_system/authentication/lib/blocs/authentic/event.dart diff --git a/packages/authentication/lib/blocs/authentic/state.dart b/modules/basic_system/authentication/lib/blocs/authentic/state.dart similarity index 100% rename from packages/authentication/lib/blocs/authentic/state.dart rename to modules/basic_system/authentication/lib/blocs/authentic/state.dart diff --git a/packages/authentication/lib/blocs/register/bloc.dart b/modules/basic_system/authentication/lib/blocs/register/bloc.dart similarity index 100% rename from packages/authentication/lib/blocs/register/bloc.dart rename to modules/basic_system/authentication/lib/blocs/register/bloc.dart diff --git a/packages/authentication/lib/blocs/register/event.dart b/modules/basic_system/authentication/lib/blocs/register/event.dart similarity index 100% rename from packages/authentication/lib/blocs/register/event.dart rename to modules/basic_system/authentication/lib/blocs/register/event.dart diff --git a/packages/authentication/lib/blocs/register/state.dart b/modules/basic_system/authentication/lib/blocs/register/state.dart similarity index 100% rename from packages/authentication/lib/blocs/register/state.dart rename to modules/basic_system/authentication/lib/blocs/register/state.dart diff --git a/modules/basic_system/authentication/lib/blocs/user/bloc.dart b/modules/basic_system/authentication/lib/blocs/user/bloc.dart new file mode 100644 index 000000000..35f81586b --- /dev/null +++ b/modules/basic_system/authentication/lib/blocs/user/bloc.dart @@ -0,0 +1,7 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'state.dart'; + +class UserBloc extends Cubit { + UserBloc() : super(UserPerformance.fromJson({})); +} diff --git a/packages/authentication/lib/blocs/user/state.dart b/modules/basic_system/authentication/lib/blocs/user/state.dart similarity index 100% rename from packages/authentication/lib/blocs/user/state.dart rename to modules/basic_system/authentication/lib/blocs/user/state.dart diff --git a/packages/authentication/lib/models/user.dart b/modules/basic_system/authentication/lib/models/user.dart similarity index 100% rename from packages/authentication/lib/models/user.dart rename to modules/basic_system/authentication/lib/models/user.dart diff --git a/packages/authentication/lib/repository/auth_repository.dart b/modules/basic_system/authentication/lib/repository/auth_repository.dart similarity index 100% rename from packages/authentication/lib/repository/auth_repository.dart rename to modules/basic_system/authentication/lib/repository/auth_repository.dart diff --git a/packages/authentication/lib/repository/impl/http_auth_repository.dart b/modules/basic_system/authentication/lib/repository/impl/http_auth_repository.dart similarity index 100% rename from packages/authentication/lib/repository/impl/http_auth_repository.dart rename to modules/basic_system/authentication/lib/repository/impl/http_auth_repository.dart diff --git a/packages/authentication/lib/views/authentic_widget.dart b/modules/basic_system/authentication/lib/views/authentic_widget.dart similarity index 100% rename from packages/authentication/lib/views/authentic_widget.dart rename to modules/basic_system/authentication/lib/views/authentic_widget.dart diff --git a/packages/authentication/lib/views/mobile/login/login_form.dart b/modules/basic_system/authentication/lib/views/mobile/login/login_form.dart similarity index 98% rename from packages/authentication/lib/views/mobile/login/login_form.dart rename to modules/basic_system/authentication/lib/views/mobile/login/login_form.dart index cb562d708..372f8f4e4 100644 --- a/packages/authentication/lib/views/mobile/login/login_form.dart +++ b/modules/basic_system/authentication/lib/views/mobile/login/login_form.dart @@ -2,7 +2,7 @@ import 'package:app/app.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:components/toly_ui/toly_ui.dart'; +import 'package:toly_ui/toly_ui.dart'; import 'package:utils/utils.dart'; import '../../../authentication.dart'; @@ -67,7 +67,7 @@ class _LoginFromState extends State { ), GestureDetector( onTap: () { - Navigator.of(context).pushReplacementNamed(UnitRouter.register); + // Navigator.of(context).pushReplacementNamed(UnitRouter.register); }, child: Padding( padding: const EdgeInsets.symmetric(vertical: 8.0), diff --git a/packages/authentication/lib/views/mobile/login/login_page.dart b/modules/basic_system/authentication/lib/views/mobile/login/login_page.dart similarity index 100% rename from packages/authentication/lib/views/mobile/login/login_page.dart rename to modules/basic_system/authentication/lib/views/mobile/login/login_page.dart diff --git a/packages/authentication/lib/views/mobile/register/arc_clipper.dart b/modules/basic_system/authentication/lib/views/mobile/register/arc_clipper.dart similarity index 97% rename from packages/authentication/lib/views/mobile/register/arc_clipper.dart rename to modules/basic_system/authentication/lib/views/mobile/register/arc_clipper.dart index c6ee159ad..3f1a66898 100644 --- a/packages/authentication/lib/views/mobile/register/arc_clipper.dart +++ b/modules/basic_system/authentication/lib/views/mobile/register/arc_clipper.dart @@ -1,4 +1,4 @@ -import 'package:components/toly_ui/toly_ui.dart'; +import 'package:toly_ui/toly_ui.dart'; import 'package:flutter/material.dart'; diff --git a/packages/authentication/lib/views/mobile/register/register_page.dart b/modules/basic_system/authentication/lib/views/mobile/register/register_page.dart similarity index 99% rename from packages/authentication/lib/views/mobile/register/register_page.dart rename to modules/basic_system/authentication/lib/views/mobile/register/register_page.dart index a88e93aa5..a4210538e 100644 --- a/packages/authentication/lib/views/mobile/register/register_page.dart +++ b/modules/basic_system/authentication/lib/views/mobile/register/register_page.dart @@ -1,5 +1,5 @@ import 'package:authentication/authentication.dart'; -import 'package:components/toly_ui/toly_ui.dart'; +import 'package:toly_ui/toly_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:utils/utils.dart'; diff --git a/packages/authentication/lib/views/mobile/register/send_code.dart b/modules/basic_system/authentication/lib/views/mobile/register/send_code.dart similarity index 100% rename from packages/authentication/lib/views/mobile/register/send_code.dart rename to modules/basic_system/authentication/lib/views/mobile/register/send_code.dart diff --git a/modules/basic_system/authentication/lib/views/mobile/user/page_item.dart b/modules/basic_system/authentication/lib/views/mobile/user/page_item.dart new file mode 100644 index 000000000..c01c44496 --- /dev/null +++ b/modules/basic_system/authentication/lib/views/mobile/user/page_item.dart @@ -0,0 +1,89 @@ +import 'package:app/app.dart'; +import 'package:app_update/views/update_red_point.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:l10n/l10n.dart'; + +/// create by 张风捷特烈 on 2020-03-26 +/// contact me by email 1981462002@qq.com +/// 说明: + +class MePageItem extends StatelessWidget { + final Color color; + + const MePageItem({Key? key, this.color = Colors.white}) : super(key: key); + + @override + Widget build(BuildContext context) { + return _buildChild(context); + } + + Widget get divider { + return const Divider(); + } + + Widget _buildChild(BuildContext context) { + return ScrollConfiguration( + behavior: NoScrollBehavior(), + child: ListView( + padding: EdgeInsets.zero, + children: [ + const SizedBox(height: 10), + Gap.sfl10, + _buildItem(context, TolyIcon.icon_them, context.l10n.appSettings, + AppRoute.settings.url), + divider, + _buildItem(context, TolyIcon.icon_layout, context.l10n.dataManagement, + AppRoute.dataManage.url), + divider, + _buildItem( + context, + TolyIcon.icon_collect, + context.l10n.userCollection, + AppRoute.collection.url, + ), + Gap.sfl10, + Stack( + children: [ + _buildItem( + context, + Icons.update, + context.l10n.versionInformation, + AppRoute.version.url, + ), + const Positioned(left: 40, top: 10, child: UpdateRedPoint()) + ], + ), + divider, + _buildItem(context, Icons.info, context.l10n.aboutApplications, + AppRoute.aboutApp.url), + Gap.sfl10, + _buildItem(context, TolyIcon.icon_kafei, context.l10n.contactThisKing, + AppRoute.aboutMe.url), + divider, + _buildItem(context, Icons.sanitizer, context.l10n.homeAccountSupport, + AppRoute.supportMe.url), + ], + ), + ); + } + + Widget _buildItem( + BuildContext context, IconData icon, String title, String linkTo, + {VoidCallback? onTap}) => + ListTile( + leading: Icon( + icon, + color: Theme.of(context).primaryColor, + ), + title: Text(title, style: const TextStyle(fontSize: 16)), + trailing: + Icon(Icons.chevron_right, color: Theme.of(context).primaryColor), + onTap: () { + if (linkTo.isNotEmpty) { + context.push(linkTo); + if (onTap != null) onTap(); + } + }, + ); +} diff --git a/modules/basic_system/authentication/lib/views/mobile/user/support_me.dart b/modules/basic_system/authentication/lib/views/mobile/user/support_me.dart new file mode 100644 index 000000000..f6c8d095b --- /dev/null +++ b/modules/basic_system/authentication/lib/views/mobile/user/support_me.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; +import 'package:l10n/l10n.dart'; + +class SupportMe extends StatelessWidget { + const SupportMe({super.key}); + + @override + Widget build(BuildContext context) { + return DefaultTabController( + length: 3, + child: Scaffold( + appBar: AppBar( + title: Column( + children: [ + Text(context.l10n.homeAccountSupport), + Text('开源不易, 请我喝咖啡~',style: TextStyle(fontSize: 12,fontWeight: FontWeight.normal),), + ], + ), + bottom: TabBar( + tabs: [ + Tab( + text: '支付宝', + ), + Tab( + text: '微信1', + ), + Tab( + text: '微信2', + ), + ], + ), + ), + body: TabBarView( + children: [ + Image.asset( + 'assets/images/coffee_zfb.webp', + ), + Image.asset('assets/images/coffee_wx.webp'), + Image.asset('assets/images/coffee_wx_ac.webp'), + ], + ), + ), + ); + } +} + diff --git a/packages/authentication/lib/views/mobile/user/unit_drawer_header.dart b/modules/basic_system/authentication/lib/views/mobile/user/unit_drawer_header.dart similarity index 100% rename from packages/authentication/lib/views/mobile/user/unit_drawer_header.dart rename to modules/basic_system/authentication/lib/views/mobile/user/unit_drawer_header.dart diff --git a/packages/authentication/lib/views/mobile/user/user_account.dart b/modules/basic_system/authentication/lib/views/mobile/user/user_account.dart similarity index 91% rename from packages/authentication/lib/views/mobile/user/user_account.dart rename to modules/basic_system/authentication/lib/views/mobile/user/user_account.dart index 1ec021768..c60c09ddd 100644 --- a/packages/authentication/lib/views/mobile/user/user_account.dart +++ b/modules/basic_system/authentication/lib/views/mobile/user/user_account.dart @@ -3,7 +3,7 @@ import 'dart:ui'; import 'package:authentication/blocs/authentic/bloc.dart'; import 'package:authentication/blocs/user/bloc.dart'; -import 'package:components/components.dart'; +import 'package:toly_ui/toly_ui.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -32,7 +32,7 @@ class UserAccountPage extends StatelessWidget { backgroundColor: isDark ? null : bgColor, appBar: AppBar( backgroundColor: isDark ? null : sbgColor, - title: Text( + title: const Text( '账号资料', ), ), @@ -49,10 +49,10 @@ class UserAccountPage extends StatelessWidget { child: Row( children: [ Container( - padding: EdgeInsets.only(left: 15), + padding: const EdgeInsets.only(left: 15), width: 120, - child: Text('头像')), - Spacer(), + child: const Text('头像')), + const Spacer(), // AuthUserAvatar( // size: 50, // borderSize: 2, @@ -86,13 +86,13 @@ class UserAccountPage extends StatelessWidget { child: Row( children: [ Container( - padding: EdgeInsets.only(left: 15), + padding: const EdgeInsets.only(left: 15), width: 120, - child: Text('昵称')), - Spacer(), + child: const Text('昵称')), + const Spacer(), Text( performance.username ?? '', - style: TextStyle(color: Colors.grey), + style: const TextStyle(color: Colors.grey), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 12), @@ -112,16 +112,16 @@ class UserAccountPage extends StatelessWidget { child: Row( children: [ Container( - padding: EdgeInsets.only(left: 15), + padding: const EdgeInsets.only(left: 15), width: 120, child: const Text('箴言')), - Spacer(), - Text( + const Spacer(), + const Text( // '${performance.userId}', '海的彼岸,有我未曾见证的风采。', style: TextStyle(color: Colors.grey,fontSize: 12), ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 12), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 12), child: Icon( Icons.arrow_forward_ios_sharp, size: 20, @@ -200,16 +200,16 @@ class UserAccountPage extends StatelessWidget { child: Row( children: [ Container( - padding: EdgeInsets.only(left: 15), + padding: const EdgeInsets.only(left: 15), width: 120, child: const Text('账号')), - Spacer(), - Text( + const Spacer(), + const Text( // '${performance.userId}', '******', style: TextStyle(color: Colors.grey,fontSize: 12), ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 12), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 12), child: Icon( Icons.arrow_forward_ios_sharp, size: 20, @@ -266,8 +266,8 @@ class UserAccountPage extends StatelessWidget { // ), // if(false) ListTile( - title: Center( - child: const Text( + title: const Center( + child: Text( '删除账号', style: TextStyle( fontSize: 15, @@ -290,7 +290,7 @@ class UserAccountPage extends StatelessWidget { // await Future.delayed(Duration(seconds: 3)); // await context.read().repo.unregister(); // - context.read().add(Logout()); + context.read().add(const Logout()); // Navigator.of(context).pushAndRemoveUntil( // NoAnimRouter(AuthRelation( @@ -394,14 +394,14 @@ class UserItemPanel extends StatelessWidget { children: [ Expanded( child: Container( - padding: EdgeInsets.only(left: 15), child: Text(label)), + padding: const EdgeInsets.only(left: 15), child: Text(label)), ), Text( value, - style: TextStyle(color: Colors.grey), + style: const TextStyle(color: Colors.grey), ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 12), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 12), child: Icon( Icons.arrow_forward_ios_sharp, size: 20, diff --git a/packages/authentication/lib/views/mobile/user/user_page.dart b/modules/basic_system/authentication/lib/views/mobile/user/user_page.dart similarity index 98% rename from packages/authentication/lib/views/mobile/user/user_page.dart rename to modules/basic_system/authentication/lib/views/mobile/user/user_page.dart index 66fde9230..6cbe82fc2 100644 --- a/packages/authentication/lib/views/mobile/user/user_page.dart +++ b/modules/basic_system/authentication/lib/views/mobile/user/user_page.dart @@ -5,7 +5,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:components/toly_ui/toly_ui.dart'; +import 'package:toly_ui/toly_ui.dart'; import '../../../authentication.dart'; diff --git a/modules/basic_system/authentication/pubspec.yaml b/modules/basic_system/authentication/pubspec.yaml new file mode 100644 index 000000000..258aaa06a --- /dev/null +++ b/modules/basic_system/authentication/pubspec.yaml @@ -0,0 +1,66 @@ +name: authentication +description: A new Flutter package project. +version: 0.0.1 +homepage: + +environment: + sdk: ">=3.5.0 <4.0.0" + flutter: ">=1.17.0" +resolution: workspace +dependencies: + flutter: + sdk: flutter + flutter_bloc: ^8.1.6 # 状态管理 + equatable: ^2.0.5 # 相等辅助 + shared_preferences: ^2.2.1 # xml 固化 + go_router: ^14.2.0 + utils: + path: ../utils + app_update: + path: ../app_update + app: + path: ../app + l10n: + path: ../l10n + components: + path: ../components + toly_ui: + path: ../toly_ui + + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # To add assets to your package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # To add custom fonts to your package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/authentication/test/authentication_test.dart b/modules/basic_system/authentication/test/authentication_test.dart similarity index 100% rename from packages/authentication/test/authentication_test.dart rename to modules/basic_system/authentication/test/authentication_test.dart diff --git a/packages/artifact/.gitignore b/modules/basic_system/components/.gitignore similarity index 100% rename from packages/artifact/.gitignore rename to modules/basic_system/components/.gitignore diff --git a/packages/components/.metadata b/modules/basic_system/components/.metadata similarity index 100% rename from packages/components/.metadata rename to modules/basic_system/components/.metadata diff --git a/packages/artifact/CHANGELOG.md b/modules/basic_system/components/CHANGELOG.md similarity index 100% rename from packages/artifact/CHANGELOG.md rename to modules/basic_system/components/CHANGELOG.md diff --git a/packages/artifact/LICENSE b/modules/basic_system/components/LICENSE similarity index 100% rename from packages/artifact/LICENSE rename to modules/basic_system/components/LICENSE diff --git a/packages/artifact/README.md b/modules/basic_system/components/README.md similarity index 100% rename from packages/artifact/README.md rename to modules/basic_system/components/README.md diff --git a/packages/artifact/analysis_options.yaml b/modules/basic_system/components/analysis_options.yaml similarity index 100% rename from packages/artifact/analysis_options.yaml rename to modules/basic_system/components/analysis_options.yaml diff --git a/modules/basic_system/components/lib/components.dart b/modules/basic_system/components/lib/components.dart new file mode 100644 index 000000000..6cf8fda4f --- /dev/null +++ b/modules/basic_system/components/lib/components.dart @@ -0,0 +1,4 @@ +library components; + +export 'flutter_ui/flutter_ui.dart'; +export 'project_ui/project_ui.dart'; \ No newline at end of file diff --git a/packages/components/lib/flutter_ui/diy_flexible_space_bar.dart b/modules/basic_system/components/lib/flutter_ui/diy_flexible_space_bar.dart similarity index 99% rename from packages/components/lib/flutter_ui/diy_flexible_space_bar.dart rename to modules/basic_system/components/lib/flutter_ui/diy_flexible_space_bar.dart index 3dcd2854f..34075ef01 100644 --- a/packages/components/lib/flutter_ui/diy_flexible_space_bar.dart +++ b/modules/basic_system/components/lib/flutter_ui/diy_flexible_space_bar.dart @@ -270,7 +270,7 @@ class _DiyFlexibleSpaceBarState extends State { final double opacity = settings.toolbarOpacity; if (opacity > 0.0) { - TextStyle titleStyle = theme.primaryTextTheme.headline6!; + TextStyle titleStyle = theme.primaryTextTheme.headlineSmall!; titleStyle = titleStyle.copyWith( color: titleStyle.color!.withOpacity(opacity), ); diff --git a/packages/components/lib/flutter_ui/flutter_ui.dart b/modules/basic_system/components/lib/flutter_ui/flutter_ui.dart similarity index 100% rename from packages/components/lib/flutter_ui/flutter_ui.dart rename to modules/basic_system/components/lib/flutter_ui/flutter_ui.dart diff --git a/packages/components/lib/flutter_ui/no_div_expansion_tile.dart b/modules/basic_system/components/lib/flutter_ui/no_div_expansion_tile.dart similarity index 100% rename from packages/components/lib/flutter_ui/no_div_expansion_tile.dart rename to modules/basic_system/components/lib/flutter_ui/no_div_expansion_tile.dart diff --git a/modules/basic_system/components/lib/flutter_ui/toly_date_picker.dart b/modules/basic_system/components/lib/flutter_ui/toly_date_picker.dart new file mode 100644 index 000000000..495884cbc --- /dev/null +++ b/modules/basic_system/components/lib/flutter_ui/toly_date_picker.dart @@ -0,0 +1,3041 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:math' as math; + +import 'package:flutter/gestures.dart' show DragStartBehavior; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; + +const Size _calendarPortraitDialogSize = Size(330.0, 518.0); +const Size _calendarLandscapeDialogSize = Size(496.0, 346.0); +const Size _inputPortraitDialogSize = Size(330.0, 270.0); +const Size _inputLandscapeDialogSize = Size(496, 160.0); +const Size _inputRangeLandscapeDialogSize = Size(496, 164.0); +const Duration _dialogSizeAnimationDuration = Duration(milliseconds: 200); +const double _inputFormPortraitHeight = 98.0; +const double _inputFormLandscapeHeight = 108.0; + +/// Shows a dialog containing a Material Design date picker. +/// +/// The returned [Future] resolves to the date selected by the user when the +/// user confirms the dialog. If the user cancels the dialog, null is returned. +/// +/// When the date picker is first displayed, it will show the month of +/// [initialDate], with [initialDate] selected. +/// +/// The [firstDate] is the earliest allowable date. The [lastDate] is the latest +/// allowable date. [initialDate] must either fall between these dates, +/// or be equal to one of them. For each of these [DateTime] parameters, only +/// their dates are considered. Their time fields are ignored. They must all +/// be non-null. +/// +/// The [currentDate] represents the current day (i.e. today). This +/// date will be highlighted in the day grid. If null, the date of +/// `DateTime.now()` will be used. +/// +/// An optional [initialEntryMode] argument can be used to display the date +/// picker in the [DatePickerEntryMode.calendar] (a calendar month grid) +/// or [DatePickerEntryMode.input] (a text input field) mode. +/// It defaults to [DatePickerEntryMode.calendar] and must be non-null. +/// +/// An optional [selectableDayPredicate] function can be passed in to only allow +/// certain days for selection. If provided, only the days that +/// [selectableDayPredicate] returns true for will be selectable. For example, +/// this can be used to only allow weekdays for selection. If provided, it must +/// return true for [initialDate]. +/// +/// The following optional string parameters allow you to override the default +/// text used for various parts of the dialog: +/// +/// * [helpText], label displayed at the top of the dialog. +/// * [cancelText], label on the cancel button. +/// * [confirmText], label on the ok button. +/// * [errorFormatText], message used when the input text isn't in a proper date format. +/// * [errorInvalidText], message used when the input text isn't a selectable date. +/// * [fieldHintText], text used to prompt the user when no text has been entered in the field. +/// * [fieldLabelText], label for the date text input field. +/// +/// An optional [locale] argument can be used to set the locale for the date +/// picker. It defaults to the ambient locale provided by [Localizations]. +/// +/// An optional [textDirection] argument can be used to set the text direction +/// ([TextDirection.ltr] or [TextDirection.rtl]) for the date picker. It +/// defaults to the ambient text direction provided by [Directionality]. If both +/// [locale] and [textDirection] are non-null, [textDirection] overrides the +/// direction chosen for the [locale]. +/// +/// The [context], [useRootNavigator] and [routeSettings] arguments are passed to +/// [showDialog], the documentation for which discusses how it is used. [context] +/// and [useRootNavigator] must be non-null. +/// +/// The [builder] parameter can be used to wrap the dialog widget +/// to add inherited widgets like [Theme]. +/// +/// An optional [initialDatePickerMode] argument can be used to have the +/// calendar date picker initially appear in the [DatePickerMode.year] or +/// [DatePickerMode.day] mode. It defaults to [DatePickerMode.day], and +/// must be non-null. +/// +/// {@macro flutter.widgets.RawDialogRoute} +/// +/// ### State Restoration +/// +/// Using this method will not enable state restoration for the date picker. +/// In order to enable state restoration for a date picker, use +/// [Navigator.restorablePush] or [Navigator.restorablePushNamed] with +/// [DatePickerDialog]. +/// +/// For more information about state restoration, see [RestorationManager]. +/// +/// {@macro flutter.widgets.RestorationManager} +/// +/// {@tool dartpad} +/// This sample demonstrates how to create a restorable Material date picker. +/// This is accomplished by enabling state restoration by specifying +/// [MaterialApp.restorationScopeId] and using [Navigator.restorablePush] to +/// push [DatePickerDialog] when the button is tapped. +/// +/// ** See code in examples/api/lib/material/date_picker/show_date_picker.0.dart ** +/// {@end-tool} +/// +/// See also: +/// +/// * [showDateRangePicker], which shows a Material Design date range picker +/// used to select a range of dates. +/// * [CalendarDatePicker], which provides the calendar grid used by the date picker dialog. +/// * [InputDatePickerFormField], which provides a text input field for entering dates. +/// * [DisplayFeatureSubScreen], which documents the specifics of how +/// [DisplayFeature]s can split the screen into sub-screens. +/// * [showTimePicker], which shows a dialog that contains a Material Design time picker. +/// +Future showDatePicker({ + required BuildContext context, + required DateTime initialDate, + required DateTime firstDate, + required DateTime lastDate, + DateTime? currentDate, + DatePickerEntryMode initialEntryMode = DatePickerEntryMode.calendar, + SelectableDayPredicate? selectableDayPredicate, + String? helpText, + String? cancelText, + String? confirmText, + Locale? locale, + bool useRootNavigator = true, + RouteSettings? routeSettings, + TextDirection? textDirection, + TransitionBuilder? builder, + DatePickerMode initialDatePickerMode = DatePickerMode.day, + String? errorFormatText, + String? errorInvalidText, + String? fieldHintText, + String? fieldLabelText, + TextInputType? keyboardType, + Offset? anchorPoint, +}) async { + assert(context != null); + assert(initialDate != null); + assert(firstDate != null); + assert(lastDate != null); + initialDate = DateUtils.dateOnly(initialDate); + firstDate = DateUtils.dateOnly(firstDate); + lastDate = DateUtils.dateOnly(lastDate); + assert( + !lastDate.isBefore(firstDate), + 'lastDate $lastDate must be on or after firstDate $firstDate.', + ); + assert( + !initialDate.isBefore(firstDate), + 'initialDate $initialDate must be on or after firstDate $firstDate.', + ); + assert( + !initialDate.isAfter(lastDate), + 'initialDate $initialDate must be on or before lastDate $lastDate.', + ); + assert( + selectableDayPredicate == null || selectableDayPredicate(initialDate), + 'Provided initialDate $initialDate must satisfy provided selectableDayPredicate.', + ); + assert(initialEntryMode != null); + assert(useRootNavigator != null); + assert(initialDatePickerMode != null); + assert(debugCheckHasMaterialLocalizations(context)); + + Widget dialog = DatePickerDialog( + initialDate: initialDate, + firstDate: firstDate, + lastDate: lastDate, + currentDate: currentDate, + initialEntryMode: initialEntryMode, + selectableDayPredicate: selectableDayPredicate, + helpText: helpText, + cancelText: cancelText, + confirmText: confirmText, + initialCalendarMode: initialDatePickerMode, + errorFormatText: errorFormatText, + errorInvalidText: errorInvalidText, + fieldHintText: fieldHintText, + fieldLabelText: fieldLabelText, + keyboardType: keyboardType, + ); + + if (textDirection != null) { + dialog = Directionality( + textDirection: textDirection, + child: dialog, + ); + } + + if (locale != null) { + dialog = Localizations.override( + context: context, + locale: locale, + child: dialog, + ); + } + + return showDialog( + context: context, + useRootNavigator: useRootNavigator, + routeSettings: routeSettings, + builder: (BuildContext context) { + return builder == null ? dialog : builder(context, dialog); + }, + anchorPoint: anchorPoint, + ); +} + +/// A Material-style date picker dialog. +/// +/// It is used internally by [showDatePicker] or can be directly pushed +/// onto the [Navigator] stack to enable state restoration. See +/// [showDatePicker] for a state restoration app example. +/// +/// See also: +/// +/// * [showDatePicker], which is a way to display the date picker. +class DatePickerDialog extends StatefulWidget { + /// A Material-style date picker dialog. + DatePickerDialog({ + super.key, + required DateTime initialDate, + required DateTime firstDate, + required DateTime lastDate, + DateTime? currentDate, + this.initialEntryMode = DatePickerEntryMode.calendar, + this.selectableDayPredicate, + this.cancelText, + this.confirmText, + this.helpText, + this.initialCalendarMode = DatePickerMode.day, + this.errorFormatText, + this.errorInvalidText, + this.fieldHintText, + this.fieldLabelText, + this.keyboardType, + this.restorationId, + }) : assert(initialDate != null), + assert(firstDate != null), + assert(lastDate != null), + initialDate = DateUtils.dateOnly(initialDate), + firstDate = DateUtils.dateOnly(firstDate), + lastDate = DateUtils.dateOnly(lastDate), + currentDate = DateUtils.dateOnly(currentDate ?? DateTime.now()), + assert(initialEntryMode != null), + assert(initialCalendarMode != null) { + assert( + !this.lastDate.isBefore(this.firstDate), + 'lastDate ${this.lastDate} must be on or after firstDate ${this.firstDate}.', + ); + assert( + !this.initialDate.isBefore(this.firstDate), + 'initialDate ${this.initialDate} must be on or after firstDate ${this.firstDate}.', + ); + assert( + !this.initialDate.isAfter(this.lastDate), + 'initialDate ${this.initialDate} must be on or before lastDate ${this.lastDate}.', + ); + assert( + selectableDayPredicate == null || + selectableDayPredicate!(this.initialDate), + 'Provided initialDate ${this.initialDate} must satisfy provided selectableDayPredicate', + ); + } + + /// The initially selected [DateTime] that the picker should display. + final DateTime initialDate; + + /// The earliest allowable [DateTime] that the user can select. + final DateTime firstDate; + + /// The latest allowable [DateTime] that the user can select. + final DateTime lastDate; + + /// The [DateTime] representing today. It will be highlighted in the day grid. + final DateTime currentDate; + + /// The initial mode of date entry method for the date picker dialog. + /// + /// See [DatePickerEntryMode] for more details on the different data entry + /// modes available. + final DatePickerEntryMode initialEntryMode; + + /// Function to provide full control over which [DateTime] can be selected. + final SelectableDayPredicate? selectableDayPredicate; + + /// The text that is displayed on the cancel button. + final String? cancelText; + + /// The text that is displayed on the confirm button. + final String? confirmText; + + /// The text that is displayed at the top of the header. + /// + /// This is used to indicate to the user what they are selecting a date for. + final String? helpText; + + /// The initial display of the calendar picker. + final DatePickerMode initialCalendarMode; + + /// The error text displayed if the entered date is not in the correct format. + final String? errorFormatText; + + /// The error text displayed if the date is not valid. + /// + /// A date is not valid if it is earlier than [firstDate], later than + /// [lastDate], or doesn't pass the [selectableDayPredicate]. + final String? errorInvalidText; + + /// The hint text displayed in the [TextField]. + /// + /// If this is null, it will default to the date format string. For example, + /// 'mm/dd/yyyy' for en_US. + final String? fieldHintText; + + /// The label text displayed in the [TextField]. + /// + /// If this is null, it will default to the words representing the date format + /// string. For example, 'Month, Day, Year' for en_US. + final String? fieldLabelText; + + /// The keyboard type of the [TextField]. + /// + /// If this is null, it will default to [TextInputType.datetime] + final TextInputType? keyboardType; + + /// Restoration ID to save and restore the state of the [DatePickerDialog]. + /// + /// If it is non-null, the date picker will persist and restore the + /// date selected on the dialog. + /// + /// The state of this widget is persisted in a [RestorationBucket] claimed + /// from the surrounding [RestorationScope] using the provided restoration ID. + /// + /// See also: + /// + /// * [RestorationManager], which explains how state restoration works in + /// Flutter. + final String? restorationId; + + @override + State createState() => _DatePickerDialogState(); +} + +class _DatePickerDialogState extends State + with RestorationMixin { + late final RestorableDateTime _selectedDate = + RestorableDateTime(widget.initialDate); + late final _RestorableDatePickerEntryMode _entryMode = + _RestorableDatePickerEntryMode(widget.initialEntryMode); + final _RestorableAutovalidateMode _autovalidateMode = + _RestorableAutovalidateMode(AutovalidateMode.disabled); + + @override + String? get restorationId => widget.restorationId; + + @override + void restoreState(RestorationBucket? oldBucket, bool initialRestore) { + registerForRestoration(_selectedDate, 'selected_date'); + registerForRestoration(_autovalidateMode, 'autovalidateMode'); + registerForRestoration(_entryMode, 'calendar_entry_mode'); + } + + final GlobalKey _calendarPickerKey = GlobalKey(); + final GlobalKey _formKey = GlobalKey(); + + void _handleOk() { + if (_entryMode.value == DatePickerEntryMode.input || + _entryMode.value == DatePickerEntryMode.inputOnly) { + final FormState form = _formKey.currentState!; + if (!form.validate()) { + setState(() => _autovalidateMode.value = AutovalidateMode.always); + return; + } + form.save(); + } + Navigator.pop(context, _selectedDate.value); + } + + void _handleCancel() { + Navigator.pop(context); + } + + void _handleEntryModeToggle() { + setState(() { + switch (_entryMode.value) { + case DatePickerEntryMode.calendar: + _autovalidateMode.value = AutovalidateMode.disabled; + _entryMode.value = DatePickerEntryMode.input; + break; + case DatePickerEntryMode.input: + _formKey.currentState!.save(); + _entryMode.value = DatePickerEntryMode.calendar; + break; + case DatePickerEntryMode.calendarOnly: + case DatePickerEntryMode.inputOnly: + assert(false, 'Can not change entry mode from _entryMode'); + break; + } + }); + } + + void _handleDateChanged(DateTime date) { + setState(() { + _selectedDate.value = date; + }); + } + + Size _dialogSize(BuildContext context) { + final Orientation orientation = MediaQuery.of(context).orientation; + switch (_entryMode.value) { + case DatePickerEntryMode.calendar: + case DatePickerEntryMode.calendarOnly: + switch (orientation) { + case Orientation.portrait: + return _calendarPortraitDialogSize; + case Orientation.landscape: + return _calendarLandscapeDialogSize; + } + case DatePickerEntryMode.input: + case DatePickerEntryMode.inputOnly: + switch (orientation) { + case Orientation.portrait: + return _inputPortraitDialogSize; + case Orientation.landscape: + return _inputLandscapeDialogSize; + } + } + } + + static const Map _formShortcutMap = + { + // Pressing enter on the field will move focus to the next field or control. + SingleActivator(LogicalKeyboardKey.enter): NextFocusIntent(), + }; + + @override + Widget build(BuildContext context) { + final ThemeData theme = Theme.of(context); + final ColorScheme colorScheme = theme.colorScheme; + final MaterialLocalizations localizations = + MaterialLocalizations.of(context); + final Orientation orientation = MediaQuery.of(context).orientation; + final TextTheme textTheme = theme.textTheme; + // Constrain the textScaleFactor to the largest supported value to prevent + // layout issues. + final double textScaleFactor = + math.min(MediaQuery.of(context).textScaleFactor, 1.3); + + final String dateText = localizations.formatMediumDate(_selectedDate.value); + final Color onPrimarySurface = colorScheme.brightness == Brightness.light + ? colorScheme.onPrimary + : colorScheme.onSurface; + final TextStyle? dateStyle = orientation == Orientation.landscape + ? textTheme.headlineMedium?.copyWith(color: onPrimarySurface) + : textTheme.headlineMedium?.copyWith(color: onPrimarySurface); + + final Widget actions = Container( + alignment: AlignmentDirectional.centerEnd, + constraints: const BoxConstraints(minHeight: 52.0), + padding: const EdgeInsets.symmetric(horizontal: 8), + child: OverflowBar( + spacing: 8, + children: [ + TextButton( + onPressed: _handleCancel, + child: Text(widget.cancelText ?? localizations.cancelButtonLabel), + ), + TextButton( + onPressed: _handleOk, + child: Text(widget.confirmText ?? localizations.okButtonLabel), + ), + ], + ), + ); + + CalendarDatePicker calendarDatePicker() { + return CalendarDatePicker( + key: _calendarPickerKey, + initialDate: _selectedDate.value, + firstDate: widget.firstDate, + lastDate: widget.lastDate, + currentDate: widget.currentDate, + onDateChanged: _handleDateChanged, + selectableDayPredicate: widget.selectableDayPredicate, + initialCalendarMode: widget.initialCalendarMode, + ); + } + + Form inputDatePicker() { + return Form( + key: _formKey, + autovalidateMode: _autovalidateMode.value, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 24), + height: orientation == Orientation.portrait + ? _inputFormPortraitHeight + : _inputFormLandscapeHeight, + child: Shortcuts( + shortcuts: _formShortcutMap, + child: Column( + children: [ + const Spacer(), + InputDatePickerFormField( + initialDate: _selectedDate.value, + firstDate: widget.firstDate, + lastDate: widget.lastDate, + onDateSubmitted: _handleDateChanged, + onDateSaved: _handleDateChanged, + selectableDayPredicate: widget.selectableDayPredicate, + errorFormatText: widget.errorFormatText, + errorInvalidText: widget.errorInvalidText, + fieldHintText: widget.fieldHintText, + fieldLabelText: widget.fieldLabelText, + keyboardType: widget.keyboardType, + autofocus: true, + ), + const Spacer(), + ], + ), + ), + ), + ); + } + + final Widget picker; + final Widget? entryModeButton; + switch (_entryMode.value) { + case DatePickerEntryMode.calendar: + picker = calendarDatePicker(); + entryModeButton = IconButton( + icon: const Icon(Icons.edit), + color: onPrimarySurface, + tooltip: localizations.inputDateModeButtonLabel, + onPressed: _handleEntryModeToggle, + ); + break; + + case DatePickerEntryMode.calendarOnly: + picker = calendarDatePicker(); + entryModeButton = null; + break; + + case DatePickerEntryMode.input: + picker = inputDatePicker(); + entryModeButton = IconButton( + icon: const Icon(Icons.calendar_today), + color: onPrimarySurface, + tooltip: localizations.calendarModeButtonLabel, + onPressed: _handleEntryModeToggle, + ); + break; + + case DatePickerEntryMode.inputOnly: + picker = inputDatePicker(); + entryModeButton = null; + break; + } + + final Widget header = _DatePickerHeader( + helpText: widget.helpText ?? localizations.datePickerHelpText, + titleText: dateText, + titleStyle: dateStyle, + orientation: orientation, + isShort: orientation == Orientation.landscape, + entryModeButton: entryModeButton, + ); + + final Size dialogSize = _dialogSize(context) * textScaleFactor; + return Dialog( + insetPadding: + const EdgeInsets.symmetric(horizontal: 16.0, vertical: 24.0), + clipBehavior: Clip.antiAlias, + child: AnimatedContainer( + width: dialogSize.width, + height: dialogSize.height, + duration: _dialogSizeAnimationDuration, + curve: Curves.easeIn, + child: MediaQuery( + data: MediaQuery.of(context).copyWith( + textScaleFactor: textScaleFactor, + ), + child: Builder(builder: (BuildContext context) { + switch (orientation) { + case Orientation.portrait: + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + header, + Expanded(child: picker), + actions, + ], + ); + case Orientation.landscape: + return Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + header, + Flexible( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Expanded(child: picker), + actions, + ], + ), + ), + ], + ); + } + }), + ), + ), + ); + } +} + +// A restorable [DatePickerEntryMode] value. +// +// This serializes each entry as a unique `int` value. +class _RestorableDatePickerEntryMode + extends RestorableValue { + _RestorableDatePickerEntryMode( + DatePickerEntryMode defaultValue, + ) : _defaultValue = defaultValue; + + final DatePickerEntryMode _defaultValue; + + @override + DatePickerEntryMode createDefaultValue() => _defaultValue; + + @override + void didUpdateValue(DatePickerEntryMode? oldValue) { + assert(debugIsSerializableForRestoration(value.index)); + notifyListeners(); + } + + @override + DatePickerEntryMode fromPrimitives(Object? data) => + DatePickerEntryMode.values[data! as int]; + + @override + Object? toPrimitives() => value.index; +} + +// A restorable [AutovalidateMode] value. +// +// This serializes each entry as a unique `int` value. +class _RestorableAutovalidateMode extends RestorableValue { + _RestorableAutovalidateMode( + AutovalidateMode defaultValue, + ) : _defaultValue = defaultValue; + + final AutovalidateMode _defaultValue; + + @override + AutovalidateMode createDefaultValue() => _defaultValue; + + @override + void didUpdateValue(AutovalidateMode? oldValue) { + assert(debugIsSerializableForRestoration(value.index)); + notifyListeners(); + } + + @override + AutovalidateMode fromPrimitives(Object? data) => + AutovalidateMode.values[data! as int]; + + @override + Object? toPrimitives() => value.index; +} + +/// Re-usable widget that displays the selected date (in large font) and the +/// help text above it. +/// +/// These types include: +/// +/// * Single Date picker with calendar mode. +/// * Single Date picker with text input mode. +/// * Date Range picker with text input mode. +/// +/// [helpText], [orientation], [icon], [onIconPressed] are required and must be +/// non-null. +class _DatePickerHeader extends StatelessWidget { + /// Creates a header for use in a date picker dialog. + const _DatePickerHeader({ + required this.helpText, + required this.titleText, + this.titleSemanticsLabel, + required this.titleStyle, + required this.orientation, + this.isShort = false, + this.entryModeButton, + }) : assert(helpText != null), + assert(orientation != null), + assert(isShort != null); + + static const double _datePickerHeaderLandscapeWidth = 152.0; + static const double _datePickerHeaderPortraitHeight = 120.0; + static const double _headerPaddingLandscape = 16.0; + + /// The text that is displayed at the top of the header. + /// + /// This is used to indicate to the user what they are selecting a date for. + final String helpText; + + /// The text that is displayed at the center of the header. + final String titleText; + + /// The semantic label associated with the [titleText]. + final String? titleSemanticsLabel; + + /// The [TextStyle] that the title text is displayed with. + final TextStyle? titleStyle; + + /// The orientation is used to decide how to layout its children. + final Orientation orientation; + + /// Indicates the header is being displayed in a shorter/narrower context. + /// + /// This will be used to tighten up the space between the help text and date + /// text if `true`. Additionally, it will use a smaller typography style if + /// `true`. + /// + /// This is necessary for displaying the manual input mode in + /// landscape orientation, in order to account for the keyboard height. + final bool isShort; + + final Widget? entryModeButton; + + @override + Widget build(BuildContext context) { + final ThemeData theme = Theme.of(context); + final ColorScheme colorScheme = theme.colorScheme; + final TextTheme textTheme = theme.textTheme; + + // The header should use the primary color in light themes and surface color in dark + final bool isDark = colorScheme.brightness == Brightness.dark; + final Color primarySurfaceColor = + isDark ? colorScheme.surface : colorScheme.primary; + final Color onPrimarySurfaceColor = + isDark ? colorScheme.onSurface : colorScheme.onPrimary; + + final TextStyle? helpStyle = textTheme.headlineMedium?.copyWith( + color: onPrimarySurfaceColor, + ); + + final Text help = Text( + helpText, + style: helpStyle, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ); + final Text title = Text( + titleText, + semanticsLabel: titleSemanticsLabel ?? titleText, + style: titleStyle, + maxLines: orientation == Orientation.portrait ? 1 : 2, + overflow: TextOverflow.ellipsis, + ); + + switch (orientation) { + case Orientation.portrait: + return SizedBox( + height: _datePickerHeaderPortraitHeight, + child: Material( + color: primarySurfaceColor, + child: Padding( + padding: const EdgeInsetsDirectional.only( + start: 24, + end: 12, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 16), + help, + const Flexible(child: SizedBox(height: 38)), + Row( + children: [ + Expanded(child: title), + if (entryModeButton != null) entryModeButton!, + ], + ), + ], + ), + ), + ), + ); + case Orientation.landscape: + return SizedBox( + width: _datePickerHeaderLandscapeWidth, + child: Material( + color: primarySurfaceColor, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 16), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: _headerPaddingLandscape, + ), + child: help, + ), + SizedBox(height: isShort ? 16 : 56), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: _headerPaddingLandscape, + ), + child: title, + ), + ), + if (entryModeButton != null) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: entryModeButton, + ), + ], + ), + ), + ); + } + } +} + +/// Shows a full screen modal dialog containing a Material Design date range +/// picker. +/// +/// The returned [Future] resolves to the [DateTimeRange] selected by the user +/// when the user saves their selection. If the user cancels the dialog, null is +/// returned. +/// +/// If [initialDateRange] is non-null, then it will be used as the initially +/// selected date range. If it is provided, `initialDateRange.start` must be +/// before or on `initialDateRange.end`. +/// +/// The [firstDate] is the earliest allowable date. The [lastDate] is the latest +/// allowable date. Both must be non-null. +/// +/// If an initial date range is provided, `initialDateRange.start` +/// and `initialDateRange.end` must both fall between or on [firstDate] and +/// [lastDate]. For all of these [DateTime] values, only their dates are +/// considered. Their time fields are ignored. +/// +/// The [currentDate] represents the current day (i.e. today). This +/// date will be highlighted in the day grid. If null, the date of +/// `DateTime.now()` will be used. +/// +/// An optional [initialEntryMode] argument can be used to display the date +/// picker in the [DatePickerEntryMode.calendar] (a scrollable calendar month +/// grid) or [DatePickerEntryMode.input] (two text input fields) mode. +/// It defaults to [DatePickerEntryMode.calendar] and must be non-null. +/// +/// The following optional string parameters allow you to override the default +/// text used for various parts of the dialog: +/// +/// * [helpText], the label displayed at the top of the dialog. +/// * [cancelText], the label on the cancel button for the text input mode. +/// * [confirmText],the label on the ok button for the text input mode. +/// * [saveText], the label on the save button for the fullscreen calendar +/// mode. +/// * [errorFormatText], the message used when an input text isn't in a proper +/// date format. +/// * [errorInvalidText], the message used when an input text isn't a +/// selectable date. +/// * [errorInvalidRangeText], the message used when the date range is +/// invalid (e.g. start date is after end date). +/// * [fieldStartHintText], the text used to prompt the user when no text has +/// been entered in the start field. +/// * [fieldEndHintText], the text used to prompt the user when no text has +/// been entered in the end field. +/// * [fieldStartLabelText], the label for the start date text input field. +/// * [fieldEndLabelText], the label for the end date text input field. +/// +/// An optional [locale] argument can be used to set the locale for the date +/// picker. It defaults to the ambient locale provided by [Localizations]. +/// +/// An optional [textDirection] argument can be used to set the text direction +/// ([TextDirection.ltr] or [TextDirection.rtl]) for the date picker. It +/// defaults to the ambient text direction provided by [Directionality]. If both +/// [locale] and [textDirection] are non-null, [textDirection] overrides the +/// direction chosen for the [locale]. +/// +/// The [context], [useRootNavigator] and [routeSettings] arguments are passed +/// to [showDialog], the documentation for which discusses how it is used. +/// [context] and [useRootNavigator] must be non-null. +/// +/// The [builder] parameter can be used to wrap the dialog widget +/// to add inherited widgets like [Theme]. +/// +/// {@macro flutter.widgets.RawDialogRoute} +/// +/// ### State Restoration +/// +/// Using this method will not enable state restoration for the date range picker. +/// In order to enable state restoration for a date range picker, use +/// [Navigator.restorablePush] or [Navigator.restorablePushNamed] with +/// [DateRangePickerDialog]. +/// +/// For more information about state restoration, see [RestorationManager]. +/// +/// {@macro flutter.widgets.RestorationManager} +/// +/// {@tool sample} +/// This sample demonstrates how to create a restorable Material date range picker. +/// This is accomplished by enabling state restoration by specifying +/// [MaterialApp.restorationScopeId] and using [Navigator.restorablePush] to +/// push [DateRangePickerDialog] when the button is tapped. +/// +/// ** See code in examples/api/lib/material/date_picker/show_date_range_picker.0.dart ** +/// {@end-tool} +/// +/// See also: +/// +/// * [showDatePicker], which shows a Material Design date picker used to +/// select a single date. +/// * [DateTimeRange], which is used to describe a date range. +/// * [DisplayFeatureSubScreen], which documents the specifics of how +/// [DisplayFeature]s can split the screen into sub-screens. +Future showDateRangePicker({ + required BuildContext context, + DateTimeRange? initialDateRange, + required DateTime firstDate, + required DateTime lastDate, + DateTime? currentDate, + DatePickerEntryMode initialEntryMode = DatePickerEntryMode.calendar, + String? helpText, + String? cancelText, + String? confirmText, + String? saveText, + String? errorFormatText, + String? errorInvalidText, + String? errorInvalidRangeText, + String? fieldStartHintText, + String? fieldEndHintText, + String? fieldStartLabelText, + String? fieldEndLabelText, + Locale? locale, + bool useRootNavigator = true, + RouteSettings? routeSettings, + TextDirection? textDirection, + TransitionBuilder? builder, + Offset? anchorPoint, +}) async { + assert(context != null); + assert( + initialDateRange == null || + (initialDateRange.start != null && initialDateRange.end != null), + 'initialDateRange must be null or have non-null start and end dates.', + ); + assert( + initialDateRange == null || + !initialDateRange.start.isAfter(initialDateRange.end), + "initialDateRange's start date must not be after it's end date.", + ); + initialDateRange = + initialDateRange == null ? null : DateUtils.datesOnly(initialDateRange); + assert(firstDate != null); + firstDate = DateUtils.dateOnly(firstDate); + assert(lastDate != null); + lastDate = DateUtils.dateOnly(lastDate); + assert( + !lastDate.isBefore(firstDate), + 'lastDate $lastDate must be on or after firstDate $firstDate.', + ); + assert( + initialDateRange == null || !initialDateRange.start.isBefore(firstDate), + "initialDateRange's start date must be on or after firstDate $firstDate.", + ); + assert( + initialDateRange == null || !initialDateRange.end.isBefore(firstDate), + "initialDateRange's end date must be on or after firstDate $firstDate.", + ); + assert( + initialDateRange == null || !initialDateRange.start.isAfter(lastDate), + "initialDateRange's start date must be on or before lastDate $lastDate.", + ); + assert( + initialDateRange == null || !initialDateRange.end.isAfter(lastDate), + "initialDateRange's end date must be on or before lastDate $lastDate.", + ); + currentDate = DateUtils.dateOnly(currentDate ?? DateTime.now()); + assert(initialEntryMode != null); + assert(useRootNavigator != null); + assert(debugCheckHasMaterialLocalizations(context)); + + Widget dialog = DateRangePickerDialog( + initialDateRange: initialDateRange, + firstDate: firstDate, + lastDate: lastDate, + currentDate: currentDate, + initialEntryMode: initialEntryMode, + helpText: helpText, + cancelText: cancelText, + confirmText: confirmText, + saveText: saveText, + errorFormatText: errorFormatText, + errorInvalidText: errorInvalidText, + errorInvalidRangeText: errorInvalidRangeText, + fieldStartHintText: fieldStartHintText, + fieldEndHintText: fieldEndHintText, + fieldStartLabelText: fieldStartLabelText, + fieldEndLabelText: fieldEndLabelText, + ); + + if (textDirection != null) { + dialog = Directionality( + textDirection: textDirection, + child: dialog, + ); + } + + if (locale != null) { + dialog = Localizations.override( + context: context, + locale: locale, + child: dialog, + ); + } + + return showDialog( + context: context, + useRootNavigator: useRootNavigator, + routeSettings: routeSettings, + useSafeArea: false, + builder: (BuildContext context) { + return builder == null ? dialog : builder(context, dialog); + }, + anchorPoint: anchorPoint, + ); +} + +/// Returns a locale-appropriate string to describe the start of a date range. +/// +/// If `startDate` is null, then it defaults to 'Start Date', otherwise if it +/// is in the same year as the `endDate` then it will use the short month +/// day format (i.e. 'Jan 21'). Otherwise it will return the short date format +/// (i.e. 'Jan 21, 2020'). +String _formatRangeStartDate(MaterialLocalizations localizations, + DateTime? startDate, DateTime? endDate) { + return startDate == null + ? localizations.dateRangeStartLabel + : (endDate == null || startDate.year == endDate.year) + ? localizations.formatShortMonthDay(startDate) + : localizations.formatShortDate(startDate); +} + +/// Returns an locale-appropriate string to describe the end of a date range. +/// +/// If `endDate` is null, then it defaults to 'End Date', otherwise if it +/// is in the same year as the `startDate` and the `currentDate` then it will +/// just use the short month day format (i.e. 'Jan 21'), otherwise it will +/// include the year (i.e. 'Jan 21, 2020'). +String _formatRangeEndDate(MaterialLocalizations localizations, + DateTime? startDate, DateTime? endDate, DateTime currentDate) { + return endDate == null + ? localizations.dateRangeEndLabel + : (startDate != null && + startDate.year == endDate.year && + startDate.year == currentDate.year) + ? localizations.formatShortMonthDay(endDate) + : localizations.formatShortDate(endDate); +} + +/// A Material-style date range picker dialog. +/// +/// It is used internally by [showDateRangePicker] or can be directly pushed +/// onto the [Navigator] stack to enable state restoration. See +/// [showDateRangePicker] for a state restoration app example. +/// +/// See also: +/// +/// * [showDateRangePicker], which is a way to display the date picker. +class DateRangePickerDialog extends StatefulWidget { + /// A Material-style date range picker dialog. + const DateRangePickerDialog({ + super.key, + this.initialDateRange, + required this.firstDate, + required this.lastDate, + this.currentDate, + this.initialEntryMode = DatePickerEntryMode.calendar, + this.helpText, + this.cancelText, + this.confirmText, + this.saveText, + this.errorInvalidRangeText, + this.errorFormatText, + this.errorInvalidText, + this.fieldStartHintText, + this.fieldEndHintText, + this.fieldStartLabelText, + this.fieldEndLabelText, + this.restorationId, + }); + + /// The date range that the date range picker starts with when it opens. + /// + /// If an initial date range is provided, `initialDateRange.start` + /// and `initialDateRange.end` must both fall between or on [firstDate] and + /// [lastDate]. For all of these [DateTime] values, only their dates are + /// considered. Their time fields are ignored. + /// + /// If [initialDateRange] is non-null, then it will be used as the initially + /// selected date range. If it is provided, `initialDateRange.start` must be + /// before or on `initialDateRange.end`. + final DateTimeRange? initialDateRange; + + /// The earliest allowable date on the date range. + final DateTime firstDate; + + /// The latest allowable date on the date range. + final DateTime lastDate; + + /// The [currentDate] represents the current day (i.e. today). + /// + /// This date will be highlighted in the day grid. + /// + /// If `null`, the date of `DateTime.now()` will be used. + final DateTime? currentDate; + + /// The initial date range picker entry mode. + /// + /// The date range has two main modes: [DatePickerEntryMode.calendar] (a + /// scrollable calendar month grid) or [DatePickerEntryMode.input] (two text + /// input fields) mode. + /// + /// It defaults to [DatePickerEntryMode.calendar] and must be non-null. + final DatePickerEntryMode initialEntryMode; + + /// The label on the cancel button for the text input mode. + /// + /// If null, the localized value of + /// [MaterialLocalizations.cancelButtonLabel] is used. + final String? cancelText; + + /// The label on the "OK" button for the text input mode. + /// + /// If null, the localized value of + /// [MaterialLocalizations.okButtonLabel] is used. + final String? confirmText; + + /// The label on the save button for the fullscreen calendar mode. + /// + /// If null, the localized value of + /// [MaterialLocalizations.saveButtonLabel] is used. + final String? saveText; + + /// The label displayed at the top of the dialog. + /// + /// If null, the localized value of + /// [MaterialLocalizations.dateRangePickerHelpText] is used. + final String? helpText; + + /// The message used when the date range is invalid (e.g. start date is after + /// end date). + /// + /// If null, the localized value of + /// [MaterialLocalizations.invalidDateRangeLabel] is used. + final String? errorInvalidRangeText; + + /// The message used when an input text isn't in a proper date format. + /// + /// If null, the localized value of + /// [MaterialLocalizations.invalidDateFormatLabel] is used. + final String? errorFormatText; + + /// The message used when an input text isn't a selectable date. + /// + /// If null, the localized value of + /// [MaterialLocalizations.dateOutOfRangeLabel] is used. + final String? errorInvalidText; + + /// The text used to prompt the user when no text has been entered in the + /// start field. + /// + /// If null, the localized value of + /// [MaterialLocalizations.dateHelpText] is used. + final String? fieldStartHintText; + + /// The text used to prompt the user when no text has been entered in the + /// end field. + /// + /// If null, the localized value of [MaterialLocalizations.dateHelpText] is + /// used. + final String? fieldEndHintText; + + /// The label for the start date text input field. + /// + /// If null, the localized value of [MaterialLocalizations.dateRangeStartLabel] + /// is used. + final String? fieldStartLabelText; + + /// The label for the end date text input field. + /// + /// If null, the localized value of [MaterialLocalizations.dateRangeEndLabel] + /// is used. + final String? fieldEndLabelText; + + /// Restoration ID to save and restore the state of the [DateRangePickerDialog]. + /// + /// If it is non-null, the date range picker will persist and restore the + /// date range selected on the dialog. + /// + /// The state of this widget is persisted in a [RestorationBucket] claimed + /// from the surrounding [RestorationScope] using the provided restoration ID. + /// + /// See also: + /// + /// * [RestorationManager], which explains how state restoration works in + /// Flutter. + final String? restorationId; + + @override + State createState() => _DateRangePickerDialogState(); +} + +class _DateRangePickerDialogState extends State + with RestorationMixin { + late final _RestorableDatePickerEntryMode _entryMode = + _RestorableDatePickerEntryMode(widget.initialEntryMode); + late final RestorableDateTimeN _selectedStart = + RestorableDateTimeN(widget.initialDateRange?.start); + late final RestorableDateTimeN _selectedEnd = + RestorableDateTimeN(widget.initialDateRange?.end); + final RestorableBool _autoValidate = RestorableBool(false); + final GlobalKey _calendarPickerKey = GlobalKey(); + final GlobalKey<_InputDateRangePickerState> _inputPickerKey = + GlobalKey<_InputDateRangePickerState>(); + + @override + String? get restorationId => widget.restorationId; + + @override + void restoreState(RestorationBucket? oldBucket, bool initialRestore) { + registerForRestoration(_entryMode, 'entry_mode'); + registerForRestoration(_selectedStart, 'selected_start'); + registerForRestoration(_selectedEnd, 'selected_end'); + registerForRestoration(_autoValidate, 'autovalidate'); + } + + void _handleOk() { + if (_entryMode.value == DatePickerEntryMode.input || + _entryMode.value == DatePickerEntryMode.inputOnly) { + final _InputDateRangePickerState picker = _inputPickerKey.currentState!; + if (!picker.validate()) { + setState(() { + _autoValidate.value = true; + }); + return; + } + } + final DateTimeRange? selectedRange = _hasSelectedDateRange + ? DateTimeRange(start: _selectedStart.value!, end: _selectedEnd.value!) + : null; + + Navigator.pop(context, selectedRange); + } + + void _handleCancel() { + Navigator.pop(context); + } + + void _handleEntryModeToggle() { + setState(() { + switch (_entryMode.value) { + case DatePickerEntryMode.calendar: + _autoValidate.value = false; + _entryMode.value = DatePickerEntryMode.input; + break; + + case DatePickerEntryMode.input: + // Validate the range dates + if (_selectedStart.value != null && + (_selectedStart.value!.isBefore(widget.firstDate) || + _selectedStart.value!.isAfter(widget.lastDate))) { + _selectedStart.value = null; + // With no valid start date, having an end date makes no sense for the UI. + _selectedEnd.value = null; + } + if (_selectedEnd.value != null && + (_selectedEnd.value!.isBefore(widget.firstDate) || + _selectedEnd.value!.isAfter(widget.lastDate))) { + _selectedEnd.value = null; + } + // If invalid range (start after end), then just use the start date + if (_selectedStart.value != null && + _selectedEnd.value != null && + _selectedStart.value!.isAfter(_selectedEnd.value!)) { + _selectedEnd.value = null; + } + _entryMode.value = DatePickerEntryMode.calendar; + break; + + case DatePickerEntryMode.calendarOnly: + case DatePickerEntryMode.inputOnly: + assert(false, 'Can not change entry mode from $_entryMode'); + break; + } + }); + } + + void _handleStartDateChanged(DateTime? date) { + setState(() => _selectedStart.value = date); + } + + void _handleEndDateChanged(DateTime? date) { + setState(() => _selectedEnd.value = date); + } + + bool get _hasSelectedDateRange => + _selectedStart.value != null && _selectedEnd.value != null; + + @override + Widget build(BuildContext context) { + final MediaQueryData mediaQuery = MediaQuery.of(context); + final Orientation orientation = mediaQuery.orientation; + final double textScaleFactor = math.min(mediaQuery.textScaleFactor, 1.3); + final MaterialLocalizations localizations = + MaterialLocalizations.of(context); + final ColorScheme colors = Theme.of(context).colorScheme; + final Color onPrimarySurface = colors.brightness == Brightness.light + ? colors.onPrimary + : colors.onSurface; + + final Widget contents; + final Size size; + ShapeBorder? shape; + final double elevation; + final EdgeInsets insetPadding; + final bool showEntryModeButton = + _entryMode.value == DatePickerEntryMode.calendar || + _entryMode.value == DatePickerEntryMode.input; + switch (_entryMode.value) { + case DatePickerEntryMode.calendar: + case DatePickerEntryMode.calendarOnly: + contents = _CalendarRangePickerDialog( + key: _calendarPickerKey, + selectedStartDate: _selectedStart.value, + selectedEndDate: _selectedEnd.value, + firstDate: widget.firstDate, + lastDate: widget.lastDate, + currentDate: widget.currentDate, + onStartDateChanged: _handleStartDateChanged, + onEndDateChanged: _handleEndDateChanged, + onConfirm: _hasSelectedDateRange ? _handleOk : null, + onCancel: _handleCancel, + entryModeButton: showEntryModeButton + ? IconButton( + icon: const Icon(Icons.edit), + padding: EdgeInsets.zero, + color: onPrimarySurface, + tooltip: localizations.inputDateModeButtonLabel, + onPressed: _handleEntryModeToggle, + ) + : null, + confirmText: widget.saveText ?? localizations.saveButtonLabel, + helpText: widget.helpText ?? localizations.dateRangePickerHelpText, + ); + size = mediaQuery.size; + insetPadding = EdgeInsets.zero; + shape = const RoundedRectangleBorder(); + elevation = 0; + break; + + case DatePickerEntryMode.input: + case DatePickerEntryMode.inputOnly: + contents = _InputDateRangePickerDialog( + selectedStartDate: _selectedStart.value, + selectedEndDate: _selectedEnd.value, + currentDate: widget.currentDate, + picker: Container( + padding: const EdgeInsets.symmetric(horizontal: 24), + height: orientation == Orientation.portrait + ? _inputFormPortraitHeight + : _inputFormLandscapeHeight, + child: Column( + children: [ + const Spacer(), + _InputDateRangePicker( + key: _inputPickerKey, + initialStartDate: _selectedStart.value, + initialEndDate: _selectedEnd.value, + firstDate: widget.firstDate, + lastDate: widget.lastDate, + onStartDateChanged: _handleStartDateChanged, + onEndDateChanged: _handleEndDateChanged, + autofocus: true, + autovalidate: _autoValidate.value, + helpText: widget.helpText, + errorInvalidRangeText: widget.errorInvalidRangeText, + errorFormatText: widget.errorFormatText, + errorInvalidText: widget.errorInvalidText, + fieldStartHintText: widget.fieldStartHintText, + fieldEndHintText: widget.fieldEndHintText, + fieldStartLabelText: widget.fieldStartLabelText, + fieldEndLabelText: widget.fieldEndLabelText, + ), + const Spacer(), + ], + ), + ), + onConfirm: _handleOk, + onCancel: _handleCancel, + entryModeButton: showEntryModeButton + ? IconButton( + icon: const Icon(Icons.calendar_today), + padding: EdgeInsets.zero, + color: onPrimarySurface, + tooltip: localizations.calendarModeButtonLabel, + onPressed: _handleEntryModeToggle, + ) + : null, + confirmText: widget.confirmText ?? localizations.okButtonLabel, + cancelText: widget.cancelText ?? localizations.cancelButtonLabel, + helpText: widget.helpText ?? localizations.dateRangePickerHelpText, + ); + final DialogThemeData dialogTheme = Theme.of(context).dialogTheme; + size = orientation == Orientation.portrait + ? _inputPortraitDialogSize + : _inputRangeLandscapeDialogSize; + insetPadding = + const EdgeInsets.symmetric(horizontal: 16.0, vertical: 24.0); + shape = dialogTheme.shape; + elevation = dialogTheme.elevation ?? 24; + break; + } + + return Dialog( + insetPadding: insetPadding, + shape: shape, + elevation: elevation, + clipBehavior: Clip.antiAlias, + child: AnimatedContainer( + width: size.width, + height: size.height, + duration: _dialogSizeAnimationDuration, + curve: Curves.easeIn, + child: MediaQuery( + data: MediaQuery.of(context).copyWith( + textScaleFactor: textScaleFactor, + ), + child: Builder(builder: (BuildContext context) { + return contents; + }), + ), + ), + ); + } +} + +class _CalendarRangePickerDialog extends StatelessWidget { + const _CalendarRangePickerDialog({ + super.key, + required this.selectedStartDate, + required this.selectedEndDate, + required this.firstDate, + required this.lastDate, + required this.currentDate, + required this.onStartDateChanged, + required this.onEndDateChanged, + required this.onConfirm, + required this.onCancel, + required this.confirmText, + required this.helpText, + this.entryModeButton, + }); + + final DateTime? selectedStartDate; + final DateTime? selectedEndDate; + final DateTime firstDate; + final DateTime lastDate; + final DateTime? currentDate; + final ValueChanged onStartDateChanged; + final ValueChanged onEndDateChanged; + final VoidCallback? onConfirm; + final VoidCallback? onCancel; + final String confirmText; + final String helpText; + final Widget? entryModeButton; + + @override + Widget build(BuildContext context) { + final ThemeData theme = Theme.of(context); + final ColorScheme colorScheme = theme.colorScheme; + final MaterialLocalizations localizations = + MaterialLocalizations.of(context); + final Orientation orientation = MediaQuery.of(context).orientation; + final TextTheme textTheme = theme.textTheme; + final Color headerForeground = colorScheme.brightness == Brightness.light + ? colorScheme.onPrimary + : colorScheme.onSurface; + final Color headerDisabledForeground = headerForeground.withOpacity(0.38); + final String startDateText = _formatRangeStartDate( + localizations, selectedStartDate, selectedEndDate); + final String endDateText = _formatRangeEndDate( + localizations, selectedStartDate, selectedEndDate, DateTime.now()); + final TextStyle? headlineStyle = textTheme.headlineMedium; + final TextStyle? startDateStyle = headlineStyle?.apply( + color: selectedStartDate != null + ? headerForeground + : headerDisabledForeground, + ); + final TextStyle? endDateStyle = headlineStyle?.apply( + color: + selectedEndDate != null ? headerForeground : headerDisabledForeground, + ); + final TextStyle saveButtonStyle = textTheme.headlineMedium!.apply( + color: onConfirm != null ? headerForeground : headerDisabledForeground, + ); + + return SafeArea( + top: false, + left: false, + right: false, + child: Scaffold( + appBar: AppBar( + leading: CloseButton( + onPressed: onCancel, + ), + actions: [ + if (orientation == Orientation.landscape && entryModeButton != null) + entryModeButton!, + TextButton( + onPressed: onConfirm, + child: Text(confirmText, style: saveButtonStyle), + ), + const SizedBox(width: 8), + ], + bottom: PreferredSize( + preferredSize: const Size(double.infinity, 64), + child: Row(children: [ + SizedBox( + width: MediaQuery.of(context).size.width < 360 ? 42 : 72), + Expanded( + child: Semantics( + label: '$helpText $startDateText to $endDateText', + excludeSemantics: true, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + helpText, + style: textTheme.headlineMedium!.apply( + color: headerForeground, + ), + ), + const SizedBox(height: 8), + Row( + children: [ + Text( + startDateText, + style: startDateStyle, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + Text( + ' – ', + style: startDateStyle, + ), + Flexible( + child: Text( + endDateText, + style: endDateStyle, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + const SizedBox(height: 16), + ], + ), + ), + ), + if (orientation == Orientation.portrait && + entryModeButton != null) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: entryModeButton, + ), + ]), + ), + ), + body: _CalendarDateRangePicker( + initialStartDate: selectedStartDate, + initialEndDate: selectedEndDate, + firstDate: firstDate, + lastDate: lastDate, + currentDate: currentDate, + onStartDateChanged: onStartDateChanged, + onEndDateChanged: onEndDateChanged, + ), + ), + ); + } +} + +const Duration _monthScrollDuration = Duration(milliseconds: 200); + +const double _monthItemHeaderHeight = 58.0; +const double _monthItemFooterHeight = 12.0; +const double _monthItemRowHeight = 42.0; +const double _monthItemSpaceBetweenRows = 8.0; +const double _horizontalPadding = 8.0; +const double _maxCalendarWidthLandscape = 384.0; +const double _maxCalendarWidthPortrait = 480.0; + +/// Displays a scrollable calendar grid that allows a user to select a range +/// of dates. +class _CalendarDateRangePicker extends StatefulWidget { + /// Creates a scrollable calendar grid for picking date ranges. + _CalendarDateRangePicker({ + DateTime? initialStartDate, + DateTime? initialEndDate, + required DateTime firstDate, + required DateTime lastDate, + DateTime? currentDate, + required this.onStartDateChanged, + required this.onEndDateChanged, + }) : initialStartDate = initialStartDate != null + ? DateUtils.dateOnly(initialStartDate) + : null, + initialEndDate = + initialEndDate != null ? DateUtils.dateOnly(initialEndDate) : null, + assert(firstDate != null), + assert(lastDate != null), + firstDate = DateUtils.dateOnly(firstDate), + lastDate = DateUtils.dateOnly(lastDate), + currentDate = DateUtils.dateOnly(currentDate ?? DateTime.now()) { + assert( + this.initialStartDate == null || + this.initialEndDate == null || + !this.initialStartDate!.isAfter(initialEndDate!), + 'initialStartDate must be on or before initialEndDate.', + ); + assert( + !this.lastDate.isBefore(this.firstDate), + 'firstDate must be on or before lastDate.', + ); + } + + /// The [DateTime] that represents the start of the initial date range selection. + final DateTime? initialStartDate; + + /// The [DateTime] that represents the end of the initial date range selection. + final DateTime? initialEndDate; + + /// The earliest allowable [DateTime] that the user can select. + final DateTime firstDate; + + /// The latest allowable [DateTime] that the user can select. + final DateTime lastDate; + + /// The [DateTime] representing today. It will be highlighted in the day grid. + final DateTime currentDate; + + /// Called when the user changes the start date of the selected range. + final ValueChanged? onStartDateChanged; + + /// Called when the user changes the end date of the selected range. + final ValueChanged? onEndDateChanged; + + @override + _CalendarDateRangePickerState createState() => + _CalendarDateRangePickerState(); +} + +class _CalendarDateRangePickerState extends State<_CalendarDateRangePicker> { + final GlobalKey _scrollViewKey = GlobalKey(); + DateTime? _startDate; + DateTime? _endDate; + int _initialMonthIndex = 0; + late ScrollController _controller; + late bool _showWeekBottomDivider; + + @override + void initState() { + super.initState(); + _controller = ScrollController(); + _controller.addListener(_scrollListener); + + _startDate = widget.initialStartDate; + _endDate = widget.initialEndDate; + + // Calculate the index for the initially displayed month. This is needed to + // divide the list of months into two `SliverList`s. + final DateTime initialDate = widget.initialStartDate ?? widget.currentDate; + if (!initialDate.isBefore(widget.firstDate) && + !initialDate.isAfter(widget.lastDate)) { + _initialMonthIndex = DateUtils.monthDelta(widget.firstDate, initialDate); + } + + _showWeekBottomDivider = _initialMonthIndex != 0; + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + void _scrollListener() { + if (_controller.offset <= _controller.position.minScrollExtent) { + setState(() { + _showWeekBottomDivider = false; + }); + } else if (!_showWeekBottomDivider) { + setState(() { + _showWeekBottomDivider = true; + }); + } + } + + int get _numberOfMonths => + DateUtils.monthDelta(widget.firstDate, widget.lastDate) + 1; + + void _vibrate() { + switch (Theme.of(context).platform) { + case TargetPlatform.android: + case TargetPlatform.fuchsia: + HapticFeedback.vibrate(); + break; + case TargetPlatform.iOS: + case TargetPlatform.linux: + case TargetPlatform.macOS: + case TargetPlatform.windows: + break; + } + } + + // This updates the selected date range using this logic: + // + // * From the unselected state, selecting one date creates the start date. + // * If the next selection is before the start date, reset date range and + // set the start date to that selection. + // * If the next selection is on or after the start date, set the end date + // to that selection. + // * After both start and end dates are selected, any subsequent selection + // resets the date range and sets start date to that selection. + void _updateSelection(DateTime date) { + _vibrate(); + setState(() { + if (_startDate != null && + _endDate == null && + !date.isBefore(_startDate!)) { + _endDate = date; + widget.onEndDateChanged?.call(_endDate); + } else { + _startDate = date; + widget.onStartDateChanged?.call(_startDate!); + if (_endDate != null) { + _endDate = null; + widget.onEndDateChanged?.call(_endDate); + } + } + }); + } + + Widget _buildMonthItem( + BuildContext context, int index, bool beforeInitialMonth) { + final int monthIndex = beforeInitialMonth + ? _initialMonthIndex - index - 1 + : _initialMonthIndex + index; + final DateTime month = + DateUtils.addMonthsToMonthDate(widget.firstDate, monthIndex); + return Stack( + alignment: Alignment.center, + children: [ + Text( + "${month.month}", + style: TextStyle(fontSize: 200, color: Colors.grey.withOpacity(0.1)), + ), + _MonthItem( + selectedDateStart: _startDate, + selectedDateEnd: _endDate, + currentDate: widget.currentDate, + firstDate: widget.firstDate, + lastDate: widget.lastDate, + displayedMonth: month, + onChanged: _updateSelection, + ), + ], + ); + } + + @override + Widget build(BuildContext context) { + const Key sliverAfterKey = Key('sliverAfterKey'); + + return Column( + children: [ + const _DayHeaders(), + if (_showWeekBottomDivider) const Divider(height: 0), + Expanded( + child: _CalendarKeyboardNavigator( + firstDate: widget.firstDate, + lastDate: widget.lastDate, + initialFocusedDay: + _startDate ?? widget.initialStartDate ?? widget.currentDate, + // In order to prevent performance issues when displaying the + // correct initial month, 2 `SliverList`s are used to split the + // months. The first item in the second SliverList is the initial + // month to be displayed. + child: CustomScrollView( + key: _scrollViewKey, + controller: _controller, + center: sliverAfterKey, + slivers: [ + SliverList( + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) => + _buildMonthItem(context, index, true), + childCount: _initialMonthIndex, + ), + ), + SliverList( + key: sliverAfterKey, + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) => + _buildMonthItem(context, index, false), + childCount: _numberOfMonths - _initialMonthIndex, + ), + ), + ], + ), + ), + ), + ], + ); + } +} + +class _CalendarKeyboardNavigator extends StatefulWidget { + const _CalendarKeyboardNavigator({ + required this.child, + required this.firstDate, + required this.lastDate, + required this.initialFocusedDay, + }); + + final Widget child; + final DateTime firstDate; + final DateTime lastDate; + final DateTime initialFocusedDay; + + @override + _CalendarKeyboardNavigatorState createState() => + _CalendarKeyboardNavigatorState(); +} + +class _CalendarKeyboardNavigatorState + extends State<_CalendarKeyboardNavigator> { + final Map _shortcutMap = + const { + SingleActivator(LogicalKeyboardKey.arrowLeft): + DirectionalFocusIntent(TraversalDirection.left), + SingleActivator(LogicalKeyboardKey.arrowRight): + DirectionalFocusIntent(TraversalDirection.right), + SingleActivator(LogicalKeyboardKey.arrowDown): + DirectionalFocusIntent(TraversalDirection.down), + SingleActivator(LogicalKeyboardKey.arrowUp): + DirectionalFocusIntent(TraversalDirection.up), + }; + late Map> _actionMap; + late FocusNode _dayGridFocus; + TraversalDirection? _dayTraversalDirection; + DateTime? _focusedDay; + + @override + void initState() { + super.initState(); + + _actionMap = >{ + NextFocusIntent: + CallbackAction(onInvoke: _handleGridNextFocus), + PreviousFocusIntent: CallbackAction( + onInvoke: _handleGridPreviousFocus), + DirectionalFocusIntent: CallbackAction( + onInvoke: _handleDirectionFocus), + }; + _dayGridFocus = FocusNode(debugLabel: 'Day Grid'); + } + + @override + void dispose() { + _dayGridFocus.dispose(); + super.dispose(); + } + + void _handleGridFocusChange(bool focused) { + setState(() { + if (focused) { + _focusedDay ??= widget.initialFocusedDay; + } + }); + } + + /// Move focus to the next element after the day grid. + void _handleGridNextFocus(NextFocusIntent intent) { + _dayGridFocus.requestFocus(); + _dayGridFocus.nextFocus(); + } + + /// Move focus to the previous element before the day grid. + void _handleGridPreviousFocus(PreviousFocusIntent intent) { + _dayGridFocus.requestFocus(); + _dayGridFocus.previousFocus(); + } + + /// Move the internal focus date in the direction of the given intent. + /// + /// This will attempt to move the focused day to the next selectable day in + /// the given direction. If the new date is not in the current month, then + /// the page view will be scrolled to show the new date's month. + /// + /// For horizontal directions, it will move forward or backward a day (depending + /// on the current [TextDirection]). For vertical directions it will move up and + /// down a week at a time. + void _handleDirectionFocus(DirectionalFocusIntent intent) { + assert(_focusedDay != null); + setState(() { + final DateTime? nextDate = + _nextDateInDirection(_focusedDay!, intent.direction); + if (nextDate != null) { + _focusedDay = nextDate; + _dayTraversalDirection = intent.direction; + } + }); + } + + static const Map _directionOffset = + { + TraversalDirection.up: -DateTime.daysPerWeek, + TraversalDirection.right: 1, + TraversalDirection.down: DateTime.daysPerWeek, + TraversalDirection.left: -1, + }; + + int _dayDirectionOffset( + TraversalDirection traversalDirection, TextDirection textDirection) { + // Swap left and right if the text direction if RTL + if (textDirection == TextDirection.rtl) { + if (traversalDirection == TraversalDirection.left) { + traversalDirection = TraversalDirection.right; + } else if (traversalDirection == TraversalDirection.right) { + traversalDirection = TraversalDirection.left; + } + } + return _directionOffset[traversalDirection]!; + } + + DateTime? _nextDateInDirection(DateTime date, TraversalDirection direction) { + final TextDirection textDirection = Directionality.of(context); + final DateTime nextDate = DateUtils.addDaysToDate( + date, _dayDirectionOffset(direction, textDirection)); + if (!nextDate.isBefore(widget.firstDate) && + !nextDate.isAfter(widget.lastDate)) { + return nextDate; + } + return null; + } + + @override + Widget build(BuildContext context) { + return FocusableActionDetector( + shortcuts: _shortcutMap, + actions: _actionMap, + focusNode: _dayGridFocus, + onFocusChange: _handleGridFocusChange, + child: _FocusedDate( + date: _dayGridFocus.hasFocus ? _focusedDay : null, + scrollDirection: _dayGridFocus.hasFocus ? _dayTraversalDirection : null, + child: widget.child, + ), + ); + } +} + +/// InheritedWidget indicating what the current focused date is for its children. +/// +/// This is used by the [_MonthPicker] to let its children [_DayPicker]s know +/// what the currently focused date (if any) should be. +class _FocusedDate extends InheritedWidget { + const _FocusedDate({ + required super.child, + this.date, + this.scrollDirection, + }); + + final DateTime? date; + final TraversalDirection? scrollDirection; + + @override + bool updateShouldNotify(_FocusedDate oldWidget) { + return !DateUtils.isSameDay(date, oldWidget.date) || + scrollDirection != oldWidget.scrollDirection; + } + + static _FocusedDate? of(BuildContext context) { + return context.dependOnInheritedWidgetOfExactType<_FocusedDate>(); + } +} + +class _DayHeaders extends StatelessWidget { + const _DayHeaders(); + + /// Builds widgets showing abbreviated days of week. The first widget in the + /// returned list corresponds to the first day of week for the current locale. + /// + /// Examples: + /// + /// ``` + /// ┌ Sunday is the first day of week in the US (en_US) + /// | + /// S M T W T F S <-- the returned list contains these widgets + /// _ _ _ _ _ 1 2 + /// 3 4 5 6 7 8 9 + /// + /// ┌ But it's Monday in the UK (en_GB) + /// | + /// M T W T F S S <-- the returned list contains these widgets + /// _ _ _ _ 1 2 3 + /// 4 5 6 7 8 9 10 + /// ``` + List _getDayHeaders( + TextStyle headerStyle, MaterialLocalizations localizations) { + final List result = []; + for (int i = localizations.firstDayOfWeekIndex; true; i = (i + 1) % 7) { + final String weekday = localizations.narrowWeekdays[i]; + result.add(ExcludeSemantics( + child: Center(child: Text(weekday, style: headerStyle)), + )); + if (i == (localizations.firstDayOfWeekIndex - 1) % 7) { + break; + } + } + return result; + } + + @override + Widget build(BuildContext context) { + final ThemeData themeData = Theme.of(context); + final ColorScheme colorScheme = themeData.colorScheme; + final TextStyle textStyle = + themeData.textTheme.headlineMedium!.apply(color: colorScheme.onSurface); + final MaterialLocalizations localizations = + MaterialLocalizations.of(context); + final List labels = _getDayHeaders(textStyle, localizations); + + // Add leading and trailing containers for edges of the custom grid layout. + labels.insert(0, Container()); + labels.add(Container()); + + return Container( + constraints: BoxConstraints( + maxWidth: MediaQuery.of(context).orientation == Orientation.landscape + ? _maxCalendarWidthLandscape + : _maxCalendarWidthPortrait, + maxHeight: _monthItemRowHeight, + ), + child: GridView.custom( + shrinkWrap: true, + gridDelegate: _monthItemGridDelegate, + childrenDelegate: SliverChildListDelegate( + labels, + addRepaintBoundaries: false, + ), + ), + ); + } +} + +class _MonthItemGridDelegate extends SliverGridDelegate { + const _MonthItemGridDelegate(); + + @override + SliverGridLayout getLayout(SliverConstraints constraints) { + final double tileWidth = + (constraints.crossAxisExtent - 2 * _horizontalPadding) / + DateTime.daysPerWeek; + return _MonthSliverGridLayout( + crossAxisCount: DateTime.daysPerWeek + 2, + dayChildWidth: tileWidth, + edgeChildWidth: _horizontalPadding, + reverseCrossAxis: axisDirectionIsReversed(constraints.crossAxisDirection), + ); + } + + @override + bool shouldRelayout(_MonthItemGridDelegate oldDelegate) => false; +} + +const _MonthItemGridDelegate _monthItemGridDelegate = _MonthItemGridDelegate(); + +class _MonthSliverGridLayout extends SliverGridLayout { + /// Creates a layout that uses equally sized and spaced tiles for each day of + /// the week and an additional edge tile for padding at the start and end of + /// each row. + /// + /// This is necessary to facilitate the painting of the range highlight + /// correctly. + const _MonthSliverGridLayout({ + required this.crossAxisCount, + required this.dayChildWidth, + required this.edgeChildWidth, + required this.reverseCrossAxis, + }) : assert(crossAxisCount != null && crossAxisCount > 0), + assert(dayChildWidth != null && dayChildWidth >= 0), + assert(edgeChildWidth != null && edgeChildWidth >= 0), + assert(reverseCrossAxis != null); + + /// The number of children in the cross axis. + final int crossAxisCount; + + /// The width in logical pixels of the day child widgets. + final double dayChildWidth; + + /// The width in logical pixels of the edge child widgets. + final double edgeChildWidth; + + /// Whether the children should be placed in the opposite order of increasing + /// coordinates in the cross axis. + /// + /// For example, if the cross axis is horizontal, the children are placed from + /// left to right when [reverseCrossAxis] is false and from right to left when + /// [reverseCrossAxis] is true. + /// + /// Typically set to the return value of [axisDirectionIsReversed] applied to + /// the [SliverConstraints.crossAxisDirection]. + final bool reverseCrossAxis; + + /// The number of logical pixels from the leading edge of one row to the + /// leading edge of the next row. + double get _rowHeight { + return _monthItemRowHeight + _monthItemSpaceBetweenRows; + } + + /// The height in logical pixels of the children widgets. + double get _childHeight { + return _monthItemRowHeight; + } + + @override + int getMinChildIndexForScrollOffset(double scrollOffset) { + return crossAxisCount * (scrollOffset ~/ _rowHeight); + } + + @override + int getMaxChildIndexForScrollOffset(double scrollOffset) { + final int mainAxisCount = (scrollOffset / _rowHeight).ceil(); + return math.max(0, crossAxisCount * mainAxisCount - 1); + } + + double _getCrossAxisOffset(double crossAxisStart, bool isPadding) { + if (reverseCrossAxis) { + return ((crossAxisCount - 2) * dayChildWidth + 2 * edgeChildWidth) - + crossAxisStart - + (isPadding ? edgeChildWidth : dayChildWidth); + } + return crossAxisStart; + } + + @override + SliverGridGeometry getGeometryForChildIndex(int index) { + final int adjustedIndex = index % crossAxisCount; + final bool isEdge = + adjustedIndex == 0 || adjustedIndex == crossAxisCount - 1; + final double crossAxisStart = + math.max(0, (adjustedIndex - 1) * dayChildWidth + edgeChildWidth); + + return SliverGridGeometry( + scrollOffset: (index ~/ crossAxisCount) * _rowHeight, + crossAxisOffset: _getCrossAxisOffset(crossAxisStart, isEdge), + mainAxisExtent: _childHeight, + crossAxisExtent: isEdge ? edgeChildWidth : dayChildWidth, + ); + } + + @override + double computeMaxScrollOffset(int childCount) { + assert(childCount >= 0); + final int mainAxisCount = ((childCount - 1) ~/ crossAxisCount) + 1; + final double mainAxisSpacing = _rowHeight - _childHeight; + return _rowHeight * mainAxisCount - mainAxisSpacing; + } +} + +/// Displays the days of a given month and allows choosing a date range. +/// +/// The days are arranged in a rectangular grid with one column for each day of +/// the week. +class _MonthItem extends StatefulWidget { + /// Creates a month item. + _MonthItem({ + required this.selectedDateStart, + required this.selectedDateEnd, + required this.currentDate, + required this.onChanged, + required this.firstDate, + required this.lastDate, + required this.displayedMonth, + this.dragStartBehavior = DragStartBehavior.start, + }) : assert(firstDate != null), + assert(lastDate != null), + assert(!firstDate.isAfter(lastDate)), + assert(selectedDateStart == null || + !selectedDateStart.isBefore(firstDate)), + assert(selectedDateEnd == null || !selectedDateEnd.isBefore(firstDate)), + assert( + selectedDateStart == null || !selectedDateStart.isAfter(lastDate)), + assert(selectedDateEnd == null || !selectedDateEnd.isAfter(lastDate)), + assert(selectedDateStart == null || + selectedDateEnd == null || + !selectedDateStart.isAfter(selectedDateEnd)), + assert(currentDate != null), + assert(onChanged != null), + assert(displayedMonth != null), + assert(dragStartBehavior != null); + + /// The currently selected start date. + /// + /// This date is highlighted in the picker. + final DateTime? selectedDateStart; + + /// The currently selected end date. + /// + /// This date is highlighted in the picker. + final DateTime? selectedDateEnd; + + /// The current date at the time the picker is displayed. + final DateTime currentDate; + + /// Called when the user picks a day. + final ValueChanged onChanged; + + /// The earliest date the user is permitted to pick. + final DateTime firstDate; + + /// The latest date the user is permitted to pick. + final DateTime lastDate; + + /// The month whose days are displayed by this picker. + final DateTime displayedMonth; + + /// Determines the way that drag start behavior is handled. + /// + /// If set to [DragStartBehavior.start], the drag gesture used to scroll a + /// date picker wheel will begin at the position where the drag gesture won + /// the arena. If set to [DragStartBehavior.down] it will begin at the position + /// where a down event is first detected. + /// + /// In general, setting this to [DragStartBehavior.start] will make drag + /// animation smoother and setting it to [DragStartBehavior.down] will make + /// drag behavior feel slightly more reactive. + /// + /// By default, the drag start behavior is [DragStartBehavior.start]. + /// + /// See also: + /// + /// * [DragGestureRecognizer.dragStartBehavior], which gives an example for + /// the different behaviors. + final DragStartBehavior dragStartBehavior; + + @override + _MonthItemState createState() => _MonthItemState(); +} + +class _MonthItemState extends State<_MonthItem> { + /// List of [FocusNode]s, one for each day of the month. + late List _dayFocusNodes; + + @override + void initState() { + super.initState(); + final int daysInMonth = DateUtils.getDaysInMonth( + widget.displayedMonth.year, widget.displayedMonth.month); + _dayFocusNodes = List.generate( + daysInMonth, + (int index) => + FocusNode(skipTraversal: true, debugLabel: 'Day ${index + 1}'), + ); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + // Check to see if the focused date is in this month, if so focus it. + final DateTime? focusedDate = _FocusedDate.of(context)?.date; + if (focusedDate != null && + DateUtils.isSameMonth(widget.displayedMonth, focusedDate)) { + _dayFocusNodes[focusedDate.day - 1].requestFocus(); + } + } + + @override + void dispose() { + for (final FocusNode node in _dayFocusNodes) { + node.dispose(); + } + super.dispose(); + } + + Color _highlightColor(BuildContext context) { + return Theme.of(context).colorScheme.primary.withOpacity(0.12); + } + + void _dayFocusChanged(bool focused) { + if (focused) { + final TraversalDirection? focusDirection = + _FocusedDate.of(context)?.scrollDirection; + if (focusDirection != null) { + ScrollPositionAlignmentPolicy policy = + ScrollPositionAlignmentPolicy.explicit; + switch (focusDirection) { + case TraversalDirection.up: + case TraversalDirection.left: + policy = ScrollPositionAlignmentPolicy.keepVisibleAtStart; + break; + case TraversalDirection.right: + case TraversalDirection.down: + policy = ScrollPositionAlignmentPolicy.keepVisibleAtEnd; + break; + } + Scrollable.ensureVisible( + primaryFocus!.context!, + duration: _monthScrollDuration, + alignmentPolicy: policy, + ); + } + } + } + + Widget _buildDayItem(BuildContext context, DateTime dayToBuild, + int firstDayOffset, int daysInMonth) { + final ThemeData theme = Theme.of(context); + final ColorScheme colorScheme = theme.colorScheme; + final TextTheme textTheme = theme.textTheme; + final MaterialLocalizations localizations = + MaterialLocalizations.of(context); + final TextDirection textDirection = Directionality.of(context); + final Color highlightColor = _highlightColor(context); + final int day = dayToBuild.day; + + final bool isDisabled = dayToBuild.isAfter(widget.lastDate) || + dayToBuild.isBefore(widget.firstDate); + + BoxDecoration? decoration; + TextStyle? itemStyle = textTheme.headlineMedium; + + final bool isRangeSelected = + widget.selectedDateStart != null && widget.selectedDateEnd != null; + final bool isSelectedDayStart = widget.selectedDateStart != null && + dayToBuild.isAtSameMomentAs(widget.selectedDateStart!); + final bool isSelectedDayEnd = widget.selectedDateEnd != null && + dayToBuild.isAtSameMomentAs(widget.selectedDateEnd!); + final bool isInRange = isRangeSelected && + dayToBuild.isAfter(widget.selectedDateStart!) && + dayToBuild.isBefore(widget.selectedDateEnd!); + + _HighlightPainter? highlightPainter; + + if (isSelectedDayStart || isSelectedDayEnd) { + // The selected start and end dates gets a circle background + // highlight, and a contrasting text color. + itemStyle = textTheme.headlineMedium?.apply(color: colorScheme.onPrimary); + decoration = BoxDecoration( + color: colorScheme.primary, + shape: BoxShape.circle, + ); + + if (isRangeSelected && + widget.selectedDateStart != widget.selectedDateEnd) { + final _HighlightPainterStyle style = isSelectedDayStart + ? _HighlightPainterStyle.highlightTrailing + : _HighlightPainterStyle.highlightLeading; + highlightPainter = _HighlightPainter( + color: highlightColor, + style: style, + textDirection: textDirection, + ); + } + } else if (isInRange) { + // The days within the range get a light background highlight. + highlightPainter = _HighlightPainter( + color: highlightColor, + style: _HighlightPainterStyle.highlightAll, + textDirection: textDirection, + ); + } else if (isDisabled) { + itemStyle = textTheme.headlineMedium + ?.apply(color: colorScheme.onSurface.withOpacity(0.38)); + } else if (DateUtils.isSameDay(widget.currentDate, dayToBuild)) { + // The current day gets a different text color and a circle stroke + // border. + itemStyle = textTheme.headlineMedium?.apply(color: colorScheme.primary); + decoration = BoxDecoration( + border: Border.all(color: colorScheme.primary), + shape: BoxShape.circle, + ); + } + + // We want the day of month to be spoken first irrespective of the + // locale-specific preferences or TextDirection. This is because + // an accessibility user is more likely to be interested in the + // day of month before the rest of the date, as they are looking + // for the day of month. To do that we prepend day of month to the + // formatted full date. + String semanticLabel = + '${localizations.formatDecimal(day)}, ${localizations.formatFullDate(dayToBuild)}'; + if (isSelectedDayStart) { + semanticLabel = + localizations.dateRangeStartDateSemanticLabel(semanticLabel); + } else if (isSelectedDayEnd) { + semanticLabel = + localizations.dateRangeEndDateSemanticLabel(semanticLabel); + } + + Widget dayWidget = Container( + decoration: decoration, + child: Center( + child: Semantics( + label: semanticLabel, + selected: isSelectedDayStart || isSelectedDayEnd, + child: ExcludeSemantics( + child: Text(localizations.formatDecimal(day), style: itemStyle), + ), + ), + ), + ); + + if (highlightPainter != null) { + dayWidget = CustomPaint( + painter: highlightPainter, + child: dayWidget, + ); + } + + if (!isDisabled) { + dayWidget = InkResponse( + focusNode: _dayFocusNodes[day - 1], + onTap: () => widget.onChanged(dayToBuild), + radius: _monthItemRowHeight / 2 + 4, + splashColor: colorScheme.primary.withOpacity(0.38), + onFocusChange: _dayFocusChanged, + child: dayWidget, + ); + } + + return dayWidget; + } + + Widget _buildEdgeContainer(BuildContext context, bool isHighlighted) { + return Container(color: isHighlighted ? _highlightColor(context) : null); + } + + @override + Widget build(BuildContext context) { + final ThemeData themeData = Theme.of(context); + final TextTheme textTheme = themeData.textTheme; + final MaterialLocalizations localizations = + MaterialLocalizations.of(context); + final int year = widget.displayedMonth.year; + final int month = widget.displayedMonth.month; + final int daysInMonth = DateUtils.getDaysInMonth(year, month); + final int dayOffset = DateUtils.firstDayOffset(year, month, localizations); + final int weeks = ((daysInMonth + dayOffset) / DateTime.daysPerWeek).ceil(); + final double gridHeight = + weeks * _monthItemRowHeight + (weeks - 1) * _monthItemSpaceBetweenRows; + final List dayItems = []; + + for (int i = 0; true; i += 1) { + // 1-based day of month, e.g. 1-31 for January, and 1-29 for February on + // a leap year. + final int day = i - dayOffset + 1; + if (day > daysInMonth) { + break; + } + if (day < 1) { + dayItems.add(Container()); + } else { + final DateTime dayToBuild = DateTime(year, month, day); + final Widget dayItem = _buildDayItem( + context, + dayToBuild, + dayOffset, + daysInMonth, + ); + dayItems.add(dayItem); + } + } + + // Add the leading/trailing edge containers to each week in order to + // correctly extend the range highlight. + final List paddedDayItems = []; + for (int i = 0; i < weeks; i++) { + final int start = i * DateTime.daysPerWeek; + final int end = math.min( + start + DateTime.daysPerWeek, + dayItems.length, + ); + final List weekList = dayItems.sublist(start, end); + + final DateTime dateAfterLeadingPadding = + DateTime(year, month, start - dayOffset + 1); + // Only color the edge container if it is after the start date and + // on/before the end date. + final bool isLeadingInRange = !(dayOffset > 0 && i == 0) && + widget.selectedDateStart != null && + widget.selectedDateEnd != null && + dateAfterLeadingPadding.isAfter(widget.selectedDateStart!) && + !dateAfterLeadingPadding.isAfter(widget.selectedDateEnd!); + weekList.insert(0, _buildEdgeContainer(context, isLeadingInRange)); + + // Only add a trailing edge container if it is for a full week and not a + // partial week. + if (end < dayItems.length || + (end == dayItems.length && + dayItems.length % DateTime.daysPerWeek == 0)) { + final DateTime dateBeforeTrailingPadding = + DateTime(year, month, end - dayOffset); + // Only color the edge container if it is on/after the start date and + // before the end date. + final bool isTrailingInRange = widget.selectedDateStart != null && + widget.selectedDateEnd != null && + !dateBeforeTrailingPadding.isBefore(widget.selectedDateStart!) && + dateBeforeTrailingPadding.isBefore(widget.selectedDateEnd!); + weekList.add(_buildEdgeContainer(context, isTrailingInRange)); + } + + paddedDayItems.addAll(weekList); + } + + final double maxWidth = + MediaQuery.of(context).orientation == Orientation.landscape + ? _maxCalendarWidthLandscape + : _maxCalendarWidthPortrait; + return Column( + children: [ + Container( + constraints: BoxConstraints(maxWidth: maxWidth), + height: _monthItemHeaderHeight, + padding: const EdgeInsets.symmetric(horizontal: 16), + alignment: AlignmentDirectional.centerStart, + child: ExcludeSemantics( + child: Text( + localizations.formatMonthYear(widget.displayedMonth), + style: textTheme.headlineMedium! + .apply(color: themeData.colorScheme.onSurface), + ), + ), + ), + Container( + constraints: BoxConstraints( + maxWidth: maxWidth, + maxHeight: gridHeight, + ), + child: GridView.custom( + physics: const NeverScrollableScrollPhysics(), + gridDelegate: _monthItemGridDelegate, + childrenDelegate: SliverChildListDelegate( + paddedDayItems, + addRepaintBoundaries: false, + ), + ), + ), + const SizedBox(height: _monthItemFooterHeight), + ], + ); + } +} + +/// Determines which style to use to paint the highlight. +enum _HighlightPainterStyle { + /// Paints nothing. + none, + + /// Paints a rectangle that occupies the leading half of the space. + highlightLeading, + + /// Paints a rectangle that occupies the trailing half of the space. + highlightTrailing, + + /// Paints a rectangle that occupies all available space. + highlightAll, +} + +/// This custom painter will add a background highlight to its child. +/// +/// This highlight will be drawn depending on the [style], [color], and +/// [textDirection] supplied. It will either paint a rectangle on the +/// left/right, a full rectangle, or nothing at all. This logic is determined by +/// a combination of the [style] and [textDirection]. +class _HighlightPainter extends CustomPainter { + _HighlightPainter({ + required this.color, + this.style = _HighlightPainterStyle.none, + this.textDirection, + }); + + final Color color; + final _HighlightPainterStyle style; + final TextDirection? textDirection; + + @override + void paint(Canvas canvas, Size size) { + if (style == _HighlightPainterStyle.none) { + return; + } + + final Paint paint = Paint() + ..color = color + ..style = PaintingStyle.fill; + + final Rect rectLeft = Rect.fromLTWH(0, 0, size.width / 2, size.height); + final Rect rectRight = + Rect.fromLTWH(size.width / 2, 0, size.width / 2, size.height); + + switch (style) { + case _HighlightPainterStyle.highlightTrailing: + canvas.drawRect( + textDirection == TextDirection.ltr ? rectRight : rectLeft, + paint, + ); + break; + case _HighlightPainterStyle.highlightLeading: + canvas.drawRect( + textDirection == TextDirection.ltr ? rectLeft : rectRight, + paint, + ); + break; + case _HighlightPainterStyle.highlightAll: + canvas.drawRect( + Rect.fromLTWH(0, 0, size.width, size.height), + paint, + ); + break; + case _HighlightPainterStyle.none: + break; + } + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) => false; +} + +class _InputDateRangePickerDialog extends StatelessWidget { + const _InputDateRangePickerDialog({ + required this.selectedStartDate, + required this.selectedEndDate, + required this.currentDate, + required this.picker, + required this.onConfirm, + required this.onCancel, + required this.confirmText, + required this.cancelText, + required this.helpText, + required this.entryModeButton, + }); + + final DateTime? selectedStartDate; + final DateTime? selectedEndDate; + final DateTime? currentDate; + final Widget picker; + final VoidCallback onConfirm; + final VoidCallback onCancel; + final String? confirmText; + final String? cancelText; + final String? helpText; + final Widget? entryModeButton; + + String _formatDateRange( + BuildContext context, DateTime? start, DateTime? end, DateTime now) { + final MaterialLocalizations localizations = + MaterialLocalizations.of(context); + final String startText = _formatRangeStartDate(localizations, start, end); + final String endText = _formatRangeEndDate(localizations, start, end, now); + if (start == null || end == null) { + return localizations.unspecifiedDateRange; + } + if (Directionality.of(context) == TextDirection.ltr) { + return '$startText – $endText'; + } else { + return '$endText – $startText'; + } + } + + @override + Widget build(BuildContext context) { + final ThemeData theme = Theme.of(context); + final ColorScheme colorScheme = theme.colorScheme; + final MaterialLocalizations localizations = + MaterialLocalizations.of(context); + final Orientation orientation = MediaQuery.of(context).orientation; + final TextTheme textTheme = theme.textTheme; + + final Color onPrimarySurfaceColor = + colorScheme.brightness == Brightness.light + ? colorScheme.onPrimary + : colorScheme.onSurface; + final TextStyle? dateStyle = orientation == Orientation.landscape + ? textTheme.headlineMedium?.apply(color: onPrimarySurfaceColor) + : textTheme.headlineMedium?.apply(color: onPrimarySurfaceColor); + final String dateText = _formatDateRange( + context, selectedStartDate, selectedEndDate, currentDate!); + final String semanticDateText = selectedStartDate != null && + selectedEndDate != null + ? '${localizations.formatMediumDate(selectedStartDate!)} – ${localizations.formatMediumDate(selectedEndDate!)}' + : ''; + + final Widget header = _DatePickerHeader( + helpText: helpText ?? localizations.dateRangePickerHelpText, + titleText: dateText, + titleSemanticsLabel: semanticDateText, + titleStyle: dateStyle, + orientation: orientation, + isShort: orientation == Orientation.landscape, + entryModeButton: entryModeButton, + ); + + final Widget actions = Container( + alignment: AlignmentDirectional.centerEnd, + constraints: const BoxConstraints(minHeight: 52.0), + padding: const EdgeInsets.symmetric(horizontal: 8), + child: OverflowBar( + spacing: 8, + children: [ + TextButton( + onPressed: onCancel, + child: Text(cancelText ?? localizations.cancelButtonLabel), + ), + TextButton( + onPressed: onConfirm, + child: Text(confirmText ?? localizations.okButtonLabel), + ), + ], + ), + ); + + switch (orientation) { + case Orientation.portrait: + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + header, + Expanded(child: picker), + actions, + ], + ); + + case Orientation.landscape: + return Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + header, + Flexible( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Expanded(child: picker), + actions, + ], + ), + ), + ], + ); + } + } +} + +/// Provides a pair of text fields that allow the user to enter the start and +/// end dates that represent a range of dates. +class _InputDateRangePicker extends StatefulWidget { + /// Creates a row with two text fields configured to accept the start and end dates + /// of a date range. + _InputDateRangePicker({ + super.key, + DateTime? initialStartDate, + DateTime? initialEndDate, + required DateTime firstDate, + required DateTime lastDate, + required this.onStartDateChanged, + required this.onEndDateChanged, + this.helpText, + this.errorFormatText, + this.errorInvalidText, + this.errorInvalidRangeText, + this.fieldStartHintText, + this.fieldEndHintText, + this.fieldStartLabelText, + this.fieldEndLabelText, + this.autofocus = false, + this.autovalidate = false, + }) : initialStartDate = initialStartDate == null + ? null + : DateUtils.dateOnly(initialStartDate), + initialEndDate = + initialEndDate == null ? null : DateUtils.dateOnly(initialEndDate), + assert(firstDate != null), + firstDate = DateUtils.dateOnly(firstDate), + assert(lastDate != null), + lastDate = DateUtils.dateOnly(lastDate), + assert(firstDate != null), + assert(lastDate != null), + assert(autofocus != null), + assert(autovalidate != null); + + /// The [DateTime] that represents the start of the initial date range selection. + final DateTime? initialStartDate; + + /// The [DateTime] that represents the end of the initial date range selection. + final DateTime? initialEndDate; + + /// The earliest allowable [DateTime] that the user can select. + final DateTime firstDate; + + /// The latest allowable [DateTime] that the user can select. + final DateTime lastDate; + + /// Called when the user changes the start date of the selected range. + final ValueChanged? onStartDateChanged; + + /// Called when the user changes the end date of the selected range. + final ValueChanged? onEndDateChanged; + + /// The text that is displayed at the top of the header. + /// + /// This is used to indicate to the user what they are selecting a date for. + final String? helpText; + + /// Error text used to indicate the text in a field is not a valid date. + final String? errorFormatText; + + /// Error text used to indicate the date in a field is not in the valid range + /// of [firstDate] - [lastDate]. + final String? errorInvalidText; + + /// Error text used to indicate the dates given don't form a valid date + /// range (i.e. the start date is after the end date). + final String? errorInvalidRangeText; + + /// Hint text shown when the start date field is empty. + final String? fieldStartHintText; + + /// Hint text shown when the end date field is empty. + final String? fieldEndHintText; + + /// Label used for the start date field. + final String? fieldStartLabelText; + + /// Label used for the end date field. + final String? fieldEndLabelText; + + /// {@macro flutter.widgets.editableText.autofocus} + final bool autofocus; + + /// If true, this the date fields will validate and update their error text + /// immediately after every change. Otherwise, you must call + /// [_InputDateRangePickerState.validate] to validate. + final bool autovalidate; + + @override + _InputDateRangePickerState createState() => _InputDateRangePickerState(); +} + +/// The current state of an [_InputDateRangePicker]. Can be used to +/// [validate] the date field entries. +class _InputDateRangePickerState extends State<_InputDateRangePicker> { + late String _startInputText; + late String _endInputText; + DateTime? _startDate; + DateTime? _endDate; + late TextEditingController _startController; + late TextEditingController _endController; + String? _startErrorText; + String? _endErrorText; + bool _autoSelected = false; + + @override + void initState() { + super.initState(); + _startDate = widget.initialStartDate; + _startController = TextEditingController(); + _endDate = widget.initialEndDate; + _endController = TextEditingController(); + } + + @override + void dispose() { + _startController.dispose(); + _endController.dispose(); + super.dispose(); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + final MaterialLocalizations localizations = + MaterialLocalizations.of(context); + if (_startDate != null) { + _startInputText = localizations.formatCompactDate(_startDate!); + final bool selectText = widget.autofocus && !_autoSelected; + _updateController(_startController, _startInputText, selectText); + _autoSelected = selectText; + } + + if (_endDate != null) { + _endInputText = localizations.formatCompactDate(_endDate!); + _updateController(_endController, _endInputText, false); + } + } + + /// Validates that the text in the start and end fields represent a valid + /// date range. + /// + /// Will return true if the range is valid. If not, it will + /// return false and display an appropriate error message under one of the + /// text fields. + bool validate() { + String? startError = _validateDate(_startDate); + final String? endError = _validateDate(_endDate); + if (startError == null && endError == null) { + if (_startDate!.isAfter(_endDate!)) { + startError = widget.errorInvalidRangeText ?? + MaterialLocalizations.of(context).invalidDateRangeLabel; + } + } + setState(() { + _startErrorText = startError; + _endErrorText = endError; + }); + return startError == null && endError == null; + } + + DateTime? _parseDate(String? text) { + final MaterialLocalizations localizations = + MaterialLocalizations.of(context); + return localizations.parseCompactDate(text); + } + + String? _validateDate(DateTime? date) { + if (date == null) { + return widget.errorFormatText ?? + MaterialLocalizations.of(context).invalidDateFormatLabel; + } else if (date.isBefore(widget.firstDate) || + date.isAfter(widget.lastDate)) { + return widget.errorInvalidText ?? + MaterialLocalizations.of(context).dateOutOfRangeLabel; + } + return null; + } + + void _updateController( + TextEditingController controller, String text, bool selectText) { + TextEditingValue textEditingValue = controller.value.copyWith(text: text); + if (selectText) { + textEditingValue = textEditingValue.copyWith( + selection: TextSelection( + baseOffset: 0, + extentOffset: text.length, + )); + } + controller.value = textEditingValue; + } + + void _handleStartChanged(String text) { + setState(() { + _startInputText = text; + _startDate = _parseDate(text); + widget.onStartDateChanged?.call(_startDate); + }); + if (widget.autovalidate) { + validate(); + } + } + + void _handleEndChanged(String text) { + setState(() { + _endInputText = text; + _endDate = _parseDate(text); + widget.onEndDateChanged?.call(_endDate); + }); + if (widget.autovalidate) { + validate(); + } + } + + @override + Widget build(BuildContext context) { + final MaterialLocalizations localizations = + MaterialLocalizations.of(context); + final InputDecorationThemeData inputTheme = + Theme.of(context).inputDecorationTheme; + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: TextField( + controller: _startController, + decoration: InputDecoration( + border: inputTheme.border ?? const UnderlineInputBorder(), + filled: inputTheme.filled, + hintText: widget.fieldStartHintText ?? localizations.dateHelpText, + labelText: widget.fieldStartLabelText ?? + localizations.dateRangeStartLabel, + errorText: _startErrorText, + ), + keyboardType: TextInputType.datetime, + onChanged: _handleStartChanged, + autofocus: widget.autofocus, + ), + ), + const SizedBox(width: 8), + Expanded( + child: TextField( + controller: _endController, + decoration: InputDecoration( + border: inputTheme.border ?? const UnderlineInputBorder(), + filled: inputTheme.filled, + hintText: widget.fieldEndHintText ?? localizations.dateHelpText, + labelText: + widget.fieldEndLabelText ?? localizations.dateRangeEndLabel, + errorText: _endErrorText, + ), + keyboardType: TextInputType.datetime, + onChanged: _handleEndChanged, + ), + ), + ], + ); + } +} diff --git a/packages/components/lib/project_ui/default/empty_search_page.dart b/modules/basic_system/components/lib/project_ui/default/empty_search_page.dart similarity index 87% rename from packages/components/lib/project_ui/default/empty_search_page.dart rename to modules/basic_system/components/lib/project_ui/default/empty_search_page.dart index f003dfe9a..de90b8d74 100644 --- a/packages/components/lib/project_ui/default/empty_search_page.dart +++ b/modules/basic_system/components/lib/project_ui/default/empty_search_page.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; class NotSearchPage extends StatelessWidget { - - const NotSearchPage({Key? key}):super(key: key); + final String tips; + const NotSearchPage({Key? key, required this.tips}):super(key: key); @override Widget build(BuildContext context) { @@ -18,7 +18,7 @@ class NotSearchPage extends StatelessWidget { Container( padding: const EdgeInsets.only(top: 16.0), child: Text( - "哥们,搜点啥...≧◔◡◔≦", + tips, style: TextStyle( fontSize: 20, color: color, diff --git a/packages/components/lib/project_ui/default/empty_shower.dart b/modules/basic_system/components/lib/project_ui/default/empty_shower.dart similarity index 100% rename from packages/components/lib/project_ui/default/empty_shower.dart rename to modules/basic_system/components/lib/project_ui/default/empty_shower.dart diff --git a/packages/components/lib/project_ui/default/error_page.dart b/modules/basic_system/components/lib/project_ui/default/error_page.dart similarity index 99% rename from packages/components/lib/project_ui/default/error_page.dart rename to modules/basic_system/components/lib/project_ui/default/error_page.dart index 49c2b0026..ceaa62137 100644 --- a/packages/components/lib/project_ui/default/error_page.dart +++ b/modules/basic_system/components/lib/project_ui/default/error_page.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; - class ErrorPage extends StatelessWidget { const ErrorPage({Key? key}):super(key: key); diff --git a/packages/components/lib/project_ui/default/error_shower.dart b/modules/basic_system/components/lib/project_ui/default/error_shower.dart similarity index 96% rename from packages/components/lib/project_ui/default/error_shower.dart rename to modules/basic_system/components/lib/project_ui/default/error_shower.dart index 30165bf37..a13760c0e 100644 --- a/packages/components/lib/project_ui/default/error_shower.dart +++ b/modules/basic_system/components/lib/project_ui/default/error_shower.dart @@ -29,7 +29,7 @@ class ErrorShower extends StatelessWidget { )), Text( error, - style:TextStyle( + style:const TextStyle( color: Colors.red, fontSize: 18, ), textAlign: TextAlign.center, diff --git a/packages/components/lib/project_ui/default/loading_shower.dart b/modules/basic_system/components/lib/project_ui/default/loading_shower.dart similarity index 100% rename from packages/components/lib/project_ui/default/loading_shower.dart rename to modules/basic_system/components/lib/project_ui/default/loading_shower.dart diff --git a/packages/components/lib/project_ui/default/no_more_widget.dart b/modules/basic_system/components/lib/project_ui/default/no_more_widget.dart similarity index 100% rename from packages/components/lib/project_ui/default/no_more_widget.dart rename to modules/basic_system/components/lib/project_ui/default/no_more_widget.dart diff --git a/modules/basic_system/components/lib/project_ui/project_ui.dart b/modules/basic_system/components/lib/project_ui/project_ui.dart new file mode 100644 index 000000000..b27f9641c --- /dev/null +++ b/modules/basic_system/components/lib/project_ui/project_ui.dart @@ -0,0 +1,13 @@ +export 'default/empty_search_page.dart'; +export 'default/empty_shower.dart'; +export 'default/error_shower.dart'; +export 'default/loading_shower.dart'; +export 'default/error_page.dart'; +export 'default/no_more_widget.dart'; +export 'wrapper/honour_wrapper.dart'; +export 'unit_app_bar.dart'; +export 'top_bar/desk_simple_top_bar.dart'; +export 'top_bar/desk_tab_top_bar.dart'; +export 'top_bar/desk_knowledge_top_bar.dart'; +export 'time_line/flutter_unit_time_line.dart'; +export 'refresh/refresh.dart'; \ No newline at end of file diff --git a/modules/basic_system/components/lib/project_ui/refresh/refresh.dart b/modules/basic_system/components/lib/project_ui/refresh/refresh.dart new file mode 100644 index 000000000..27edcb913 --- /dev/null +++ b/modules/basic_system/components/lib/project_ui/refresh/refresh.dart @@ -0,0 +1,2 @@ +export 'refresh_config_wrapper.dart'; +export 'package:tolyui_refresh/tolyui_refresh.dart' show TolyRefresh,RefreshController; \ No newline at end of file diff --git a/modules/basic_system/components/lib/project_ui/refresh/refresh_config_wrapper.dart b/modules/basic_system/components/lib/project_ui/refresh/refresh_config_wrapper.dart new file mode 100644 index 000000000..d9ffba556 --- /dev/null +++ b/modules/basic_system/components/lib/project_ui/refresh/refresh_config_wrapper.dart @@ -0,0 +1,57 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:tolyui_refresh/tolyui_refresh.dart'; + +import 'toly_refresh_indicator.dart'; + +class RefreshConfigWrapper extends StatelessWidget { + final Widget child; + const RefreshConfigWrapper({Key? key, required this.child}) : super(key: key); + + @override + Widget build(BuildContext context) { + Color themeColor = Theme.of(context).primaryColor; + return RefreshConfigScope( + headerTriggerDistance: 60, + topHitBoundary: 20, + child: child, + springDescription: SpringDescription.withDampingRatio( + mass: 0.5, + stiffness: 100.0, + ratio: 1.1, + ), + headerBuilder: () => const TolyRefreshIndicator(), + footerBuilder: () => CustomFooter( + builder: (BuildContext context, LoadStatus? mode) { + Widget body; + if (mode == LoadStatus.idle) { + body = Wrap( + alignment: WrapAlignment.center, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + Icon(Icons.arrow_upward, color: themeColor), + Text("上拉加载", style: TextStyle(color: themeColor, height: 1)), + ], + ); + } else if (mode == LoadStatus.loading) { + body = CupertinoActivityIndicator(); + } else if (mode == LoadStatus.failed) { + body = Text("加载失败!点击重试!"); + } else if (mode == LoadStatus.canLoading) { + body = Text("松手,加载更多!", + style: TextStyle(color: themeColor, height: 1)); + } else { + body = Text("没有更多数据了!", + style: TextStyle( + color: Colors.grey, + )); + } + return Container( + height: 55.0, + child: Center(child: body), + ); + }, + ), + ); + } +} diff --git a/packages/artifact/lib/src/views/toly_refresh_indicator.dart b/modules/basic_system/components/lib/project_ui/refresh/toly_refresh_indicator.dart similarity index 87% rename from packages/artifact/lib/src/views/toly_refresh_indicator.dart rename to modules/basic_system/components/lib/project_ui/refresh/toly_refresh_indicator.dart index d44ac625f..cd239d439 100644 --- a/packages/artifact/lib/src/views/toly_refresh_indicator.dart +++ b/modules/basic_system/components/lib/project_ui/refresh/toly_refresh_indicator.dart @@ -4,7 +4,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart' hide RefreshIndicator, RefreshIndicatorState; import 'package:flutter/services.dart'; -import 'package:refresh/refresh.dart'; +import 'package:tolyui_refresh/tolyui_refresh.dart'; class TolyRefreshIndicator extends RefreshIndicator { const TolyRefreshIndicator({super.key}); @@ -22,8 +22,8 @@ class _TolyRefreshIndicatorState @override void initState() { super.initState(); - _iconRotateCtrl = - AnimationController(vsync: this, duration: const Duration(milliseconds: 250)); + _iconRotateCtrl = AnimationController( + vsync: this, duration: const Duration(milliseconds: 250)); rotateAnima = Tween(begin: 0.0, end: -0.5).animate(_iconRotateCtrl); } @@ -63,8 +63,13 @@ class _TolyRefreshIndicatorState @override Widget buildContent(BuildContext context, RefreshStatus mode) { Widget child = switch (mode) { - RefreshStatus.refreshing => const CupertinoActivityIndicator(radius: 10,), - RefreshStatus.completed => const Icon(Icons.check,color: Colors.green,), + RefreshStatus.refreshing => const CupertinoActivityIndicator( + radius: 10, + ), + RefreshStatus.completed => const Icon( + Icons.check, + color: Colors.green, + ), RefreshStatus.idle || RefreshStatus.canRefresh => RotationTransition( turns: rotateAnima, child: Icon( diff --git a/lib/app/views/time_line/flutter_unit_time_line.dart b/modules/basic_system/components/lib/project_ui/time_line/flutter_unit_time_line.dart similarity index 99% rename from lib/app/views/time_line/flutter_unit_time_line.dart rename to modules/basic_system/components/lib/project_ui/time_line/flutter_unit_time_line.dart index 3d85af34b..eedef00a7 100644 --- a/lib/app/views/time_line/flutter_unit_time_line.dart +++ b/modules/basic_system/components/lib/project_ui/time_line/flutter_unit_time_line.dart @@ -174,7 +174,7 @@ class TimeLineNode extends StatelessWidget { @override Widget build(BuildContext context) { - Color themeColor = BlocProvider.of(context).state.themeColor; + Color themeColor = BlocProvider.of(context).state.themeColor.color; return IntrinsicHeight( child: Row( diff --git a/lib/app/views/time_line/model/time_node.dart b/modules/basic_system/components/lib/project_ui/time_line/model/time_node.dart similarity index 100% rename from lib/app/views/time_line/model/time_node.dart rename to modules/basic_system/components/lib/project_ui/time_line/model/time_node.dart diff --git a/modules/basic_system/components/lib/project_ui/top_bar/desk_account_top_bar.dart b/modules/basic_system/components/lib/project_ui/top_bar/desk_account_top_bar.dart new file mode 100644 index 000000000..4f562adbd --- /dev/null +++ b/modules/basic_system/components/lib/project_ui/top_bar/desk_account_top_bar.dart @@ -0,0 +1,33 @@ +import 'package:app/app.dart'; +import 'package:flutter/material.dart'; + +class DeskAccountTopBar extends StatelessWidget { + final Widget? leading; + + const DeskAccountTopBar({super.key, this.leading}); + + @override + Widget build(BuildContext context) { + bool isDark = Theme.of(context).brightness == Brightness.dark; + + return DragToMoveWrapper( + child: Container( + height: 64, + color: isDark ? Color(0xff2C3036) : Colors.white, + child: Row( + children: [ + if (leading != null) Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: leading!, + ), + const Spacer(), + const SizedBox( + width: 20, + ), + const WindowButtons(), + ], + ), + ), + ); + } +} diff --git a/modules/basic_system/components/lib/project_ui/top_bar/desk_knowledge_top_bar.dart b/modules/basic_system/components/lib/project_ui/top_bar/desk_knowledge_top_bar.dart new file mode 100644 index 000000000..5cd2f5712 --- /dev/null +++ b/modules/basic_system/components/lib/project_ui/top_bar/desk_knowledge_top_bar.dart @@ -0,0 +1,90 @@ +import 'package:app/app.dart'; +import 'package:toly_ui/toly_ui.dart'; +import 'package:flutter/material.dart'; + +class DeskKnowledgeTabTopBar extends StatefulWidget { + final List tabs; + final ValueChanged onTabPressed; + + const DeskKnowledgeTabTopBar( + {Key? key, required this.onTabPressed, required this.tabs}) + : super(key: key); + + @override + State createState() => _DeskKnowledgeTabTopBarState(); +} + +class _DeskKnowledgeTabTopBarState extends State + with TickerProviderStateMixin { + late TabController tabController; + + @override + void initState() { + super.initState(); + tabController = TabController(length: widget.tabs.length, vsync: this); + } + + @override + void didUpdateWidget(covariant DeskKnowledgeTabTopBar oldWidget) { + if (oldWidget.tabs.length != widget.tabs.length) { + tabController.dispose(); + tabController = TabController(length: widget.tabs.length, vsync: this); + } + super.didUpdateWidget(oldWidget); + } + + @override + Widget build(BuildContext context) { + Color themeColor = Theme.of(context).primaryColor; + + bool isDark = Theme.of(context).brightness == Brightness.dark; + + return DragToMoveWrapper( + child: Stack( + children: [ + Container( + height: 64, + color: isDark ? const Color(0xff2C3036) : Colors.white, + child: Row( + children: [ + Expanded( + child: Center( + child: SizedBox( + width: 400, + child: TabBar( + onTap: widget.onTabPressed, + indicatorSize: TabBarIndicatorSize.label, + labelPadding: const EdgeInsets.symmetric(horizontal: 6), + isScrollable: false, + indicator: RoundRectTabIndicator( + borderSide: BorderSide(color: themeColor, width: 3), + ), + labelStyle: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + ), + controller: tabController, + labelColor: themeColor, + indicatorWeight: 3, + unselectedLabelColor: Colors.grey, + indicatorColor: themeColor, + tabs: widget.tabs + .map((String name) => Tab(text: name)) + .toList(), + ), + ), + ), + ), + const SizedBox( + width: 20, + ), + // WindowButtons(), + ], + ), + ), + const Positioned(child: WindowButtons()) + ], + ), + ); + } +} diff --git a/modules/basic_system/components/lib/project_ui/top_bar/desk_simple_top_bar.dart b/modules/basic_system/components/lib/project_ui/top_bar/desk_simple_top_bar.dart new file mode 100644 index 000000000..5b173b0f0 --- /dev/null +++ b/modules/basic_system/components/lib/project_ui/top_bar/desk_simple_top_bar.dart @@ -0,0 +1,33 @@ +import 'package:app/app.dart'; +import 'package:flutter/material.dart'; + +class SimpleDeskTopBar extends StatelessWidget { + final Widget? leading; + + const SimpleDeskTopBar({super.key, this.leading}); + + @override + Widget build(BuildContext context) { + bool isDark = Theme.of(context).brightness == Brightness.dark; + + return DragToMoveWrapper( + child: Container( + height: 64, + color: isDark ? const Color(0xff2C3036) : Colors.white, + child: Row( + children: [ + if (leading != null) Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: leading!, + ), + const Spacer(), + const SizedBox( + width: 20, + ), + const WindowButtons(), + ], + ), + ), + ); + } +} diff --git a/lib/components/top_bar/desk_tab_top_bar.dart b/modules/basic_system/components/lib/project_ui/top_bar/desk_tab_top_bar.dart similarity index 89% rename from lib/components/top_bar/desk_tab_top_bar.dart rename to modules/basic_system/components/lib/project_ui/top_bar/desk_tab_top_bar.dart index 3b798c157..8c05408f6 100644 --- a/lib/components/top_bar/desk_tab_top_bar.dart +++ b/modules/basic_system/components/lib/project_ui/top_bar/desk_tab_top_bar.dart @@ -1,5 +1,5 @@ import 'package:app/app.dart'; -import 'package:components/toly_ui/toly_ui.dart'; +import 'package:toly_ui/toly_ui.dart'; import 'package:flutter/material.dart'; @@ -38,13 +38,14 @@ class _DeskTabTopBarState extends State with TickerProviderState bool isDark = Theme.of(context).brightness == Brightness.dark; - return DragToMoveAreaNoDouble( + return DragToMoveWrapper( child: Container( - padding: const EdgeInsets.symmetric(horizontal: 20), height: 64, - color: isDark?Color(0xff2C3036):Colors.white, + color: isDark?const Color(0xff2C3036):Colors.white, child: Row( children: [ + const SizedBox(width: 12,), + const BackButton(), SizedBox( width: 350, child: TabBar( @@ -67,9 +68,9 @@ class _DeskTabTopBarState extends State with TickerProviderState tabs: widget.tabs.map((String name) => Tab(text: name)).toList(), ), ), - Spacer(), + const Spacer(), const SizedBox(width: 20,), - WindowButtons(), + const WindowButtons(), ], ), ), diff --git a/packages/components/lib/project_ui/unit_app_bar.dart b/modules/basic_system/components/lib/project_ui/unit_app_bar.dart similarity index 100% rename from packages/components/lib/project_ui/unit_app_bar.dart rename to modules/basic_system/components/lib/project_ui/unit_app_bar.dart diff --git a/packages/components/lib/project_ui/wrapper/honour_wrapper.dart b/modules/basic_system/components/lib/project_ui/wrapper/honour_wrapper.dart similarity index 100% rename from packages/components/lib/project_ui/wrapper/honour_wrapper.dart rename to modules/basic_system/components/lib/project_ui/wrapper/honour_wrapper.dart diff --git a/modules/basic_system/components/pubspec.yaml b/modules/basic_system/components/pubspec.yaml new file mode 100644 index 000000000..b529d1284 --- /dev/null +++ b/modules/basic_system/components/pubspec.yaml @@ -0,0 +1,52 @@ +name: components +description: A new Flutter package project. +version: 0.0.1 +homepage: +publish_to: none + +environment: + sdk: ">=3.5.0 <4.0.0" + flutter: ">=1.17.0" + +resolution: workspace + +dependencies: + flutter: + sdk: flutter + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # To add assets to your package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # To add custom fonts to your package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/components/test/components_test.dart b/modules/basic_system/components/test/components_test.dart similarity index 100% rename from packages/components/test/components_test.dart rename to modules/basic_system/components/test/components_test.dart diff --git a/modules/basic_system/l10n/.gitignore b/modules/basic_system/l10n/.gitignore new file mode 100644 index 000000000..ac5aa9893 --- /dev/null +++ b/modules/basic_system/l10n/.gitignore @@ -0,0 +1,29 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +build/ diff --git a/modules/basic_system/l10n/.metadata b/modules/basic_system/l10n/.metadata new file mode 100644 index 000000000..b2c661a57 --- /dev/null +++ b/modules/basic_system/l10n/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "67457e669f79e9f8d13d7a68fe09775fefbb79f4" + channel: "stable" + +project_type: package diff --git a/packages/authentication/CHANGELOG.md b/modules/basic_system/l10n/CHANGELOG.md similarity index 100% rename from packages/authentication/CHANGELOG.md rename to modules/basic_system/l10n/CHANGELOG.md diff --git a/packages/authentication/LICENSE b/modules/basic_system/l10n/LICENSE similarity index 100% rename from packages/authentication/LICENSE rename to modules/basic_system/l10n/LICENSE diff --git a/packages/authentication/README.md b/modules/basic_system/l10n/README.md similarity index 100% rename from packages/authentication/README.md rename to modules/basic_system/l10n/README.md diff --git a/packages/authentication/analysis_options.yaml b/modules/basic_system/l10n/analysis_options.yaml similarity index 100% rename from packages/authentication/analysis_options.yaml rename to modules/basic_system/l10n/analysis_options.yaml diff --git a/modules/basic_system/l10n/desiredFileName.txt b/modules/basic_system/l10n/desiredFileName.txt new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/modules/basic_system/l10n/desiredFileName.txt @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/modules/basic_system/l10n/l10n.yaml b/modules/basic_system/l10n/l10n.yaml new file mode 100644 index 000000000..3419d52f3 --- /dev/null +++ b/modules/basic_system/l10n/l10n.yaml @@ -0,0 +1,10 @@ +arb-dir: lib/arb +template-arb-file: app_zh.arb +output-localization-file: app_localizations.dart + + +synthetic-package: false +output-dir: lib/gen_l10n +output-class: AppLocalizations +nullable-getter: false +untranslated-messages-file: desiredFileName.txt \ No newline at end of file diff --git a/modules/basic_system/l10n/l10n_copy.sh b/modules/basic_system/l10n/l10n_copy.sh new file mode 100644 index 000000000..7f95dd96a --- /dev/null +++ b/modules/basic_system/l10n/l10n_copy.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +# 复制文件夹 +cp -r .dart_tool/flutter_gen/gen_l10n lib + +# 检查拷贝是否成功 +if [ $? -eq 0 ]; then + echo "文件夹拷贝成功" +else + echo "文件夹拷贝失败" +fi \ No newline at end of file diff --git a/modules/basic_system/l10n/lib/arb/app_en.arb b/modules/basic_system/l10n/lib/arb/app_en.arb new file mode 100644 index 000000000..63d25fa67 --- /dev/null +++ b/modules/basic_system/l10n/lib/arb/app_en.arb @@ -0,0 +1,199 @@ +{ + "widgetCollection": "Widgets", + "paintCollection": "Painter", + "knowledgeCollection": "Knowledge", + + "homeAccount": "Application", + + "homeAccountTabInfo": "About App", + "homeAccountTabMe": "Contact Me", + "homeAccountSupport": "Support Project", + + "collectCollection": "Collection", + "essentialCollection": "KeypointsCollection", + "treasureTools": "Treasure", + "searchWidget": "search widget ...", + "stateless":"Stateless", + "stateful":"Stateful", + "single":"Single", + "multi":"Multi", + "sliver":"Sliver", + "proxy":"Proxy", + "other":"Other", + "homeTabWidget": "Widget", + "homeTabPaint": "Paint", + "homeTabKnowledge": "Knowledge", + "homeTabTools": "Treasure", + "homeTabMine": "Mine", + "favorite":"Collected", + + "userCollection":"Collection", + + + "appSettings":"Application Setting", + "darkMode":"Dark Mode ", + "themeColorSetting":"Theme Color", + "fontSetting":"Font Setting", + "settingLanguage": "Language Setting", + "codeHighlightStyle":"Code Highlight Style", + "versionInformation":"App Version", + "showFloatingTools":"Show floating tools", + "displayPerformanceFloatingLayer":"Performance Layer", + + "showBackground":"Display Background", + + "followSystem":"Follow system", + "manualSetting":"Manual settings", + "lightMode":"Light mode", + "appDetails":"Application details", + + "settingLanguageText": "Setting Language", + + "checkUpdate":"Check New Version", + "downloadNewVersion":"Download New Version", + "downloadingNewVersion":"Downloading New Version ...", + "currentIsNew":"There is the latest version of FlutterUnit!", + + "enterComponentName":"Input widget name", + "containerComponents":"Container components", + "relatedComponents":"Related Widgets", + "componentTavern":"Component Tavern", + "cherishedComponents":"Treasure components", + "textImageCollection":"TextImageCollection", + "layoutCollection":"LayoutCollection", + "eventCollection":"EventCollection", + "animationCollection":"AnimationCollection", + "slidingCollection":"SlidingCollection", + "decorationCollection":"DecorativeCollection", + "assemblyCollection":"AssemblyCollection", + "functionCollection":"FeatureCollection", + "popupCollection":"Pop upCollection", + "themeCollection":"ThemeCollection", + "derivativeCollection":"DerivativeCollection", + "hardToCategorize":"It's hard to distinguish", + "basicDrawing":"Basic drawing", + "animationGesture":"Animated gestures", + "particleDrawing":"Particle drawing", + "interestingDrawing":"Fun drawing", + "artGallery":"Art galleries ", + "drawingOfImages":"This example explains how to draw images: by loading images and drawing image resources to a specified area. Draw a batch of 45 \"angled grid lines on the upper layer to practice drawing the lines ", + "digitalDisplayTube":"This example introduces how to draw LED digital display tubes to practice the use, transformation, combination of path paths, and knowledge of component packaging. It is a very good drawing case ", + "pathDrawing":"This example introduces how to perform simple path drawing, rotate the drawing board, and combine animation to make the windmill rotate. This is a very concise case of combining drawing and animation. ", + "gridCoordinateSystem":"This example explains how to use line diameter and text to draw a grid coordinate system, and encapsulate the drawn objects for easy reuse. The coordinate system also provides reference during drawing, which is essential for beginners.", + "polarCoordinateSystemOfFaces":"This example explains how to use a polar coordinate system to draw a plane and collect polar coordinates based on a function equation for drawing. ", + "drawFunctionCurvesForPathPairs":"This example explains how to use a path to draw a function curve, fitting a small number of points on the function curve through a Bessel curve. ", + "drawRegularPolygons":"This example introduces how to collect points in a circle and draw regular polygons, which is a good example for practicing drawing and forming paths. \n Special operations:+, - Modify the number of edges", + "randomNumberProcessing":"This example introduces drawing rectangles and handling random numbers. Determine the rectangular position information through a set of points and draw it. Can practice the ability to control data.", + "clockDrawing":"This example uses the drawing of a clock to practice the drawing technique of rotating scale types in Flutter, and uses animation to rotate the dial pointer.", + "drawSprings":" This example introduces how to draw a spring, stretch and compress it vertically through the contact points, and restore the animation when releasing it. It is a good comprehensive small case. Special operation: Drag the telescopic spring up and down ", + "theApplicationOfAnglesInDrawing":"This example explains how to perform rotational motion based on a point as the center. Learn the application of the angle between two points in drawing. \n Special operation: Click to run", + "usingShadersAndFilters":"This example explains how to use shaders and filters in painting, and achieve a rotating streamer effect through animation with numerical variations.", + "pathDrawingFunctionCurve":"This example explains how to use path to draw function curves and use path measurement for animation", + "thePathOfBingDwenDwen":"This sample will draw the path of the mascot Bing Dwen Dwen for the 2022 Beijing Winter Olympics and use path measurement for animation. \n Special operation: Click to run", + "drawCubicBesselCurve":"This example introduces how to draw a cubic Bezier curve, determine whether a point is activated through the contacts, and use this to control the position of the point to achieve drag control effect. \n Special operation: Click on the drawing point, double-click to clear it", + "theEffectOfAnimationCurve":"This example provides an intuitive way to examine the effect of animation curves, allowing everyone to have a deeper understanding of animation. \n Special operation: Click to run", + "randomParticlesAndBoundaryBouncing":"This example introduces how to create random particles and handle boundary bounce logic, which is a great starting point for learning particle motion. Special operation: click to stop running ", + "particleCollision":"This example introduces how to perform collision detection on a particle and split multiple particles, which is an interesting case. \n Special operation: Click Reset", + "particle":"This example introduces using particles to represent images and animating them to achieve explosive effects. \nSpecial operation: Click to run", + "rectangleAndRandomNumbers":"This example introduces drawing rectangles and handling random numbers. Determine the rectangular position information through a set of points and draw it. Can practice the ability to control data. \nSpecial operation: Click to randomly generate", + "bingDwenDwen":"This example is to draw the shape of the mascot Bing Dwen Dwen for the 2022 Beijing Winter Olympics, from which you can learn knowledge such as path drawing and gradient colors.", + "pufengInjectionTest":"This sample implements the testing process of the Pufeng needle injection test, estimating pi based on probability. You can learn some drawing tips and logical processing of data.", + "ticTacToe":"This example combines important skills such as gestures, drawing, animation, and verification through the drawing and logical verification of the Chinese checkerboard, making it a very good case study. \n Special operation: Double click to reset", + "tiledLines":"The root cause of this example comes from generateArchistry.com tiled-lines,Implemented by xrr 2016 using Flutter。Warehouse address:flutter-generative-artistry", + "joyDivision":"The root cause of this example comes from generateArchistry.com joy-division,Implemented by xrr 2016 using Flutter。Warehouse address:flutter-generative-artistry", + "cubicDisarray":"The root cause of this example comes from generateArchistry.com cubic-disarray,Implemented by xrr 2016 using Flutter。Warehouse address:flutter-generative-artistry", + "triangularMesh":"The root cause of this example comes from generateArchistry.com triangular-mesh,Implemented by xrr 2016 using Flutter。Warehouse address:flutter-generative-artistry", + "unDeuxTrois": "The root cause of this example comes from generateArchistry.com un-deux-trois,Implemented by xrr 2016 using Flutter。Warehouse address:flutter-generative-artistry", + "circlePacking":"The root cause of this example comes from generateArchistry.com circle-packing,Implemented by xrr 2016 using Flutter。Warehouse address:flutter-generative-artistry", + "hypnoticSquares":"The root cause of this example comes from generateArchistry.com hypnotic-squares,Implemented by xrr 2016 using Flutter。Warehouse address:flutter-generative-artistry", + "pietMondrian":"The root cause of this example comes from generateArchistry.com piet-mondrian,Implemented by xrr 2016 using Flutter。Warehouse address:flutter-generative-artistry", + "downloadCompressedPackage":"Usage: \n1. Select the icon in iconfont.cn, add the project, and download the compressed file. \n2. Select the Flutter project address, configure resource and product file locations. \n3.Click the Generate Code button to generate the relevant code.", + "qAIssues":"The QA data in the key points collection is included in FlutterUnit's issues labeled with points. If data needs to be provided, simply ask and answer in the issues section.", + "tips":"tips:", + "visualSorting":"Visual sorting", + "visual":"Visual sorting", + "insertion": "Insert sorting", + "bubble": "Bubble sorting", + "cocktail": "Cocktail sorting (bidirectional bubble sorting)", + "comb": "Comb sorting", + "pigeonHole": "Pigeonhole sorting ", + "shell": "Shell sorting ", + "selection": "Select sorting", + "gnome": "Dwarf Sorting", + "cycle": "Circular sorting", + "heap": "Heap sorting", + "quick": "Quick sorting", + "merge": "Merge sorting", + "sortingAlgorithmConfiguration":"Sorting algorithm configuration", + "dataCount":"Data quantity (number)", + "timeInterval":"Time interval (microseconds)", + "randomSeed":"Random Seed", + "codeGeneration":"Code generation", + "generateCode":"Generate Code", + "artifactLocation":"Product location", + "codeClassLocation":"Code class storage location", + "resourceDirectory":"Resource Catalog", + "iconfontResourceLocation":"iconfont Resource storage location", + "projectPath":"Project Path", + "inputProjectAddress":"Please select or enter the project address", + "iconfontCompressedPackagePath":"Iconfont Compressed package path", + "pleaseSelectOrInputIconfontCompressedPackagePath":"Please select or enter the compressed file path for iconfont download", + "stayTuned":"Stay tuned", + "iconFont":"IconFont", + "dataClass":"Data class", + "stateManagement":"State management", + "jsonParsing":"Json Parsing", + "clickHereToJump":"Click here to jump to", + "knowledgeTabToly":"Toly Articles", + "knowledgeTabAlgo":"Algo Player", + "knowledgeTabLayout":"Layout Treasury", + "knowledgeTabPoint":"Key Points", + "knowledgeConstruction":"In Construction", + "knowledgeToJuejin":"To Juejin", + "srcPath":"Source Path", + "widgetsInn":"Widgets Inn", + "likedWidgets":"Liked Widgets", + + "afterOpeningWillFollowTheSystemToOpenOrCloseDarkMode":"After activation, it will follow the system to turn on or off dark mode", + "basicDrawingDesc":"Including some basic graphics drawing examples would be very friendly to beginners in programming. Through these examples, one can learn how to draw basic shapes such as points, lines, rectangles, circles, arcs, text, images, etc., and understand the usage of core objects in drawing like Canvas, Paint, Path, etc.", + "animationGestureDesc":"Includes some drawing examples of animation and gestures, which make drawing more interactive. Through these examples, one can learn the usage of animation and gestures, such as sliding, rotating, scaling, moving effects, etc., making drawing not just static presentation.", + "particleDrawingDesc": "Includes some drawing examples related to particles, which are top-level operations in drawing. Through these examples, one can learn how to use particles to create stunning visual effects, such as particle clocks, particle explosions, particle backgrounds, etc., giving drawing endless possibilities.", + "interestingDrawingDesc": "Includes some fun drawing examples, let's experience the joy of drawing, programming, and intelligence together here.", + "artGalleryDesc": "Includes some hall-level drawing examples, which are pinnacle works of drawing. They have no practicality and are not born for any demand. They exist only because they exist, serving as a medium for human wisdom and expression, called art.", + "checkDatabaseNewVersion":"Check for new versions of the database", + "viewThisProjectGithubRepository":"《View the Github Repository for this project》", + "dataManagement":"Data management", + "backupFavoritesCollectionData":"Backup Collection Data", + "syncFavoritesCollectionData":"Synchronize collection data", + "favoritesCollectionDataReset":"Reset Collection Data", + "resetSuccess":"Reset successful!", + "dataSetBackupSuccess":"Dataset backup successful!", + "dataSetBackupFailure":"Dataset backup failed!", + "dataSynchronizationCopySuccess":"Data synchronization successful!", + "dataSynchronizationCopyFailure":"Data synchronization failed!", + "destructionRed":"Destruction Red ", + "rageOrange":"Anger Orange", + "warningYellow":"Warning Yellow", + "camouflageGreen":"Disguising Green", + "coldBlue":"Indifferent Blue", + "infiniteBlue":"Infinite Indigo", + "mysteryPurple":"Mysterious Purple", + "destinyBlack":"Destiny Black", + + "toly":"toly", + "dartHandbook":"Dart Handbook", + "aboutApplications":"About Applications", + "contactThisKing":"Contact this king", + "codeCopiedSuccessfully":"Code copied successfully", + "favoriteFolderManagement":"Favorite folder management", + "assembly":"Assembly", + "draw":"Draw", + "knowledge":"Knowledge", + "collection":"Collection", + "my":"My", + "picture":"pics", + "widgetInn":"Widget Collection", + "emptySearch":"No Result \n(≡ _ ≡)/~┴┴", + "searchSomething":"Search Something ≧◔◡◔≦", + "slogan":"The unity of flutter, The unity of coder." +} \ No newline at end of file diff --git a/modules/basic_system/l10n/lib/arb/app_ja.arb b/modules/basic_system/l10n/lib/arb/app_ja.arb new file mode 100644 index 000000000..d35a3fabb --- /dev/null +++ b/modules/basic_system/l10n/lib/arb/app_ja.arb @@ -0,0 +1,188 @@ +{ + "widgetCollection": "ウィジェットコレクション", + "paintCollection": "描画コレクション", + "knowledgeCollection": "知識集", + "treasureTools": "ツールボックス", + "collectCollection": "コレクション集", + "essentialCollection": "要点集", + "homeAccount": "アプリ情報", + "homeAccountTabInfo": "アプリについて", + "homeAccountTabMe": "本王に連絡", + "homeAccountSupport": "プロジェクトをサポート", + "searchWidget": "ウィジェットを検索", + "stateless": "ステートレス", + "stateful": "ステートフル", + "single": "シングルレンダリング", + "multi": "マルチレンダリング", + "sliver": "スライバー", + "proxy": "プロキシ", + "other": "その他", + "homeTabWidget": "ウィジェット", + "homeTabPaint": "描画", + "homeTabKnowledge": "知識", + "homeTabTools": "ツール", + "homeTabMine": "マイ", + "dataManagement": "データ管理", + "userCollection": "マイコレクション", + "aboutApplications": "アプリについて", + "contactThisKing": "本王に連絡", + "appSettings": "アプリ設定", + "darkMode": "ダークモード", + "themeColorSetting": "テーマカラー設定", + "fontSetting": "フォント設定", + "settingLanguageText": "多言語", + "codeHighlightStyle": "コードハイライトスタイル", + "versionInformation": "バージョン情報", + "displayPerformanceFloatingLayer": "パフォーマンスフローティングレイヤーを表示", + "showFloatingTools": "フローティングツールを表示", + "followSystem": "システムに従う", + "afterOpeningWillFollowTheSystemToOpenOrCloseDarkMode": "有効にすると、システムに従ってダークモードをオンまたはオフにします", + "manualSetting": "手動設定", + "lightMode": "ライトモード", + "settingLanguage": "言語設定", + "appDetails": "アプリ詳細", + "checkUpdate": "新しいバージョンを確認", + "downloadNewVersion": "新しいバージョンをダウンロード", + "downloadingNewVersion": "新しいバージョンをダウンロード中...", + "currentIsNew": "現在のアプリは最新バージョンです!", + "checkDatabaseNewVersion": "データベースの新しいバージョンを確認", + "viewThisProjectGithubRepository": "《このプロジェクトのGithubリポジトリを表示》", + "favorite": "お気に入り", + "enterComponentName": "コンポーネント名を入力", + "containerComponents": "コンテナコンポーネント", + "componentTavern": "コンポーネント酒場", + "cherishedComponents": "大切なコンポーネント", + "textImageCollection": "テキスト画像集", + "layoutCollection": "レイアウト集", + "eventCollection": "イベント集", + "animationCollection": "アニメーション集", + "slidingCollection": "スライド集", + "decorationCollection": "装飾集", + "assemblyCollection": "アセンブリ集", + "functionCollection": "機能集", + "popupCollection": "ポップアップ集", + "themeCollection": "テーマ集", + "derivativeCollection": "派生集", + "hardToCategorize": "分類が難しい", + "basicDrawing": "基本描画", + "basicDrawingDesc": "基本的な図形描画のケースを収録しています。これらのケースは、描画を始めたばかりのプログラマーにとって非常に役立ちます。これらのケースを通じて、点、線、矩形、円、円弧、テキスト、画像などの基本的な図形の描画方法を学び、Canvas、Paint、Pathなどの描画の核心オブジェクトの使用方法を理解できます。", + "animationGesture": "アニメーションとジェスチャー", + "animationGestureDesc": "アニメーションとジェスチャーの描画ケースを収録しています。これらのケースは、描画をより操作可能にします。これらのケースを通じて、スライド、回転、拡大縮小、移動などの効果を学び、描画が静的な表現だけでなくなることを理解できます。", + "particleDrawing": "粒子描画", + "particleDrawingDesc": "粒子関連の描画ケースを収録しています。これらのケースは、描画のトップレベルの操作です。これらのケースを通じて、粒子を使用して驚くべき視覚効果を描画する方法を学び、粒子時計、粒子爆発、粒子背景などの効果を実現し、描画に無限の可能性を与えます。", + "interestingDrawing": "面白い描画", + "interestingDrawingDesc": "いくつかの面白い描画ケースを収録しています。ここで一緒に描画の楽しさ、プログラミングの楽しさ、そして知恵の楽しさを体験しましょう。", + "artGallery": "アートギャラリー", + "artGalleryDesc": "殿堂級の描画ケースを収録しています。これらのケースは、描画の頂点作品であり、それらは実用性がなく、いかなるニーズのためでもありません。それらは存在するために存在し、人間の知恵と表現の媒体であり、芸術と呼ばれます。", + "drawingOfImages": "このサンプルでは、画像を描画する方法を紹介します。画像をロードし、指定された領域に画像リソースを描画します。上層に45度傾いたグリッド線を描画し、線の描画を練習します。", + "digitalDisplayTube": "このサンプルでは、LEDデジタル表示管を描画する方法を紹介し、パスPathの使用、変換、組み合わせ、およびコンポーネントのカプセル化の知識を練習します。非常に良い描画ケースです。", + "pathDrawing": "このサンプルでは、簡単なパスの描画とキャンバスの回転を紹介し、アニメーションと組み合わせて風車を回転させます。これは、描画とアニメーションを組み合わせた非常に簡潔なケースです。", + "gridCoordinateSystem": "このサンプルでは、線パスとテキストを使用してグリッド座標系を描画し、描画オブジェクトをカプセル化して再利用しやすくします。座標系は描画時に参考を提供し、入門に最適です。", + "polarCoordinateSystemOfFaces": "このサンプルでは、平面の極座標系を使用して描画し、関数方程式に基づいて極座標を収集して描画する方法を紹介します。", + "drawFunctionCurvesForPathPairs": "このサンプルでは、パスを使用して関数曲線を描画し、関数曲線上の少数の点をベジェ曲線でフィッティングする方法を紹介します。", + "drawRegularPolygons": "このサンプルでは、円内で点を収集し、正多角形を描画する方法を紹介します。描画とパス形成の練習に最適なケースです。\n特殊操作:+、- で辺の数を変更", + "randomNumberProcessing": "このサンプルでは、矩形の描画と乱数処理を紹介します。点の集合を使用して矩形の位置情報を決定し、それを描画します。データの制御能力を練習できます。", + "clockDrawing": "このサンプルでは、時計の描画を通じて、Flutterでの回転目盛りの描画テクニックを練習し、アニメーションで時計の針を回転させます。", + "drawSprings": "このサンプルでは、バネを描画し、垂直にドラッグして伸縮し、手を離すと復元アニメーションを行う方法を紹介します。非常に良い総合的な小ケースです。特殊操作:上下にドラッグしてバネを伸縮", + "theApplicationOfAnglesInDrawing": "このサンプルでは、ある点を中心に回転運動を行う方法を紹介します。これにより、2点間の角度を描画に適用する方法を学びます。\n特殊操作:クリックして実行", + "usingShadersAndFilters": "このサンプルでは、描画でシェーダーとフィルターを使用し、アニメーションで数値を変化させて回転する光の効果を実現する方法を紹介します。", + "pathDrawingFunctionCurve": "このサンプルでは、パスを使用して関数曲線を描画し、パス測定を使用してアニメーションを行う方法を紹介します。", + "thePathOfBingDwenDwen": "このサンプルでは、2022年北京冬季オリンピックのマスコットであるビンドゥンドゥンのパスを描画し、パス測定を使用してアニメーションを行います。\n特殊操作:クリックして実行", + "drawCubicBesselCurve": "このサンプルでは、3次ベジェ曲線を描画し、タッチポイントを使用してある点がアクティブかどうかを判断し、それに応じて点の位置を制御してドラッグ制御効果を実現する方法を紹介します。\n特殊操作:クリックで点を描画、ダブルクリックでクリア", + "theEffectOfAnimationCurve": "このサンプルでは、アニメーションカーブの効果を直感的に確認し、アニメーションに対する理解を深めます。\n特殊操作:クリックして実行", + "randomParticlesAndBoundaryBouncing": "このサンプルでは、ランダムな粒子を作成し、境界でのバウンスロジックを処理する方法を紹介します。粒子運動を学ぶのに非常に良い入門ケースです。特殊操作:クリックで停止/実行", + "particleCollision": "このサンプルでは、個々の粒子の衝突検出を行い、複数の粒子に分裂させる方法を紹介します。非常に面白いケースです。\n特殊操作:クリックでリセット", + "particle": "このサンプルでは、画像を粒子として表現し、粒子にアニメーションを適用して爆発効果を実現する方法を紹介します。\n特殊操作:クリックして実行", + "rectangleAndRandomNumbers": "このサンプルでは、矩形の描画と乱数処理を紹介します。点の集合を使用して矩形の位置情報を決定し、それを描画します。データの制御能力を練習できます。\n特殊操作:クリックしてランダム生成", + "bingDwenDwen": "このサンプルでは、2022年北京冬季オリンピックのマスコットであるビンドゥンドゥンの形を描画し、パス描画やグラデーションなどの知識を学びます。", + "pufengInjectionTest": "このサンプルでは、蒲豊の針投げテストのプロセスを実装し、確率を使用して円周率を推定します。描画の小技やデータの論理処理を学ぶことができます。", + "ticTacToe": "このサンプルでは、三目並べの描画と論理検証を通じて、ジェスチャー、描画、アニメーション、検証などの重要なスキルを組み合わせます。非常に良い練習ケースです。\n特殊操作:ダブルクリックでリセット", + "tiledLines": "このサンプルは、generativeartistry.comのtiled-linesに由来し、xrr2016がFlutterで実装しました。リポジトリアドレス:flutter-generative-artistry", + "joyDivision": "このサンプルは、generativeartistry.comのjoy-divisionに由来し、xrr2016がFlutterで実装しました。リポジトリアドレス:flutter-generative-artistry", + "cubicDisarray": "このサンプルは、generativeartistry.comのcubic-disarrayに由来し、xrr2016がFlutterで実装しました。リポジトリアドレス:flutter-generative-artistry", + "triangularMesh": "このサンプルは、generativeartistry.comのtriangular-meshに由来し、xrr2016がFlutterで実装しました。リポジトリアドレス:flutter-generative-artistry", + "unDeuxTrois": "このサンプルは、generativeartistry.comのun-deux-troisに由来し、xrr2016がFlutterで実装しました。リポジトリアドレス:flutter-generative-artistry", + "circlePacking": "このサンプルは、generativeartistry.comのcircle-packingに由来し、xrr2016がFlutterで実装しました。リポジトリアドレス:flutter-generative-artistry", + "hypnoticSquares": "このサンプルは、generativeartistry.comのhypnotic-squaresに由来し、xrr2016がFlutterで実装しました。リポジトリアドレス:flutter-generative-artistry", + "pietMondrian": "このサンプルは、generativeartistry.comのpiet-mondrianに由来し、xrr2016がFlutterで実装しました。リポジトリアドレス:flutter-generative-artistry", + "downloadCompressedPackage": "使用方法:\n1. iconfont.cnでアイコンを選び、プロジェクトに追加し、圧縮パッケージをダウンロードします。\n2. Flutterプロジェクトのアドレスを選択し、リソースと生成物ファイルの場所を設定します。\n3. コード生成ボタンをクリックすると、関連するコードが生成されます。", + "qAIssues": "要点集録のQAデータは、rUnitのpointタグが付いたissuesに収録されています。データを提供する必要がある場合は、issuesで質問してください。", + "tips": "注:", + "visualSorting": "可視化ソート", + "visual": "可視ソート", + "insertion": "挿入ソート", + "bubble": "バブルソート", + "cocktail": "カクテルソート(双方向バブルソート)", + "comb": "コームソート", + "pigeonHole": "鳩の巣ソート", + "shell": "シェルソート", + "selection": "選択ソート", + "gnome": "ノームソート", + "cycle": "サイクルソート", + "heap": "ヒープソート", + "quick": "クイックソート", + "merge": "マージソート", + "sortingAlgorithmConfiguration": "ソートアルゴリズム設定", + "dataCount": "データ数(個数)", + "timeInterval": "時間間隔(マイクロ秒)", + "randomSeed": "ランダムシード", + "codeGeneration": "コード生成", + "generateCode": "コードを生成", + "artifactLocation": "生成物の場所", + "codeClassLocation": "コードクラスの保存場所", + "resourceDirectory": "リソースディレクトリ", + "iconfontResourceLocation": "iconfontリソースの保存場所", + "projectPath": "プロジェクトパス", + "inputProjectAddress": "プロジェクトアドレスを選択または入力してください", + "iconfontCompressedPackagePath": "Iconfont圧縮パッケージのパス", + "pleaseSelectOrInputIconfontCompressedPackagePath": "iconfontのダウンロードした圧縮パッケージのパスを選択または入力してください", + "stayTuned": "お楽しみに", + "iconFont": "IconFont", + "dataClass": "データクラス", + "stateManagement": "状態管理", + "jsonParsing": "Json解析", + "clickHereToJump": "ここをクリックしてジャンプ", + "knowledgeTabToly": "ジェット文庫", + "knowledgeTabAlgo": "アルゴリズム演繹", + "knowledgeTabLayout": "レイアウト宝庫", + "knowledgeTabPoint": "要点宝庫", + "knowledgeConstruction": "建設中", + "knowledgeToJuejin": "掘金へ", + "srcPath": "ソースアドレス", + "widgetsInn": "ウィジェット酒場", + "likedWidgets": "大切なウィジェット", + "relatedComponents": "関連コンポーネント", + "backupFavoritesCollectionData": "お気に入りコレクションデータのバックアップ", + "syncFavoritesCollectionData": "お気に入りコレクションデータの同期", + "favoritesCollectionDataReset": "お気に入りコレクションデータのリセット", + "resetSuccess": "リセット成功!", + "dataSetBackupSuccess": "データセットのバックアップ成功!", + "dataSetBackupFailure": "データセットのバックアップ失敗!", + "dataSynchronizationCopySuccess": "データ同期コピー成功!", + "dataSynchronizationCopyFailure": "データ同期コピー失敗!", + "destructionRed": "破滅の赤", + "rageOrange": "怒りのオレンジ", + "warningYellow": "警告の黄", + "camouflageGreen": "偽装の緑", + "coldBlue": "冷徹な青", + "infiniteBlue": "無限の藍", + "mysteryPurple": "神秘の紫", + "destinyBlack": "帰宿の黒", + + "showBackground": "背景を表示", + "toly": "張風捷特烈", + "dartHandbook": "Dart 手引き", + + "codeCopiedSuccessfully": "コードがコピーされました", + "favoriteFolderManagement": "お気に入りフォルダの管理", + "assembly": "コンポーネント", + "draw": "描画", + "knowledge": "知識", + "collection": "コレクション", + "my": "私の", + "picture": "枚", + "widgetInn": "コンポーネント酒場", + "emptySearch": "データがありません、俺もどうしようもない\n(≡ _ ≡)/~┴┴", + "searchSomething": "友よ、何か検索しよう…≧◔◡◔≦", + "slogan": "Flutterの連携、プログラマーの連携" + } \ No newline at end of file diff --git a/modules/basic_system/l10n/lib/arb/app_ko.arb b/modules/basic_system/l10n/lib/arb/app_ko.arb new file mode 100644 index 000000000..374ad4e0e --- /dev/null +++ b/modules/basic_system/l10n/lib/arb/app_ko.arb @@ -0,0 +1,186 @@ +{ +"widgetCollection": "위젯 컬렉션", +"paintCollection": "그림 컬렉션", +"knowledgeCollection": "지식 모음", +"treasureTools": "도구 보물상자", +"collectCollection": "컬렉션 모음", +"essentialCollection": "핵심 모음", +"homeAccount": "앱 정보", +"homeAccountTabInfo": "앱 정보", +"homeAccountTabMe": "저에게 연락", +"homeAccountSupport": "프로젝트 지원", +"searchWidget": "위젯 검색", +"stateless": "무상태", +"stateful": "상태 있음", +"single": "단일 렌더링", +"multi": "다중 렌더링", +"sliver": "슬라이버", +"proxy": "프록시", +"other": "기타", +"homeTabWidget": "위젯", +"homeTabPaint": "그리기", +"homeTabKnowledge": "지식", +"homeTabTools": "도구", +"homeTabMine": "내 정보", +"dataManagement": "데이터 관리", +"userCollection": "내 컬렉션", +"aboutApplications": "앱 정보", +"contactThisKing": "저에게 연락", +"appSettings": "앱 설정", +"darkMode": "다크 모드", +"themeColorSetting": "테마 색상 설정", +"fontSetting": "폰트 설정", +"settingLanguageText": "다국어", +"codeHighlightStyle": "코드 하이라이트 스타일", +"versionInformation": "버전 정보", +"displayPerformanceFloatingLayer": "성능 플로팅 레이어 표시", +"showFloatingTools": "플로팅 도구 표시", +"followSystem": "시스템 따라가기", +"afterOpeningWillFollowTheSystemToOpenOrCloseDarkMode": "시스템 설정에 따라 다크 모드를 켜거나 끕니다.", +"manualSetting": "수동 설정", +"lightMode": "라이트 모드", +"settingLanguage": "언어 설정", +"appDetails": "앱 상세 정보", +"checkUpdate": "새 버전 확인", +"downloadNewVersion": "새 버전 다운로드", +"downloadingNewVersion": "새 버전 다운로드 중...", +"currentIsNew": "현재 버전이 최신입니다!", +"checkDatabaseNewVersion": "데이터베이스 새 버전 확인", +"viewThisProjectGithubRepository": "《이 프로젝트의 Github 저장소 보기》", +"favorite": "즐겨찾기", +"enterComponentName": "컴포넌트 이름 입력", +"containerComponents": "컨테이너 컴포넌트", +"componentTavern": "컴포넌트 선술집", +"cherishedComponents": "소중한 컴포넌트", +"textImageCollection": "텍스트 이미지 모음", +"layoutCollection": "레이아웃 모음", +"eventCollection": "이벤트 모음", +"animationCollection": "애니메이션 모음", +"slidingCollection": "슬라이딩 모음", +"decorationCollection": "장식 모음", +"assemblyCollection": "조립 모음", +"functionCollection": "기능 모음", +"popupCollection": "팝업 모음", +"themeCollection": "테마 모음", +"derivativeCollection": "파생 모음", +"hardToCategorize": "분류하기 어려움", +"basicDrawing": "기본 그리기", +"basicDrawingDesc": "기본 도형 그리기 예제를 포함합니다. 이 예제들은 그리기를 처음 접하는 프로그래머에게 매우 친숙할 것입니다. 이 예제들을 통해 점, 선, 사각형, 원, 호, 텍스트, 이미지 등 기본 도형을 그리는 방법을 배우고, Canvas, Paint, Path 등 그리기의 핵심 객체 사용법을 익힐 수 있습니다.", +"animationGesture": "애니메이션 제스처", +"animationGestureDesc": "애니메이션과 제스처 그리기 예제를 포함합니다. 이 예제들은 그리기를 더욱 상호작용적으로 만듭니다. 이 예제들을 통해 슬라이드, 회전, 확대/축소, 이동 등의 효과를 배울 수 있으며, 그리기가 단순히 정적인 표현이 아니게 됩니다.", +"particleDrawing": "입자 그리기", +"particleDrawingDesc": "입자 관련 그리기 예제를 포함합니다. 이 예제들은 그리기의 최고 수준의 작업입니다. 이 예제들을 통해 입자를 사용하여 놀라운 시각적 효과를 그리는 방법을 배울 수 있습니다. 입자 시계, 입자 폭발, 입자 배경 등 다양한 효과를 통해 그리기의 무한한 가능성을 경험할 수 있습니다.", +"interestingDrawing": "재미있는 그리기", +"interestingDrawingDesc": "재미있는 그리기 예제를 포함합니다. 여기서 그리기, 프로그래밍, 지혜의 즐거움을 함께 경험해 보세요.", +"artGallery": "예술 갤러리", +"artGalleryDesc": "최고 수준의 그리기 예제를 포함합니다. 이 예제들은 그리기의 정점에 있는 작품들로, 실용성은 없지만 존재 자체로 의미가 있는 예술 작품입니다.", +"drawingOfImages": "이 예제는 이미지를 그리는 방법을 소개합니다: 이미지를 로드하고 지정된 영역에 그립니다. 상단에 45도 각도의 그리드 선을 그려 선 그리기를 연습합니다.", +"digitalDisplayTube": "이 예제는 LED 디지털 디스플레이 튜브를 그리는 방법을 소개하며, Path 사용, 변환, 조합 및 컴포넌트 캡슐화를 연습합니다. 매우 좋은 그리기 예제입니다.", +"pathDrawing": "이 예제는 간단한 경로 그리기 및 캔버스 회전을 소개하며, 애니메이션을 결합하여 풍차를 회전시킵니다. 매우 간결한 그리기와 애니메이션 결합 예제입니다.", +"gridCoordinateSystem": "이 예제는 선 경로와 텍스트를 사용하여 그리드 좌표계를 그리는 방법을 소개하며, 그리기 객체를 캡슐화하여 재사용하기 쉽게 만듭니다. 좌표계는 그리기 시 참조를 제공하며, 입문자에게 필수입니다.", +"polarCoordinateSystemOfFaces": "이 예제는 평면의 극좌표계를 그리는 방법을 소개하며, 함수 방정식에 따라 극좌표를 수집하여 그립니다.", +"drawFunctionCurvesForPathPairs": "이 예제는 경로를 사용하여 함수 곡선을 그리는 방법을 소개하며, 함수 곡선 상의 소량의 점을 베지어 곡선으로 피팅합니다.", +"drawRegularPolygons": "이 예제는 원 안에서 점을 수집하여 정다각형을 그리는 방법을 소개하며, 그리기 및 경로 형성 연습에 매우 좋은 예제입니다.\n특수 조작: +, - 로 변 수정", +"randomNumberProcessing": "이 예제는 사각형 그리기 및 난수 처리를 소개합니다. 점 집합을 통해 사각형 위치 정보를 결정하고 그립니다. 데이터 제어 능력을 연습할 수 있습니다.", +"clockDrawing": "이 예제는 시계 그리기를 통해 Flutter에서 회전 눈금 그리기 기술을 연습하고, 애니메이션을 통해 시계 바늘이 회전하도록 합니다.", +"drawSprings": "이 예제는 스프링을 그리는 방법을 소개하며, 수직 드래그로 스프링을 늘이거나 줄이고, 놓을 때 복원 애니메이션을 수행합니다. 매우 좋은 종합 예제입니다. 특수 조작: 위아래 드래그로 스프링 늘이기/줄이기", +"theApplicationOfAnglesInDrawing": "이 예제는 특정 점을 중심으로 회전 운동을 수행하는 방법을 소개합니다. 이를 통해 두 점 사이의 각도가 그리기에서 어떻게 적용되는지 배울 수 있습니다.\n특수 조작: 클릭하여 실행", +"usingShadersAndFilters": "이 예제는 그리기에서 셰이더와 필터를 사용하는 방법을 소개하며, 애니메이션을 통해 수치를 변경하여 회전하는 빛의 효과를 만듭니다.", +"pathDrawingFunctionCurve": "이 예제는 경로를 사용하여 함수 곡선을 그리는 방법을 소개하며, 경로 측정을 사용하여 애니메이션을 만듭니다.", +"thePathOfBingDwenDwen": "이 예제는 2022년 베이징 동계 올림픽 마스코트 빙둔둔의 경로를 그리며, 경로 측정을 사용하여 애니메이션을 만듭니다.\n특수 조작: 클릭하여 실행", +"drawCubicBesselCurve": "이 예제는 3차 베지어 곡선을 그리는 방법을 소개하며, 접점을 통해 특정 점이 활성화되었는지 판단하여 점의 위치를 제어합니다.\n특수 조작: 클릭하여 점 그리기, 더블 클릭하여 지우기", +"theEffectOfAnimationCurve": "이 예제는 애니메이션 곡선의 효과를 직관적으로 보여줍니다. 이를 통해 애니메이션에 대한 이해를 높일 수 있습니다.\n특수 조작: 클릭하여 실행", +"randomParticlesAndBoundaryBouncing": "이 예제는 무작위 입자를 생성하고 경계에서 튕기는 로직을 처리하는 방법을 소개합니다. 입자 운동을 배우기에 매우 좋은 입문 예제입니다. 특수 조작: 클릭하여 정지/실행", +"particleCollision": "이 예제는 개별 입자에 대한 충돌 감지를 수행하고, 여러 입자로 분열시키는 방법을 소개합니다. 매우 재미있는 예제입니다.\n특수 조작: 클릭하여 재설정", +"particle": "이 예제는 이미지를 입자로 표현하고, 입자에 애니메이션을 적용하여 폭발 효과를 만드는 방법을 소개합니다.\n특수 조작: 클릭하여 실행", +"rectangleAndRandomNumbers": "이 예제는 사각형 그리기 및 난수 처리를 소개합니다. 점 집합을 통해 사각형 위치 정보를 결정하고 그립니다. 데이터 제어 능력을 연습할 수 있습니다.\n특수 조작: 클릭하여 무작위 생성", +"bingDwenDwen": "이 예제는 2022년 베이징 동계 올림픽 마스코트 빙둔둔의 형태를 그리는 방법을 소개하며, 경로 그리기 및 그라데이션 색상 사용법을 배울 수 있습니다.", +"pufengInjectionTest": "이 예제는 푸펑 투시험의 테스트 과정을 구현하며, 확률을 통해 원주율을 추정합니다. 그리기 기술 및 데이터 논리 처리 방법을 배울 수 있습니다.", +"ticTacToe": "이 예제는 틱택토 그리기 및 논리 검증을 통해 제스처, 그리기, 애니메이션, 검증 등의 중요한 기술을 종합적으로 연습할 수 있는 매우 좋은 예제입니다.\n특수 조작: 더블 클릭하여 재설정", +"tiledLines": "이 예제는 generativeartistry.com의 tiled-lines에서 유래되었으며, xrr2016이 Flutter로 구현했습니다. 저장소 주소: flutter-generative-artistry", +"joyDivision": "이 예제는 generativeartistry.com의 joy-division에서 유래되었으며, xrr2016이 Flutter로 구현했습니다. 저장소 주소: flutter-generative-artistry", +"cubicDisarray": "이 예제는 generativeartistry.com의 cubic-disarray에서 유래되었으며, xrr2016이 Flutter로 구현했습니다. 저장소 주소: flutter-generative-artistry", +"triangularMesh": "이 예제는 generativeartistry.com의 triangular-mesh에서 유래되었으며, xrr2016이 Flutter로 구현했습니다. 저장소 주소: flutter-generative-artistry", +"unDeuxTrois": "이 예제는 generativeartistry.com의 un-deux-trois에서 유래되었으며, xrr2016이 Flutter로 구현했습니다. 저장소 주소: flutter-generative-artistry", +"circlePacking": "이 예제는 generativeartistry.com의 circle-packing에서 유래되었으며, xrr2016이 Flutter로 구현했습니다. 저장소 주소: flutter-generative-artistry", +"hypnoticSquares": "이 예제는 generativeartistry.com의 hypnotic-squares에서 유래되었으며, xrr2016이 Flutter로 구현했습니다. 저장소 주소: flutter-generative-artistry", +"pietMondrian": "이 예제는 generativeartistry.com의 piet-mondrian에서 유래되었으며, xrr2016이 Flutter로 구현했습니다. 저장소 주소: flutter-generative-artistry", +"downloadCompressedPackage": "사용 방법:\n1. iconfont.cn에서 아이콘을 선택하고 프로젝트에 추가한 후 압축 파일을 다운로드합니다.\n2. Flutter 프로젝트 경로를 선택하고 리소스 및 산출물 파일 위치를 설정합니다.\n3. 코드 생성 버튼을 클릭하여 관련 코드를 생성합니다.", +"qAIssues": "핵심 모음의 QA 데이터는 rUnit의 point 태그가 있는 issues에 포함되어 있습니다. 데이터를 제공하려면 issues에서 질문하세요.", +"tips": "참고:", +"visualSorting": "시각적 정렬", +"visual": "시각적 정렬", +"insertion": "삽입 정렬", +"bubble": "버블 정렬", +"cocktail": "칵테일 정렬(양방향 버블 정렬)", +"comb": "빗질 정렬", +"pigeonHole": "비둘기집 정렬", +"shell": "셸 정렬", +"selection": "선택 정렬", +"gnome": "노움 정렬", +"cycle": "순환 정렬", +"heap": "힙 정렬", +"quick": "퀵 정렬", +"merge": "병합 정렬", +"sortingAlgorithmConfiguration": "정렬 알고리즘 설정", +"dataCount": "데이터 수(개수)", +"timeInterval": "시간 간격(마이크로초)", +"randomSeed": "무작위 시드", +"codeGeneration": "코드 생성", +"generateCode": "코드 생성", +"artifactLocation": "산출물 위치", +"codeClassLocation": "코드 클래스 위치", +"resourceDirectory": "리소스 디렉토리", +"iconfontResourceLocation": "iconfont 리소스 위치", +"projectPath": "프로젝트 경로", +"inputProjectAddress": "프로젝트 주소를 선택하거나 입력하세요", +"iconfontCompressedPackagePath": "Iconfont 압축 파일 경로", +"pleaseSelectOrInputIconfontCompressedPackagePath": "iconfont 다운로드 압축 파일 경로를 선택하거나 입력하세요", +"stayTuned": "기대해 주세요", +"iconFont": "IconFont", +"dataClass": "데이터 클래스", +"stateManagement": "상태 관리", +"jsonParsing": "Json 파싱", +"clickHereToJump": "여기를 클릭하여 이동", +"knowledgeTabToly": "제트 문고", +"knowledgeTabAlgo": "알고리즘 연습", +"knowledgeTabLayout": "레이아웃 보물", +"knowledgeTabPoint": "핵심 보물", +"knowledgeConstruction": "구축 중", +"knowledgeToJuejin": "Juejin으로 이동", +"srcPath": "소스 경로", +"widgetsInn": "위젯 선술집", +"likedWidgets": "소중한 위젯", +"relatedComponents": "관련 컴포넌트", +"backupFavoritesCollectionData": "즐겨찾기 컬렉션 데이터 백업", +"syncFavoritesCollectionData": "즐겨찾기 컬렉션 데이터 동기화", +"favoritesCollectionDataReset": "즐겨찾기 컬렉션 데이터 재설정", +"resetSuccess": "재설정 성공!", +"dataSetBackupSuccess": "데이터 세트 백업 성공!", +"dataSetBackupFailure": "데이터 세트 백업 실패!", +"dataSynchronizationCopySuccess": "데이터 동기화 복사 성공!", +"dataSynchronizationCopyFailure": "데이터 동기화 복사 실패!", + "destructionRed": "파괴의 빨강", + "rageOrange": "분노의 주황", + "warningYellow": "경고의 노랑", + "camouflageGreen": "위장의 초록", + "coldBlue": "냉담의 파랑", + "infiniteBlue": "무한의 남색", + "mysteryPurple": "신비의 보라", + "destinyBlack": "운명의 검정", + "showBackground": "배경 표시", + "toly": "장풍제특렬", + "dartHandbook": "Dart 핸드북", + "codeCopiedSuccessfully": "코드 복사 성공", + "favoriteFolderManagement": "즐겨찾기 폴더 관리", + "assembly": "컴포넌트", + "draw": "그리기", + "knowledge": "지식", + "collection": "컬렉션", + "my": "내 정보", + "picture": "그림", + "widgetInn": "위젯 선술집", + "emptySearch": "데이터가 없습니다, 형님도 방법이 없어요\n(≡ _ ≡)/~┴┴", + "searchSomething": "형님, 뭘 검색하시겠어요...≧◔◡◔≦", + "slogan": "Flutter의 연합, 프로그래머의 연합" + } \ No newline at end of file diff --git a/modules/basic_system/l10n/lib/arb/app_ru.arb b/modules/basic_system/l10n/lib/arb/app_ru.arb new file mode 100644 index 000000000..bf24822fa --- /dev/null +++ b/modules/basic_system/l10n/lib/arb/app_ru.arb @@ -0,0 +1,194 @@ +{ + "widgetCollection": "Сборник компонентов", + "paintCollection": "Сборник рисования", + "knowledgeCollection": "Сборник знаний", + "treasureTools": "Сундучок инструментов", + "collectCollection": "Сборник избранного", + "essentialCollection": "Сборник ключевых моментов", + "homeAccount": "Информация о приложении", + "homeAccountTabInfo": "О приложении", + "homeAccountTabMe": "Связаться с этим королём", + "homeAccountSupport": "Проекты поддержки", + "searchWidget": "Поиск компонентов", + "stateless": "Без состояния", + "stateful": "С состоянием", + "single": "Один рендеринг", + "multi": "Многократный рендеринг", + "sliver": "Скользящая панель", + "proxy": "Прокси", + "other": "Другие", + "homeTabWidget": "Компоненты", + "homeTabPaint": "Рисование", + "homeTabKnowledge": "Знания", + "homeTabTools": "Инструменты", + "homeTabMine": "Мой профиль", + "dataManagement": "Управление данными", + "userCollection": "Мои избранные", + "aboutApplications": "О приложении", + "contactThisKing": "Связаться с этим королём", + "appSettings": "Настройки приложения", + "darkMode": "Тёмный режим", + "themeColorSetting": "Настройки темы", + "fontSetting": "Настройки шрифта", + "settingLanguageText": "Многоязычность", + "codeHighlightStyle": "Стиль подсветки кода", + "versionInformation": "Информация о версии", + "displayPerformanceFloatingLayer": "Показать плавающий слой производительности", + + "showFloatingTools": "Показать плавающие инструменты", + + "followSystem": "Следовать за системой", + "afterOpeningWillFollowTheSystemToOpenOrCloseDarkMode": "После включения будет следовать за системой для включения или отключения тёмного режима", + "manualSetting": "Ручные настройки", + "lightMode": "Светлый режим", + "settingLanguage": "Настройки языка", + + "appDetails": "Детали приложения", + "checkUpdate": "Проверить обновление", + "downloadNewVersion": "Скачать новое обновление", + "downloadingNewVersion": "Скачивание нового обновления...", + "currentIsNew": "Текущая версия приложения уже самая новая!", + "checkDatabaseNewVersion": "Проверить обновление базы данных", + "viewThisProjectGithubRepository": "Посмотреть репозиторий проекта на Github", + + "favorite": "Добавлено в избранное", + "enterComponentName": "Введите название компонента", + "containerComponents": "Контейнерные компоненты", + "componentTavern": "Таверна компонентов", + "cherishedComponents": "Ценные компоненты", + "textImageCollection": "Сборник изображений и текста", + "layoutCollection": "Сборник макетов", + "eventCollection": "Сборник событий", + "animationCollection": "Сборник анимаций", + "slidingCollection": "Сборник слайдов", + "decorationCollection": "Сборник декораций", + "assemblyCollection": "Сборник сборки", + "functionCollection": "Сборник функций", + "popupCollection": "Сборник всплывающих окон", + "themeCollection": "Сборник тем", + "derivativeCollection": "Сборник производных", + "hardToCategorize": "Трудно классифицировать", + "basicDrawing": "Основы рисования", + "basicDrawingDesc": "Включает несколько примеров базового рисования, которые будут полезны для начинающих программистов. Эти примеры научат рисованию базовых фигур, таких как точки, линии, прямоугольники, круги и текст.", + "animationGesture": "Анимация жестов", + "animationGestureDesc": "Содержит примеры рисования анимаций и жестов, которые сделают рисование более интерактивным. Эти примеры научат использовать анимации и жесты, такие как скольжение, вращение, масштабирование и перемещение.", + "particleDrawing": "Частицы в рисовании", + "particleDrawingDesc": "Включает примеры рисования с использованием частиц. Эти примеры покажут, как использовать частицы для создания зрелищных эффектов, таких как взрывы или анимации с частицами.", + "interestingDrawing": "Интересные рисунки", + "interestingDrawingDesc": "Примеры интересных и забавных рисунков, где мы можем наслаждаться процессом рисования и программирования.", + "artGallery": "Галерея искусства", + "artGalleryDesc": "Содержит примеры высокого искусства, которые не предназначены для практических целей, а являются выражением человеческого интеллекта и творчества.", + "drawingOfImages": "Пример рисования изображений: загрузка изображения и его отрисовка в определенной области. На верхнем слое добавлены линии сетки для практики рисования линий.", + "digitalDisplayTube": "Пример рисования цифровых трубок с использованием пути Path и анимации.", + "pathDrawing": "Пример рисования простых путей с анимацией.", + "gridCoordinateSystem": "Пример рисования сетки координат с линиями и текстом для новичков.", + "polarCoordinateSystemOfFaces": "Пример рисования полярной координатной системы с использованием функций.", + "drawFunctionCurvesForPathPairs": "Пример рисования функциональных кривых с использованием пути и Беезье.", + "drawRegularPolygons": "Пример рисования правильных многоугольников внутри круга.", + "randomNumberProcessing": "Пример рисования прямоугольников с обработкой случайных чисел.", + "clockDrawing": "Пример рисования часов с анимацией.", + "drawSprings": "Пример рисования пружины с анимацией сжатия и растяжения.", + "theApplicationOfAnglesInDrawing": "Пример рисования с использованием углов для вращения.", + "usingShadersAndFilters": "Пример использования шейдеров и фильтров для создания анимации.", + "pathDrawingFunctionCurve": "Пример рисования кривых с помощью пути и измерения анимации.", + "thePathOfBingDwenDwen": "Пример рисования пути маскота Олимпиады Пекин-2022 с использованием анимации пути.", + "drawCubicBesselCurve": "Пример рисования кривой кубического Безье с возможностью перетаскивания точек.", + "theEffectOfAnimationCurve": "Пример демонстрации эффекта анимационной кривой для углубленного понимания анимаций.", + "randomParticlesAndBoundaryBouncing": "Пример создания случайных частиц с отскоком от границ.", + "particleCollision": "Пример столкновений частиц с анимацией их распада.", + "particle": "Пример взрыва изображений с использованием частиц.", + "rectangleAndRandomNumbers": "Пример рисования прямоугольников с использованием случайных чисел.", + "bingDwenDwen": "Пример рисования формы маскота Олимпиады Пекин-2022.", + "pufengInjectionTest": "Пример теста для вычисления числа Пи с использованием вероятности.", + "ticTacToe": "Пример игры в крестики-нолики с логикой и анимацией.", + "tiledLines": "Пример, основанный на генеративном искусстве, реализованный с помощью Flutter.", + "joyDivision": "Пример, основанный на генеративном искусстве, реализованный с помощью Flutter.", + "cubicDisarray": "Пример, основанный на генеративном искусстве, реализованный с помощью Flutter.", + "triangularMesh": "Пример, основанный на генеративном искусстве, реализованный с помощью Flutter.", + "unDeuxTrois": "Пример, основанный на генеративном искусстве, реализованный с помощью Flutter.", + "circlePacking": "Пример, основанный на генеративном искусстве, реализованный с помощью Flutter.", + "hypnoticSquares": "Пример, основанный на генеративном искусстве, реализованный с помощью Flutter.", + "pietMondrian": "Пример, основанный на генеративном искусстве, реализованный с помощью Flutter.", + "downloadCompressedPackage": "Инструкция по использованию:\n1. Выберите и скачайте иконки на iconfont.cn.\n2. Настройте путь к проекту Flutter.\n3. Нажмите кнопку для генерации кода.", + "qAIssues": "Данные QA из сборника важных моментов rUnit будут добавлены в issues с меткой point.", + "tips": "Совет:", + "visualSorting": "Визуальная сортировка", + "visual": "Визуальная сортировка", + "insertion": "Сортировка вставками", + "bubble": "Пузырьковая сортировка", + "cocktail": "Коктейльная сортировка", + "comb": "Сортировка расческой", + "pigeonHole": "Сортировка по принципу дырок для голубей", + "shell": "Сортировка Шелла", + "selection": "Сортировка выбором", + "gnome": "Сортировка гномом", + "cycle": "Циклическая сортировка", + "heap": "Сортировка кучей", + "quick": "Быстрая сортировка", + "merge": "Сортировка слиянием", + "sortingAlgorithmConfiguration": "Настройки сортировки", + "dataCount": "Количество данных", + "timeInterval": "Интервал времени", + "randomSeed": "Случайное зерно", + "codeGeneration": "Генерация кода", + "generateCode": "Сгенерировать код", + "artifactLocation": "Местоположение артефакта", + "codeClassLocation": "Местоположение класса кода", + "resourceDirectory": "Каталог ресурсов", + "iconfontResourceLocation": "Местоположение ресурсов iconfont", + "projectPath": "Путь проекта", + "inputProjectAddress": "Выберите или введите путь к проекту", + "iconfontCompressedPackagePath": "Путь к сжатию иконок", + "pleaseSelectOrInputIconfontCompressedPackagePath": "Пожалуйста, выберите или введите путь к сжатому пакету iconfont", + "stayTuned": "Ожидайте", + "iconFont": "IconFont", + "dataClass": "Класс данных", + "stateManagement": "Управление состоянием", + "jsonParsing": "Парсинг JSON", + "clickHereToJump": "Нажмите здесь для перехода", + + "knowledgeTabToly": "Библиотека Jet", + "knowledgeTabAlgo": "Алгоритмы", + "knowledgeTabLayout": "Библиотека макетов", + "knowledgeTabPoint": "Библиотека ключевых моментов", + "knowledgeConstruction": "На стадии строительства", + "knowledgeToJuejin": "Перейти на Juejin", + "srcPath": "Путь к исходному коду", + "widgetsInn": "Таверна компонентов", + "likedWidgets": "Избранные компоненты", + "relatedComponents": "Связанные компоненты", + + "backupFavoritesCollectionData": "Резервное копирование данных избранного", + "syncFavoritesCollectionData": "Синхронизация данных избранного", + "favoritesCollectionDataReset": "Сброс данных избранного", + "resetSuccess": "Сброс успешен!", + "dataSetBackupSuccess": "Резервное копирование данных прошло успешно!", + "dataSetBackupFailure": "Ошибка резервного копирования данных!", + "dataSynchronizationCopySuccess": "Синхронизация данных успешна!", + "dataSynchronizationCopyFailure": "Ошибка синхронизации данных!", + "destructionRed": "Красный разрушения", + "rageOrange": "Оранжевый гнева", + "warningYellow": "Желтый предупреждения", + "camouflageGreen": "Зеленый камуфляжа", + "coldBlue": "Холодный синий", + "infiniteBlue": "Бесконечный синий", + "mysteryPurple": "Тайный фиолетовый", + "destinyBlack": "Черный судьбы", + + "showBackground": "Показать фон", + "toly": "Толь", + "dartHandbook": "Руководство Dart", + + "codeCopiedSuccessfully": "Код успешно скопирован", + "favoriteFolderManagement": "Управление папками избранного", + "assembly": "Сборка", + "draw": "Рисование", + "knowledge": "Знания", + "collection": "Коллекция", + "my": "Мои", + "picture": "Картинка", + "widgetInn": "Таверна компонентов", + "emptySearch": "Данных нет, я тоже не знаю, что делать\n(≡ _ ≡)/~┴┴", + "searchSomething": "Друзья, давайте что-то искать…≧◔◡◔≦", + "slogan": "Связь Flutter, связь программистов" +} diff --git a/modules/basic_system/l10n/lib/arb/app_zh.arb b/modules/basic_system/l10n/lib/arb/app_zh.arb new file mode 100644 index 000000000..b76c3bc19 --- /dev/null +++ b/modules/basic_system/l10n/lib/arb/app_zh.arb @@ -0,0 +1,195 @@ +{ + "widgetCollection": "组件集录", + "paintCollection": "绘制集录", + "knowledgeCollection": "知识集锦", + "treasureTools": "工具宝箱", + "collectCollection": "收藏集录", + "essentialCollection": "要点集录", + "homeAccount": "应用信息", + "homeAccountTabInfo": "关于应用", + "homeAccountTabMe": "联系本王", + "homeAccountSupport": "支持项目", + "searchWidget": "搜索组件", + "stateless":"无态", + "stateful":"有态", + "single":"单渲", + "multi":"多渲", + "sliver":"滑片", + "proxy":"代理", + "other":"其他", + "homeTabWidget": "组件", + "homeTabPaint": "绘制", + "homeTabKnowledge": "知识", + "homeTabTools": "工具", + "homeTabMine": "我的", + "dataManagement":"数据管理", + "userCollection":"我的收藏", + "aboutApplications":"关于应用", + "contactThisKing":"联系本王", + "appSettings":"应用设置", + "darkMode":"深色模式", + "themeColorSetting":"主题色设置", + "fontSetting":"字体设置", + "settingLanguageText": "多语言", + "codeHighlightStyle":"代码高亮样式", + "versionInformation":"版本信息", + "displayPerformanceFloatingLayer":"显示性能浮层", + + + "showFloatingTools":"显示浮动工具", + + "followSystem":"跟随系统", + "afterOpeningWillFollowTheSystemToOpenOrCloseDarkMode":"开启后,将跟随系统打开或关闭深色模式", + "manualSetting":"手动设置", + "lightMode":"浅色模式", + "settingLanguage": "设置语言", + + "appDetails":"应用详情", + "checkUpdate":"检查新版本", + "downloadNewVersion":"下载新版本", + "downloadingNewVersion":"新版本下载中...", + "currentIsNew":"当前应用已是最新版本!", + "checkDatabaseNewVersion":"检查数据库新版本", + "viewThisProjectGithubRepository":"《查看本项目Github仓库》", + + "favorite":"已收藏", + "enterComponentName":"输入组件名称", + "containerComponents":"容器组件", + "componentTavern":"组件酒肆", + "cherishedComponents":"珍藏组件", + "textImageCollection":"图文集", + "layoutCollection":"布局集", + "eventCollection":"事件集", + "animationCollection":"动画集", + "slidingCollection":"滑动集", + "decorationCollection":"装饰集", + "assemblyCollection":"组装集", + "functionCollection":"功能集", + "popupCollection":"弹出集", + "themeCollection":"主题集", + "derivativeCollection":"衍生集", + "hardToCategorize":"很难分", + "basicDrawing":"基础绘制", + "basicDrawingDesc":"收录一些基础图形绘制案例,这些案例对初涉绘制的编程者会非常友好。通过这些案例,可以学会点、线、矩形、圆、圆弧、文字、图片等基本图形的绘制方法,了解 Canvas、Paint、Path 等绘制中核心对象的使用。", + "animationGesture":"动画手势", + "animationGestureDesc":"收录一些动画和手势的绘制案例,这些案例会让绘制更具有操作性。通过这些案例,可以学会动画和手势的使用,如滑动、旋转、缩放、移动等效果,让绘制不再只是静态展现。", + "particleDrawing":"粒子绘制", + "particleDrawingDesc":"收录一些粒子相关的绘制案例,这些案例将是绘制的顶级操作。通过这些案例,可以学会如何使用粒子来绘制惊艳的视觉效果,如粒子时钟、粒子爆炸、粒子背景等效果,让绘制拥有无限可能。", + "interestingDrawing":"趣味绘制", + "interestingDrawingDesc":"收录一些比较有趣的绘制案例,让我们一起在这里一起体验绘制的乐趣、编程的乐趣和智慧的乐趣吧。", + "artGallery":"艺术画廊", + "artGalleryDesc":"收录一些殿堂级的绘制案例,这些案例将是绘制的巅峰作品,它们的没有任何的实用性,也不为任何需求而生,它们仅是因为存在而存在,是人类智慧和表达的媒介,称谓艺术。", + "drawingOfImages":"本样例介绍如何进行图片的绘制: 通过加载图片并将图片资源绘制到指定的区域。在上层绘制一批 45”倾角的栅格线,来练习线条的绘制 ", + "digitalDisplayTube":"本样例介绍如何绘制 LED 数字显示管,以此练习对路径 Path 的使用、变换、组合,以及组件封装的知识。是一个非常好的绘制案例 ", + "pathDrawing":"本样例介绍如何进行简单的路径绘制,以及画板的旋转,再结合动画让风车旋转。这是一个非常精简的绘制与动画结合的案例。 ", + "gridCoordinateSystem":"本样例介绍如何使用线路径和文字绘制网格坐标系,并将绘制对象进行封装,方便重用。坐标系也会在绘制时提供参考,入门必备。 ", + "polarCoordinateSystemOfFaces":"本样例介绍如何使用绘制平面的极坐标系,并根据函数方程收集极坐标进行绘制。 ", + "drawFunctionCurvesForPathPairs":"本样例介绍如何使用路径对函数曲线进行绘制,通过函数曲线上的少量点通过贝塞尔曲线进行拟合。 ", + "drawRegularPolygons":"本样例介绍如何在圆中收集点位,绘制正多边形,是练习绘制及形成路径的很好案例。\n特殊操作:+、- 修改边数", + "randomNumberProcessing":"本样例介绍绘制矩形及随机数处理。通过点位集合确定矩形位置信息,将其绘制出来。可以练习对数据的控制能力。", + "clockDrawing":"本样例通过时钟的绘制,练习 Flutter 中旋转刻度类型的绘制技巧,并通过动画使表盘指针转动。", + "drawSprings":" 本样例介绍如何绘制弹簧,通过触点竖直拖拽拉伸、压缩,放手时进行恢复动画,是一个很好的综合小案例。特殊操作:上下拖拽伸缩弹簧 ", + "theApplicationOfAnglesInDrawing":"本样例介绍如何根据以某个点为中心,进行旋转运动。以此学习两点间的角度在绘制中的应用。\n特殊操作:点击运行", + "usingShadersAndFilters":"本样例介绍如何在绘制中使用着色器和过滤器,并通过动画进行数值变化达到旋转流光效果。", + "pathDrawingFunctionCurve":"本样例介绍如何使用路径绘制函数曲线,并使用路径测量进行动画", + "thePathOfBingDwenDwen":"本样例会绘制 2022 年北京冬奥会吉祥物冰墩墩的路径,并使用路径测量进行动画。\n特殊操作:点击运行", + "drawCubicBesselCurve":"本样例介绍如何绘制三次贝塞尔曲线,通过触点判断某点是否激活,据此控制点的位置达到拖动控制效果。\n特殊操作:单击绘点,双击清除", + "theEffectOfAnimationCurve":"本样例通过直观的方式,来查看动画曲线 curve 的作用效果,让大家对动画有更深的理解。\n特殊操作:点击运行", + "randomParticlesAndBoundaryBouncing":" 本样例介绍如何创建随机粒子及边界反弹逻辑处理,是学习粒子运动非常好的入门案例特殊操作:单击停止/运行 ", + "particleCollision":"本样例介绍如何对个粒子进行碰撞检测,并分裂处多个粒子,是一个比较有趣的案例。\n特殊操作:单击重置", + "particle":"本样例介绍将图片使用粒子表示,并对粒子进行动画处理,达到爆炸的效果。\n特殊操作:单击运行", + "rectangleAndRandomNumbers":"本样例介绍绘制矩形及随机数处理。通过点位集合确定矩形位置信息,将其绘制出来。可以练习对数据的控制能力。\n特殊操作:点击随机生成", + "bingDwenDwen":"本样例是绘制 2022 年北京冬奥会吉祥物冰墩墩的形体,从中可以学到路径绘制、渐变色等知识。", + "pufengInjectionTest":"本样实现蒲丰投针试验的测试过程,根据概率来估算圆周率。其中可以学习到一些绘制小技巧已经数据的逻辑处理。", + "ticTacToe":"本例通过井字棋的绘制与逻辑校验,集合了手势、绘制、动画、校验等重要的技能,是一个非常好的联系案例。\n特殊操作:双击重置", + "tiledLines":"本样例根源来自generativeartistry.com的tiled-lines,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", + "joyDivision":"本样例根源来自generativeartistry.com的joy-division,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", + "cubicDisarray":"本样例根源来自generativeartistry.com的cubic-disarray,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", + "triangularMesh":"本样例根源来自generativeartistry.com的triangular-mesh,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", + "unDeuxTrois": "本样例根源来自generativeartistry.com的un-deux-trois,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", + "circlePacking":"本样例根源来自generativeartistry.com的circle-packing,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", + "hypnoticSquares":"本样例根源来自generativeartistry.com的hypnotic-squares,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", + "pietMondrian":"本样例根源来自generativeartistry.com的piet-mondrian,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", + "downloadCompressedPackage":"使用方式:\n1. 在 iconfont.cn 挑选图标,加入项目,下载压缩包。\n2. 选择 Flutter 项目地址,配置资源、产物文件位置。\n3. 点击生成代码按钮,即可生成相关代码。", + "qAIssues":"要点集录中的 QA 数据收录rUnit 以 point 为标签的 issues 中。如果需要提供数据,在 issues 中问答即可。", + "tips":"注:", + "visualSorting":"可视化排序", + "visual":"可视排序", + "insertion": "插入排序", + "bubble": "冒泡排序", + "cocktail": "鸡尾酒排序(双向冒泡排序)", + "comb": "梳排序", + "pigeonHole": "鸽巢排序", + "shell": "希尔排序", + "selection": "选择排序", + "gnome": "侏儒排序", + "cycle": "循环排序", + "heap": "堆排序", + "quick": "快速排序", + "merge": "归并排序", + "sortingAlgorithmConfiguration":"排序算法配置", + "dataCount":"数据数量(个数)", + "timeInterval":"时间间隔(微秒)", + "randomSeed":"随机种子", + "codeGeneration":"代码生成", + "generateCode":"生成代码", + "artifactLocation":"产物位置", + "codeClassLocation":"代码类存放位置", + "resourceDirectory":"资源目录", + "iconfontResourceLocation":"iconfont 资源存放位置", + "projectPath":"项目路径", + "inputProjectAddress":"请选择或输入项目地址", + "iconfontCompressedPackagePath":"Iconfont 压缩包路径", + "pleaseSelectOrInputIconfontCompressedPackagePath":"请选择或输入 iconfont 下载的压缩包路径", + "stayTuned":"敬请期待", + "iconFont":"IconFont", + "dataClass":"数据类", + "stateManagement":"状态管理", + "jsonParsing":"Json 解析", + "clickHereToJump":"点击这里跳转", + + "knowledgeTabToly":"捷特文库", + "knowledgeTabAlgo":"算法演绎", + "knowledgeTabLayout":"布局宝库", + "knowledgeTabPoint":"要点宝库", + "knowledgeConstruction":"正在建设中", + "knowledgeToJuejin":"前往掘金", + "srcPath":"源码地址", + "widgetsInn":"组件酒肆", + "likedWidgets":"珍藏组件", + "relatedComponents":"相关组件", + + "backupFavoritesCollectionData":"备份收藏集数据", + "syncFavoritesCollectionData":"同步收藏集数据", + "favoritesCollectionDataReset":"收藏集数据重置", + "resetSuccess":"重置成功!", + "dataSetBackupSuccess":"数据集备份成功!", + "dataSetBackupFailure":"数据集备份失败!", + "dataSynchronizationCopySuccess":"数据同步份成功!", + "dataSynchronizationCopyFailure":"数据同步份失败!", + "destructionRed":"毁灭之红", + "rageOrange":"愤怒之橙", + "warningYellow":"警告之黄", + "camouflageGreen":"伪装之绿", + "coldBlue":"冷漠之蓝", + "infiniteBlue":"无限之靛", + "mysteryPurple":"神秘之紫", + "destinyBlack":"归宿之黑", + + "showBackground":"显示背景", + "toly":"张风捷特烈", + "dartHandbook":"Dart 手册", + + "codeCopiedSuccessfully":"代码复制成功", + "favoriteFolderManagement":"收藏夹管理", + "assembly":"组件", + "draw":"绘制", + "knowledge":"知识", + "collection":"收藏", + "my":"我的", + "picture":"幅", + "widgetInn":"组件酒肆", + "emptySearch":"没数据,哥也没办法\n(≡ _ ≡)/~┴┴", + "searchSomething":"哥们,搜点啥...≧◔◡◔≦", + "slogan":"Flutter 的联合,编程者的联合" +} \ No newline at end of file diff --git a/modules/basic_system/l10n/lib/enum/language.dart b/modules/basic_system/l10n/lib/enum/language.dart new file mode 100644 index 000000000..7f969262d --- /dev/null +++ b/modules/basic_system/l10n/lib/enum/language.dart @@ -0,0 +1,26 @@ +import 'dart:ui'; + +enum Language { + zh_CN(locale: Locale('zh', 'CN'), label: '简体中文'), + en_US(locale: Locale('en', 'US'), label: 'English'), + // ru_RU(locale: Locale('ru','RU'), label: 'Русский'), + // fr_FR(locale: Locale('fr','FR'), label: 'Français'), + // ko_KR(locale: Locale('ko','KR'), label: '한국어'), + // de_DE(locale: Locale('de','DE'), label: 'Deutsch'), + // ja_JP(locale: Locale('ja','JP'), label: '日本語'), + // it_IT(locale: Locale('it','IT'), label: 'Italiano'), + // pt_PT(locale: Locale('pt','PT'), label: 'Português'), + // es_ES(locale: Locale('es','ES'), label: 'Español'), + ; + + String get code => + '${locale.languageCode}-${locale.countryCode}'.toLowerCase(); + + final Locale locale; + final String label; + + const Language({ + required this.locale, + required this.label, + }); +} diff --git a/modules/basic_system/l10n/lib/ext.dart b/modules/basic_system/l10n/lib/ext.dart new file mode 100644 index 000000000..86cc2fba1 --- /dev/null +++ b/modules/basic_system/l10n/lib/ext.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +import 'gen_l10n/app_localizations.dart'; +export 'gen_l10n/app_localizations.dart' show AppLocalizations; +const l10nDelegates = AppLocalizations.localizationsDelegates; +const l10nLocales = AppLocalizations.supportedLocales; + +extension AppLocalizationsX on BuildContext { + AppLocalizations get l10n => AppLocalizations.of(this); +} diff --git a/modules/basic_system/l10n/lib/gen_l10n/app_localizations.dart b/modules/basic_system/l10n/lib/gen_l10n/app_localizations.dart new file mode 100644 index 000000000..bdf016a87 --- /dev/null +++ b/modules/basic_system/l10n/lib/gen_l10n/app_localizations.dart @@ -0,0 +1,1242 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:intl/intl.dart' as intl; + +import 'app_localizations_en.dart'; +import 'app_localizations_ja.dart'; +import 'app_localizations_ko.dart'; +import 'app_localizations_ru.dart'; +import 'app_localizations_zh.dart'; + +// ignore_for_file: type=lint + +/// Callers can lookup localized strings with an instance of AppLocalizations +/// returned by `AppLocalizations.of(context)`. +/// +/// Applications need to include `AppLocalizations.delegate()` in their app's +/// `localizationDelegates` list, and the locales they support in the app's +/// `supportedLocales` list. For example: +/// +/// ```dart +/// import 'gen_l10n/app_localizations.dart'; +/// +/// return MaterialApp( +/// localizationsDelegates: AppLocalizations.localizationsDelegates, +/// supportedLocales: AppLocalizations.supportedLocales, +/// home: MyApplicationHome(), +/// ); +/// ``` +/// +/// ## Update pubspec.yaml +/// +/// Please make sure to update your pubspec.yaml to include the following +/// packages: +/// +/// ```yaml +/// dependencies: +/// # Internationalization support. +/// flutter_localizations: +/// sdk: flutter +/// intl: any # Use the pinned version from flutter_localizations +/// +/// # Rest of dependencies +/// ``` +/// +/// ## iOS Applications +/// +/// iOS applications define key application metadata, including supported +/// locales, in an Info.plist file that is built into the application bundle. +/// To configure the locales supported by your app, you’ll need to edit this +/// file. +/// +/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file. +/// Then, in the Project Navigator, open the Info.plist file under the Runner +/// project’s Runner folder. +/// +/// Next, select the Information Property List item, select Add Item from the +/// Editor menu, then select Localizations from the pop-up menu. +/// +/// Select and expand the newly-created Localizations item then, for each +/// locale your application supports, add a new item and select the locale +/// you wish to add from the pop-up menu in the Value field. This list should +/// be consistent with the languages listed in the AppLocalizations.supportedLocales +/// property. +abstract class AppLocalizations { + AppLocalizations(String locale) : localeName = intl.Intl.canonicalizedLocale(locale.toString()); + + final String localeName; + + static AppLocalizations of(BuildContext context) { + return Localizations.of(context, AppLocalizations)!; + } + + static const LocalizationsDelegate delegate = _AppLocalizationsDelegate(); + + /// A list of this localizations delegate along with the default localizations + /// delegates. + /// + /// Returns a list of localizations delegates containing this delegate along with + /// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, + /// and GlobalWidgetsLocalizations.delegate. + /// + /// Additional delegates can be added by appending to this list in + /// MaterialApp. This list does not have to be used at all if a custom list + /// of delegates is preferred or required. + static const List> localizationsDelegates = >[ + delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ]; + + /// A list of this localizations delegate's supported locales. + static const List supportedLocales = [ + Locale('en'), + Locale('ja'), + Locale('ko'), + Locale('ru'), + Locale('zh') + ]; + + /// No description provided for @widgetCollection. + /// + /// In zh, this message translates to: + /// **'组件集录'** + String get widgetCollection; + + /// No description provided for @paintCollection. + /// + /// In zh, this message translates to: + /// **'绘制集录'** + String get paintCollection; + + /// No description provided for @knowledgeCollection. + /// + /// In zh, this message translates to: + /// **'知识集锦'** + String get knowledgeCollection; + + /// No description provided for @treasureTools. + /// + /// In zh, this message translates to: + /// **'工具宝箱'** + String get treasureTools; + + /// No description provided for @collectCollection. + /// + /// In zh, this message translates to: + /// **'收藏集录'** + String get collectCollection; + + /// No description provided for @essentialCollection. + /// + /// In zh, this message translates to: + /// **'要点集录'** + String get essentialCollection; + + /// No description provided for @homeAccount. + /// + /// In zh, this message translates to: + /// **'应用信息'** + String get homeAccount; + + /// No description provided for @homeAccountTabInfo. + /// + /// In zh, this message translates to: + /// **'关于应用'** + String get homeAccountTabInfo; + + /// No description provided for @homeAccountTabMe. + /// + /// In zh, this message translates to: + /// **'联系本王'** + String get homeAccountTabMe; + + /// No description provided for @homeAccountSupport. + /// + /// In zh, this message translates to: + /// **'支持项目'** + String get homeAccountSupport; + + /// No description provided for @searchWidget. + /// + /// In zh, this message translates to: + /// **'搜索组件'** + String get searchWidget; + + /// No description provided for @stateless. + /// + /// In zh, this message translates to: + /// **'无态'** + String get stateless; + + /// No description provided for @stateful. + /// + /// In zh, this message translates to: + /// **'有态'** + String get stateful; + + /// No description provided for @single. + /// + /// In zh, this message translates to: + /// **'单渲'** + String get single; + + /// No description provided for @multi. + /// + /// In zh, this message translates to: + /// **'多渲'** + String get multi; + + /// No description provided for @sliver. + /// + /// In zh, this message translates to: + /// **'滑片'** + String get sliver; + + /// No description provided for @proxy. + /// + /// In zh, this message translates to: + /// **'代理'** + String get proxy; + + /// No description provided for @other. + /// + /// In zh, this message translates to: + /// **'其他'** + String get other; + + /// No description provided for @homeTabWidget. + /// + /// In zh, this message translates to: + /// **'组件'** + String get homeTabWidget; + + /// No description provided for @homeTabPaint. + /// + /// In zh, this message translates to: + /// **'绘制'** + String get homeTabPaint; + + /// No description provided for @homeTabKnowledge. + /// + /// In zh, this message translates to: + /// **'知识'** + String get homeTabKnowledge; + + /// No description provided for @homeTabTools. + /// + /// In zh, this message translates to: + /// **'工具'** + String get homeTabTools; + + /// No description provided for @homeTabMine. + /// + /// In zh, this message translates to: + /// **'我的'** + String get homeTabMine; + + /// No description provided for @dataManagement. + /// + /// In zh, this message translates to: + /// **'数据管理'** + String get dataManagement; + + /// No description provided for @userCollection. + /// + /// In zh, this message translates to: + /// **'我的收藏'** + String get userCollection; + + /// No description provided for @aboutApplications. + /// + /// In zh, this message translates to: + /// **'关于应用'** + String get aboutApplications; + + /// No description provided for @contactThisKing. + /// + /// In zh, this message translates to: + /// **'联系本王'** + String get contactThisKing; + + /// No description provided for @appSettings. + /// + /// In zh, this message translates to: + /// **'应用设置'** + String get appSettings; + + /// No description provided for @darkMode. + /// + /// In zh, this message translates to: + /// **'深色模式'** + String get darkMode; + + /// No description provided for @themeColorSetting. + /// + /// In zh, this message translates to: + /// **'主题色设置'** + String get themeColorSetting; + + /// No description provided for @fontSetting. + /// + /// In zh, this message translates to: + /// **'字体设置'** + String get fontSetting; + + /// No description provided for @settingLanguageText. + /// + /// In zh, this message translates to: + /// **'多语言'** + String get settingLanguageText; + + /// No description provided for @codeHighlightStyle. + /// + /// In zh, this message translates to: + /// **'代码高亮样式'** + String get codeHighlightStyle; + + /// No description provided for @versionInformation. + /// + /// In zh, this message translates to: + /// **'版本信息'** + String get versionInformation; + + /// No description provided for @displayPerformanceFloatingLayer. + /// + /// In zh, this message translates to: + /// **'显示性能浮层'** + String get displayPerformanceFloatingLayer; + + /// No description provided for @showFloatingTools. + /// + /// In zh, this message translates to: + /// **'显示浮动工具'** + String get showFloatingTools; + + /// No description provided for @followSystem. + /// + /// In zh, this message translates to: + /// **'跟随系统'** + String get followSystem; + + /// No description provided for @afterOpeningWillFollowTheSystemToOpenOrCloseDarkMode. + /// + /// In zh, this message translates to: + /// **'开启后,将跟随系统打开或关闭深色模式'** + String get afterOpeningWillFollowTheSystemToOpenOrCloseDarkMode; + + /// No description provided for @manualSetting. + /// + /// In zh, this message translates to: + /// **'手动设置'** + String get manualSetting; + + /// No description provided for @lightMode. + /// + /// In zh, this message translates to: + /// **'浅色模式'** + String get lightMode; + + /// No description provided for @settingLanguage. + /// + /// In zh, this message translates to: + /// **'设置语言'** + String get settingLanguage; + + /// No description provided for @appDetails. + /// + /// In zh, this message translates to: + /// **'应用详情'** + String get appDetails; + + /// No description provided for @checkUpdate. + /// + /// In zh, this message translates to: + /// **'检查新版本'** + String get checkUpdate; + + /// No description provided for @downloadNewVersion. + /// + /// In zh, this message translates to: + /// **'下载新版本'** + String get downloadNewVersion; + + /// No description provided for @downloadingNewVersion. + /// + /// In zh, this message translates to: + /// **'新版本下载中...'** + String get downloadingNewVersion; + + /// No description provided for @currentIsNew. + /// + /// In zh, this message translates to: + /// **'当前应用已是最新版本!'** + String get currentIsNew; + + /// No description provided for @checkDatabaseNewVersion. + /// + /// In zh, this message translates to: + /// **'检查数据库新版本'** + String get checkDatabaseNewVersion; + + /// No description provided for @viewThisProjectGithubRepository. + /// + /// In zh, this message translates to: + /// **'《查看本项目Github仓库》'** + String get viewThisProjectGithubRepository; + + /// No description provided for @favorite. + /// + /// In zh, this message translates to: + /// **'已收藏'** + String get favorite; + + /// No description provided for @enterComponentName. + /// + /// In zh, this message translates to: + /// **'输入组件名称'** + String get enterComponentName; + + /// No description provided for @containerComponents. + /// + /// In zh, this message translates to: + /// **'容器组件'** + String get containerComponents; + + /// No description provided for @componentTavern. + /// + /// In zh, this message translates to: + /// **'组件酒肆'** + String get componentTavern; + + /// No description provided for @cherishedComponents. + /// + /// In zh, this message translates to: + /// **'珍藏组件'** + String get cherishedComponents; + + /// No description provided for @textImageCollection. + /// + /// In zh, this message translates to: + /// **'图文集'** + String get textImageCollection; + + /// No description provided for @layoutCollection. + /// + /// In zh, this message translates to: + /// **'布局集'** + String get layoutCollection; + + /// No description provided for @eventCollection. + /// + /// In zh, this message translates to: + /// **'事件集'** + String get eventCollection; + + /// No description provided for @animationCollection. + /// + /// In zh, this message translates to: + /// **'动画集'** + String get animationCollection; + + /// No description provided for @slidingCollection. + /// + /// In zh, this message translates to: + /// **'滑动集'** + String get slidingCollection; + + /// No description provided for @decorationCollection. + /// + /// In zh, this message translates to: + /// **'装饰集'** + String get decorationCollection; + + /// No description provided for @assemblyCollection. + /// + /// In zh, this message translates to: + /// **'组装集'** + String get assemblyCollection; + + /// No description provided for @functionCollection. + /// + /// In zh, this message translates to: + /// **'功能集'** + String get functionCollection; + + /// No description provided for @popupCollection. + /// + /// In zh, this message translates to: + /// **'弹出集'** + String get popupCollection; + + /// No description provided for @themeCollection. + /// + /// In zh, this message translates to: + /// **'主题集'** + String get themeCollection; + + /// No description provided for @derivativeCollection. + /// + /// In zh, this message translates to: + /// **'衍生集'** + String get derivativeCollection; + + /// No description provided for @hardToCategorize. + /// + /// In zh, this message translates to: + /// **'很难分'** + String get hardToCategorize; + + /// No description provided for @basicDrawing. + /// + /// In zh, this message translates to: + /// **'基础绘制'** + String get basicDrawing; + + /// No description provided for @basicDrawingDesc. + /// + /// In zh, this message translates to: + /// **'收录一些基础图形绘制案例,这些案例对初涉绘制的编程者会非常友好。通过这些案例,可以学会点、线、矩形、圆、圆弧、文字、图片等基本图形的绘制方法,了解 Canvas、Paint、Path 等绘制中核心对象的使用。'** + String get basicDrawingDesc; + + /// No description provided for @animationGesture. + /// + /// In zh, this message translates to: + /// **'动画手势'** + String get animationGesture; + + /// No description provided for @animationGestureDesc. + /// + /// In zh, this message translates to: + /// **'收录一些动画和手势的绘制案例,这些案例会让绘制更具有操作性。通过这些案例,可以学会动画和手势的使用,如滑动、旋转、缩放、移动等效果,让绘制不再只是静态展现。'** + String get animationGestureDesc; + + /// No description provided for @particleDrawing. + /// + /// In zh, this message translates to: + /// **'粒子绘制'** + String get particleDrawing; + + /// No description provided for @particleDrawingDesc. + /// + /// In zh, this message translates to: + /// **'收录一些粒子相关的绘制案例,这些案例将是绘制的顶级操作。通过这些案例,可以学会如何使用粒子来绘制惊艳的视觉效果,如粒子时钟、粒子爆炸、粒子背景等效果,让绘制拥有无限可能。'** + String get particleDrawingDesc; + + /// No description provided for @interestingDrawing. + /// + /// In zh, this message translates to: + /// **'趣味绘制'** + String get interestingDrawing; + + /// No description provided for @interestingDrawingDesc. + /// + /// In zh, this message translates to: + /// **'收录一些比较有趣的绘制案例,让我们一起在这里一起体验绘制的乐趣、编程的乐趣和智慧的乐趣吧。'** + String get interestingDrawingDesc; + + /// No description provided for @artGallery. + /// + /// In zh, this message translates to: + /// **'艺术画廊'** + String get artGallery; + + /// No description provided for @artGalleryDesc. + /// + /// In zh, this message translates to: + /// **'收录一些殿堂级的绘制案例,这些案例将是绘制的巅峰作品,它们的没有任何的实用性,也不为任何需求而生,它们仅是因为存在而存在,是人类智慧和表达的媒介,称谓艺术。'** + String get artGalleryDesc; + + /// No description provided for @drawingOfImages. + /// + /// In zh, this message translates to: + /// **'本样例介绍如何进行图片的绘制: 通过加载图片并将图片资源绘制到指定的区域。在上层绘制一批 45”倾角的栅格线,来练习线条的绘制 '** + String get drawingOfImages; + + /// No description provided for @digitalDisplayTube. + /// + /// In zh, this message translates to: + /// **'本样例介绍如何绘制 LED 数字显示管,以此练习对路径 Path 的使用、变换、组合,以及组件封装的知识。是一个非常好的绘制案例 '** + String get digitalDisplayTube; + + /// No description provided for @pathDrawing. + /// + /// In zh, this message translates to: + /// **'本样例介绍如何进行简单的路径绘制,以及画板的旋转,再结合动画让风车旋转。这是一个非常精简的绘制与动画结合的案例。 '** + String get pathDrawing; + + /// No description provided for @gridCoordinateSystem. + /// + /// In zh, this message translates to: + /// **'本样例介绍如何使用线路径和文字绘制网格坐标系,并将绘制对象进行封装,方便重用。坐标系也会在绘制时提供参考,入门必备。 '** + String get gridCoordinateSystem; + + /// No description provided for @polarCoordinateSystemOfFaces. + /// + /// In zh, this message translates to: + /// **'本样例介绍如何使用绘制平面的极坐标系,并根据函数方程收集极坐标进行绘制。 '** + String get polarCoordinateSystemOfFaces; + + /// No description provided for @drawFunctionCurvesForPathPairs. + /// + /// In zh, this message translates to: + /// **'本样例介绍如何使用路径对函数曲线进行绘制,通过函数曲线上的少量点通过贝塞尔曲线进行拟合。 '** + String get drawFunctionCurvesForPathPairs; + + /// No description provided for @drawRegularPolygons. + /// + /// In zh, this message translates to: + /// **'本样例介绍如何在圆中收集点位,绘制正多边形,是练习绘制及形成路径的很好案例。\n特殊操作:+、- 修改边数'** + String get drawRegularPolygons; + + /// No description provided for @randomNumberProcessing. + /// + /// In zh, this message translates to: + /// **'本样例介绍绘制矩形及随机数处理。通过点位集合确定矩形位置信息,将其绘制出来。可以练习对数据的控制能力。'** + String get randomNumberProcessing; + + /// No description provided for @clockDrawing. + /// + /// In zh, this message translates to: + /// **'本样例通过时钟的绘制,练习 Flutter 中旋转刻度类型的绘制技巧,并通过动画使表盘指针转动。'** + String get clockDrawing; + + /// No description provided for @drawSprings. + /// + /// In zh, this message translates to: + /// **' 本样例介绍如何绘制弹簧,通过触点竖直拖拽拉伸、压缩,放手时进行恢复动画,是一个很好的综合小案例。特殊操作:上下拖拽伸缩弹簧 '** + String get drawSprings; + + /// No description provided for @theApplicationOfAnglesInDrawing. + /// + /// In zh, this message translates to: + /// **'本样例介绍如何根据以某个点为中心,进行旋转运动。以此学习两点间的角度在绘制中的应用。\n特殊操作:点击运行'** + String get theApplicationOfAnglesInDrawing; + + /// No description provided for @usingShadersAndFilters. + /// + /// In zh, this message translates to: + /// **'本样例介绍如何在绘制中使用着色器和过滤器,并通过动画进行数值变化达到旋转流光效果。'** + String get usingShadersAndFilters; + + /// No description provided for @pathDrawingFunctionCurve. + /// + /// In zh, this message translates to: + /// **'本样例介绍如何使用路径绘制函数曲线,并使用路径测量进行动画'** + String get pathDrawingFunctionCurve; + + /// No description provided for @thePathOfBingDwenDwen. + /// + /// In zh, this message translates to: + /// **'本样例会绘制 2022 年北京冬奥会吉祥物冰墩墩的路径,并使用路径测量进行动画。\n特殊操作:点击运行'** + String get thePathOfBingDwenDwen; + + /// No description provided for @drawCubicBesselCurve. + /// + /// In zh, this message translates to: + /// **'本样例介绍如何绘制三次贝塞尔曲线,通过触点判断某点是否激活,据此控制点的位置达到拖动控制效果。\n特殊操作:单击绘点,双击清除'** + String get drawCubicBesselCurve; + + /// No description provided for @theEffectOfAnimationCurve. + /// + /// In zh, this message translates to: + /// **'本样例通过直观的方式,来查看动画曲线 curve 的作用效果,让大家对动画有更深的理解。\n特殊操作:点击运行'** + String get theEffectOfAnimationCurve; + + /// No description provided for @randomParticlesAndBoundaryBouncing. + /// + /// In zh, this message translates to: + /// **' 本样例介绍如何创建随机粒子及边界反弹逻辑处理,是学习粒子运动非常好的入门案例特殊操作:单击停止/运行 '** + String get randomParticlesAndBoundaryBouncing; + + /// No description provided for @particleCollision. + /// + /// In zh, this message translates to: + /// **'本样例介绍如何对个粒子进行碰撞检测,并分裂处多个粒子,是一个比较有趣的案例。\n特殊操作:单击重置'** + String get particleCollision; + + /// No description provided for @particle. + /// + /// In zh, this message translates to: + /// **'本样例介绍将图片使用粒子表示,并对粒子进行动画处理,达到爆炸的效果。\n特殊操作:单击运行'** + String get particle; + + /// No description provided for @rectangleAndRandomNumbers. + /// + /// In zh, this message translates to: + /// **'本样例介绍绘制矩形及随机数处理。通过点位集合确定矩形位置信息,将其绘制出来。可以练习对数据的控制能力。\n特殊操作:点击随机生成'** + String get rectangleAndRandomNumbers; + + /// No description provided for @bingDwenDwen. + /// + /// In zh, this message translates to: + /// **'本样例是绘制 2022 年北京冬奥会吉祥物冰墩墩的形体,从中可以学到路径绘制、渐变色等知识。'** + String get bingDwenDwen; + + /// No description provided for @pufengInjectionTest. + /// + /// In zh, this message translates to: + /// **'本样实现蒲丰投针试验的测试过程,根据概率来估算圆周率。其中可以学习到一些绘制小技巧已经数据的逻辑处理。'** + String get pufengInjectionTest; + + /// No description provided for @ticTacToe. + /// + /// In zh, this message translates to: + /// **'本例通过井字棋的绘制与逻辑校验,集合了手势、绘制、动画、校验等重要的技能,是一个非常好的联系案例。\n特殊操作:双击重置'** + String get ticTacToe; + + /// No description provided for @tiledLines. + /// + /// In zh, this message translates to: + /// **'本样例根源来自generativeartistry.com的tiled-lines,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry'** + String get tiledLines; + + /// No description provided for @joyDivision. + /// + /// In zh, this message translates to: + /// **'本样例根源来自generativeartistry.com的joy-division,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry'** + String get joyDivision; + + /// No description provided for @cubicDisarray. + /// + /// In zh, this message translates to: + /// **'本样例根源来自generativeartistry.com的cubic-disarray,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry'** + String get cubicDisarray; + + /// No description provided for @triangularMesh. + /// + /// In zh, this message translates to: + /// **'本样例根源来自generativeartistry.com的triangular-mesh,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry'** + String get triangularMesh; + + /// No description provided for @unDeuxTrois. + /// + /// In zh, this message translates to: + /// **'本样例根源来自generativeartistry.com的un-deux-trois,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry'** + String get unDeuxTrois; + + /// No description provided for @circlePacking. + /// + /// In zh, this message translates to: + /// **'本样例根源来自generativeartistry.com的circle-packing,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry'** + String get circlePacking; + + /// No description provided for @hypnoticSquares. + /// + /// In zh, this message translates to: + /// **'本样例根源来自generativeartistry.com的hypnotic-squares,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry'** + String get hypnoticSquares; + + /// No description provided for @pietMondrian. + /// + /// In zh, this message translates to: + /// **'本样例根源来自generativeartistry.com的piet-mondrian,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry'** + String get pietMondrian; + + /// No description provided for @downloadCompressedPackage. + /// + /// In zh, this message translates to: + /// **'使用方式:\n1. 在 iconfont.cn 挑选图标,加入项目,下载压缩包。\n2. 选择 Flutter 项目地址,配置资源、产物文件位置。\n3. 点击生成代码按钮,即可生成相关代码。'** + String get downloadCompressedPackage; + + /// No description provided for @qAIssues. + /// + /// In zh, this message translates to: + /// **'要点集录中的 QA 数据收录rUnit 以 point 为标签的 issues 中。如果需要提供数据,在 issues 中问答即可。'** + String get qAIssues; + + /// No description provided for @tips. + /// + /// In zh, this message translates to: + /// **'注:'** + String get tips; + + /// No description provided for @visualSorting. + /// + /// In zh, this message translates to: + /// **'可视化排序'** + String get visualSorting; + + /// No description provided for @visual. + /// + /// In zh, this message translates to: + /// **'可视排序'** + String get visual; + + /// No description provided for @insertion. + /// + /// In zh, this message translates to: + /// **'插入排序'** + String get insertion; + + /// No description provided for @bubble. + /// + /// In zh, this message translates to: + /// **'冒泡排序'** + String get bubble; + + /// No description provided for @cocktail. + /// + /// In zh, this message translates to: + /// **'鸡尾酒排序(双向冒泡排序)'** + String get cocktail; + + /// No description provided for @comb. + /// + /// In zh, this message translates to: + /// **'梳排序'** + String get comb; + + /// No description provided for @pigeonHole. + /// + /// In zh, this message translates to: + /// **'鸽巢排序'** + String get pigeonHole; + + /// No description provided for @shell. + /// + /// In zh, this message translates to: + /// **'希尔排序'** + String get shell; + + /// No description provided for @selection. + /// + /// In zh, this message translates to: + /// **'选择排序'** + String get selection; + + /// No description provided for @gnome. + /// + /// In zh, this message translates to: + /// **'侏儒排序'** + String get gnome; + + /// No description provided for @cycle. + /// + /// In zh, this message translates to: + /// **'循环排序'** + String get cycle; + + /// No description provided for @heap. + /// + /// In zh, this message translates to: + /// **'堆排序'** + String get heap; + + /// No description provided for @quick. + /// + /// In zh, this message translates to: + /// **'快速排序'** + String get quick; + + /// No description provided for @merge. + /// + /// In zh, this message translates to: + /// **'归并排序'** + String get merge; + + /// No description provided for @sortingAlgorithmConfiguration. + /// + /// In zh, this message translates to: + /// **'排序算法配置'** + String get sortingAlgorithmConfiguration; + + /// No description provided for @dataCount. + /// + /// In zh, this message translates to: + /// **'数据数量(个数)'** + String get dataCount; + + /// No description provided for @timeInterval. + /// + /// In zh, this message translates to: + /// **'时间间隔(微秒)'** + String get timeInterval; + + /// No description provided for @randomSeed. + /// + /// In zh, this message translates to: + /// **'随机种子'** + String get randomSeed; + + /// No description provided for @codeGeneration. + /// + /// In zh, this message translates to: + /// **'代码生成'** + String get codeGeneration; + + /// No description provided for @generateCode. + /// + /// In zh, this message translates to: + /// **'生成代码'** + String get generateCode; + + /// No description provided for @artifactLocation. + /// + /// In zh, this message translates to: + /// **'产物位置'** + String get artifactLocation; + + /// No description provided for @codeClassLocation. + /// + /// In zh, this message translates to: + /// **'代码类存放位置'** + String get codeClassLocation; + + /// No description provided for @resourceDirectory. + /// + /// In zh, this message translates to: + /// **'资源目录'** + String get resourceDirectory; + + /// No description provided for @iconfontResourceLocation. + /// + /// In zh, this message translates to: + /// **'iconfont 资源存放位置'** + String get iconfontResourceLocation; + + /// No description provided for @projectPath. + /// + /// In zh, this message translates to: + /// **'项目路径'** + String get projectPath; + + /// No description provided for @inputProjectAddress. + /// + /// In zh, this message translates to: + /// **'请选择或输入项目地址'** + String get inputProjectAddress; + + /// No description provided for @iconfontCompressedPackagePath. + /// + /// In zh, this message translates to: + /// **'Iconfont 压缩包路径'** + String get iconfontCompressedPackagePath; + + /// No description provided for @pleaseSelectOrInputIconfontCompressedPackagePath. + /// + /// In zh, this message translates to: + /// **'请选择或输入 iconfont 下载的压缩包路径'** + String get pleaseSelectOrInputIconfontCompressedPackagePath; + + /// No description provided for @stayTuned. + /// + /// In zh, this message translates to: + /// **'敬请期待'** + String get stayTuned; + + /// No description provided for @iconFont. + /// + /// In zh, this message translates to: + /// **'IconFont'** + String get iconFont; + + /// No description provided for @dataClass. + /// + /// In zh, this message translates to: + /// **'数据类'** + String get dataClass; + + /// No description provided for @stateManagement. + /// + /// In zh, this message translates to: + /// **'状态管理'** + String get stateManagement; + + /// No description provided for @jsonParsing. + /// + /// In zh, this message translates to: + /// **'Json 解析'** + String get jsonParsing; + + /// No description provided for @clickHereToJump. + /// + /// In zh, this message translates to: + /// **'点击这里跳转'** + String get clickHereToJump; + + /// No description provided for @knowledgeTabToly. + /// + /// In zh, this message translates to: + /// **'捷特文库'** + String get knowledgeTabToly; + + /// No description provided for @knowledgeTabAlgo. + /// + /// In zh, this message translates to: + /// **'算法演绎'** + String get knowledgeTabAlgo; + + /// No description provided for @knowledgeTabLayout. + /// + /// In zh, this message translates to: + /// **'布局宝库'** + String get knowledgeTabLayout; + + /// No description provided for @knowledgeTabPoint. + /// + /// In zh, this message translates to: + /// **'要点宝库'** + String get knowledgeTabPoint; + + /// No description provided for @knowledgeConstruction. + /// + /// In zh, this message translates to: + /// **'正在建设中'** + String get knowledgeConstruction; + + /// No description provided for @knowledgeToJuejin. + /// + /// In zh, this message translates to: + /// **'前往掘金'** + String get knowledgeToJuejin; + + /// No description provided for @srcPath. + /// + /// In zh, this message translates to: + /// **'源码地址'** + String get srcPath; + + /// No description provided for @widgetsInn. + /// + /// In zh, this message translates to: + /// **'组件酒肆'** + String get widgetsInn; + + /// No description provided for @likedWidgets. + /// + /// In zh, this message translates to: + /// **'珍藏组件'** + String get likedWidgets; + + /// No description provided for @relatedComponents. + /// + /// In zh, this message translates to: + /// **'相关组件'** + String get relatedComponents; + + /// No description provided for @backupFavoritesCollectionData. + /// + /// In zh, this message translates to: + /// **'备份收藏集数据'** + String get backupFavoritesCollectionData; + + /// No description provided for @syncFavoritesCollectionData. + /// + /// In zh, this message translates to: + /// **'同步收藏集数据'** + String get syncFavoritesCollectionData; + + /// No description provided for @favoritesCollectionDataReset. + /// + /// In zh, this message translates to: + /// **'收藏集数据重置'** + String get favoritesCollectionDataReset; + + /// No description provided for @resetSuccess. + /// + /// In zh, this message translates to: + /// **'重置成功!'** + String get resetSuccess; + + /// No description provided for @dataSetBackupSuccess. + /// + /// In zh, this message translates to: + /// **'数据集备份成功!'** + String get dataSetBackupSuccess; + + /// No description provided for @dataSetBackupFailure. + /// + /// In zh, this message translates to: + /// **'数据集备份失败!'** + String get dataSetBackupFailure; + + /// No description provided for @dataSynchronizationCopySuccess. + /// + /// In zh, this message translates to: + /// **'数据同步份成功!'** + String get dataSynchronizationCopySuccess; + + /// No description provided for @dataSynchronizationCopyFailure. + /// + /// In zh, this message translates to: + /// **'数据同步份失败!'** + String get dataSynchronizationCopyFailure; + + /// No description provided for @destructionRed. + /// + /// In zh, this message translates to: + /// **'毁灭之红'** + String get destructionRed; + + /// No description provided for @rageOrange. + /// + /// In zh, this message translates to: + /// **'愤怒之橙'** + String get rageOrange; + + /// No description provided for @warningYellow. + /// + /// In zh, this message translates to: + /// **'警告之黄'** + String get warningYellow; + + /// No description provided for @camouflageGreen. + /// + /// In zh, this message translates to: + /// **'伪装之绿'** + String get camouflageGreen; + + /// No description provided for @coldBlue. + /// + /// In zh, this message translates to: + /// **'冷漠之蓝'** + String get coldBlue; + + /// No description provided for @infiniteBlue. + /// + /// In zh, this message translates to: + /// **'无限之靛'** + String get infiniteBlue; + + /// No description provided for @mysteryPurple. + /// + /// In zh, this message translates to: + /// **'神秘之紫'** + String get mysteryPurple; + + /// No description provided for @destinyBlack. + /// + /// In zh, this message translates to: + /// **'归宿之黑'** + String get destinyBlack; + + /// No description provided for @showBackground. + /// + /// In zh, this message translates to: + /// **'显示背景'** + String get showBackground; + + /// No description provided for @toly. + /// + /// In zh, this message translates to: + /// **'张风捷特烈'** + String get toly; + + /// No description provided for @dartHandbook. + /// + /// In zh, this message translates to: + /// **'Dart 手册'** + String get dartHandbook; + + /// No description provided for @codeCopiedSuccessfully. + /// + /// In zh, this message translates to: + /// **'代码复制成功'** + String get codeCopiedSuccessfully; + + /// No description provided for @favoriteFolderManagement. + /// + /// In zh, this message translates to: + /// **'收藏夹管理'** + String get favoriteFolderManagement; + + /// No description provided for @assembly. + /// + /// In zh, this message translates to: + /// **'组件'** + String get assembly; + + /// No description provided for @draw. + /// + /// In zh, this message translates to: + /// **'绘制'** + String get draw; + + /// No description provided for @knowledge. + /// + /// In zh, this message translates to: + /// **'知识'** + String get knowledge; + + /// No description provided for @collection. + /// + /// In zh, this message translates to: + /// **'收藏'** + String get collection; + + /// No description provided for @my. + /// + /// In zh, this message translates to: + /// **'我的'** + String get my; + + /// No description provided for @picture. + /// + /// In zh, this message translates to: + /// **'幅'** + String get picture; + + /// No description provided for @widgetInn. + /// + /// In zh, this message translates to: + /// **'组件酒肆'** + String get widgetInn; + + /// No description provided for @emptySearch. + /// + /// In zh, this message translates to: + /// **'没数据,哥也没办法\n(≡ _ ≡)/~┴┴'** + String get emptySearch; + + /// No description provided for @searchSomething. + /// + /// In zh, this message translates to: + /// **'哥们,搜点啥...≧◔◡◔≦'** + String get searchSomething; + + /// No description provided for @slogan. + /// + /// In zh, this message translates to: + /// **'Flutter 的联合,编程者的联合'** + String get slogan; +} + +class _AppLocalizationsDelegate extends LocalizationsDelegate { + const _AppLocalizationsDelegate(); + + @override + Future load(Locale locale) { + return SynchronousFuture(lookupAppLocalizations(locale)); + } + + @override + bool isSupported(Locale locale) => ['en', 'ja', 'ko', 'ru', 'zh'].contains(locale.languageCode); + + @override + bool shouldReload(_AppLocalizationsDelegate old) => false; +} + +AppLocalizations lookupAppLocalizations(Locale locale) { + + + // Lookup logic when only language code is specified. + switch (locale.languageCode) { + case 'en': return AppLocalizationsEn(); + case 'ja': return AppLocalizationsJa(); + case 'ko': return AppLocalizationsKo(); + case 'ru': return AppLocalizationsRu(); + case 'zh': return AppLocalizationsZh(); + } + + throw FlutterError( + 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' + 'an issue with the localizations generation tool. Please file an issue ' + 'on GitHub with a reproducible sample app and the gen-l10n configuration ' + 'that was used.' + ); +} diff --git a/modules/basic_system/l10n/lib/gen_l10n/app_localizations_en.dart b/modules/basic_system/l10n/lib/gen_l10n/app_localizations_en.dart new file mode 100644 index 000000000..f665029d2 --- /dev/null +++ b/modules/basic_system/l10n/lib/gen_l10n/app_localizations_en.dart @@ -0,0 +1,560 @@ +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for English (`en`). +class AppLocalizationsEn extends AppLocalizations { + AppLocalizationsEn([String locale = 'en']) : super(locale); + + @override + String get widgetCollection => 'Widgets'; + + @override + String get paintCollection => 'Painter'; + + @override + String get knowledgeCollection => 'Knowledge'; + + @override + String get treasureTools => 'Treasure'; + + @override + String get collectCollection => 'Collection'; + + @override + String get essentialCollection => 'KeypointsCollection'; + + @override + String get homeAccount => 'Application'; + + @override + String get homeAccountTabInfo => 'About App'; + + @override + String get homeAccountTabMe => 'Contact Me'; + + @override + String get homeAccountSupport => 'Support Project'; + + @override + String get searchWidget => 'search widget ...'; + + @override + String get stateless => 'Stateless'; + + @override + String get stateful => 'Stateful'; + + @override + String get single => 'Single'; + + @override + String get multi => 'Multi'; + + @override + String get sliver => 'Sliver'; + + @override + String get proxy => 'Proxy'; + + @override + String get other => 'Other'; + + @override + String get homeTabWidget => 'Widget'; + + @override + String get homeTabPaint => 'Paint'; + + @override + String get homeTabKnowledge => 'Knowledge'; + + @override + String get homeTabTools => 'Treasure'; + + @override + String get homeTabMine => 'Mine'; + + @override + String get dataManagement => 'Data management'; + + @override + String get userCollection => 'Collection'; + + @override + String get aboutApplications => 'About Applications'; + + @override + String get contactThisKing => 'Contact this king'; + + @override + String get appSettings => 'Application Setting'; + + @override + String get darkMode => 'Dark Mode '; + + @override + String get themeColorSetting => 'Theme Color'; + + @override + String get fontSetting => 'Font Setting'; + + @override + String get settingLanguageText => 'Setting Language'; + + @override + String get codeHighlightStyle => 'Code Highlight Style'; + + @override + String get versionInformation => 'App Version'; + + @override + String get displayPerformanceFloatingLayer => 'Performance Layer'; + + @override + String get showFloatingTools => 'Show floating tools'; + + @override + String get followSystem => 'Follow system'; + + @override + String get afterOpeningWillFollowTheSystemToOpenOrCloseDarkMode => 'After activation, it will follow the system to turn on or off dark mode'; + + @override + String get manualSetting => 'Manual settings'; + + @override + String get lightMode => 'Light mode'; + + @override + String get settingLanguage => 'Language Setting'; + + @override + String get appDetails => 'Application details'; + + @override + String get checkUpdate => 'Check New Version'; + + @override + String get downloadNewVersion => 'Download New Version'; + + @override + String get downloadingNewVersion => 'Downloading New Version ...'; + + @override + String get currentIsNew => 'There is the latest version of FlutterUnit!'; + + @override + String get checkDatabaseNewVersion => 'Check for new versions of the database'; + + @override + String get viewThisProjectGithubRepository => '《View the Github Repository for this project》'; + + @override + String get favorite => 'Collected'; + + @override + String get enterComponentName => 'Input widget name'; + + @override + String get containerComponents => 'Container components'; + + @override + String get componentTavern => 'Component Tavern'; + + @override + String get cherishedComponents => 'Treasure components'; + + @override + String get textImageCollection => 'TextImageCollection'; + + @override + String get layoutCollection => 'LayoutCollection'; + + @override + String get eventCollection => 'EventCollection'; + + @override + String get animationCollection => 'AnimationCollection'; + + @override + String get slidingCollection => 'SlidingCollection'; + + @override + String get decorationCollection => 'DecorativeCollection'; + + @override + String get assemblyCollection => 'AssemblyCollection'; + + @override + String get functionCollection => 'FeatureCollection'; + + @override + String get popupCollection => 'Pop upCollection'; + + @override + String get themeCollection => 'ThemeCollection'; + + @override + String get derivativeCollection => 'DerivativeCollection'; + + @override + String get hardToCategorize => 'It\'s hard to distinguish'; + + @override + String get basicDrawing => 'Basic drawing'; + + @override + String get basicDrawingDesc => 'Including some basic graphics drawing examples would be very friendly to beginners in programming. Through these examples, one can learn how to draw basic shapes such as points, lines, rectangles, circles, arcs, text, images, etc., and understand the usage of core objects in drawing like Canvas, Paint, Path, etc.'; + + @override + String get animationGesture => 'Animated gestures'; + + @override + String get animationGestureDesc => 'Includes some drawing examples of animation and gestures, which make drawing more interactive. Through these examples, one can learn the usage of animation and gestures, such as sliding, rotating, scaling, moving effects, etc., making drawing not just static presentation.'; + + @override + String get particleDrawing => 'Particle drawing'; + + @override + String get particleDrawingDesc => 'Includes some drawing examples related to particles, which are top-level operations in drawing. Through these examples, one can learn how to use particles to create stunning visual effects, such as particle clocks, particle explosions, particle backgrounds, etc., giving drawing endless possibilities.'; + + @override + String get interestingDrawing => 'Fun drawing'; + + @override + String get interestingDrawingDesc => 'Includes some fun drawing examples, let\'s experience the joy of drawing, programming, and intelligence together here.'; + + @override + String get artGallery => 'Art galleries '; + + @override + String get artGalleryDesc => 'Includes some hall-level drawing examples, which are pinnacle works of drawing. They have no practicality and are not born for any demand. They exist only because they exist, serving as a medium for human wisdom and expression, called art.'; + + @override + String get drawingOfImages => 'This example explains how to draw images: by loading images and drawing image resources to a specified area. Draw a batch of 45 \"angled grid lines on the upper layer to practice drawing the lines '; + + @override + String get digitalDisplayTube => 'This example introduces how to draw LED digital display tubes to practice the use, transformation, combination of path paths, and knowledge of component packaging. It is a very good drawing case '; + + @override + String get pathDrawing => 'This example introduces how to perform simple path drawing, rotate the drawing board, and combine animation to make the windmill rotate. This is a very concise case of combining drawing and animation. '; + + @override + String get gridCoordinateSystem => 'This example explains how to use line diameter and text to draw a grid coordinate system, and encapsulate the drawn objects for easy reuse. The coordinate system also provides reference during drawing, which is essential for beginners.'; + + @override + String get polarCoordinateSystemOfFaces => 'This example explains how to use a polar coordinate system to draw a plane and collect polar coordinates based on a function equation for drawing. '; + + @override + String get drawFunctionCurvesForPathPairs => 'This example explains how to use a path to draw a function curve, fitting a small number of points on the function curve through a Bessel curve. '; + + @override + String get drawRegularPolygons => 'This example introduces how to collect points in a circle and draw regular polygons, which is a good example for practicing drawing and forming paths. \n Special operations:+, - Modify the number of edges'; + + @override + String get randomNumberProcessing => 'This example introduces drawing rectangles and handling random numbers. Determine the rectangular position information through a set of points and draw it. Can practice the ability to control data.'; + + @override + String get clockDrawing => 'This example uses the drawing of a clock to practice the drawing technique of rotating scale types in Flutter, and uses animation to rotate the dial pointer.'; + + @override + String get drawSprings => ' This example introduces how to draw a spring, stretch and compress it vertically through the contact points, and restore the animation when releasing it. It is a good comprehensive small case. Special operation: Drag the telescopic spring up and down '; + + @override + String get theApplicationOfAnglesInDrawing => 'This example explains how to perform rotational motion based on a point as the center. Learn the application of the angle between two points in drawing. \n Special operation: Click to run'; + + @override + String get usingShadersAndFilters => 'This example explains how to use shaders and filters in painting, and achieve a rotating streamer effect through animation with numerical variations.'; + + @override + String get pathDrawingFunctionCurve => 'This example explains how to use path to draw function curves and use path measurement for animation'; + + @override + String get thePathOfBingDwenDwen => 'This sample will draw the path of the mascot Bing Dwen Dwen for the 2022 Beijing Winter Olympics and use path measurement for animation. \n Special operation: Click to run'; + + @override + String get drawCubicBesselCurve => 'This example introduces how to draw a cubic Bezier curve, determine whether a point is activated through the contacts, and use this to control the position of the point to achieve drag control effect. \n Special operation: Click on the drawing point, double-click to clear it'; + + @override + String get theEffectOfAnimationCurve => 'This example provides an intuitive way to examine the effect of animation curves, allowing everyone to have a deeper understanding of animation. \n Special operation: Click to run'; + + @override + String get randomParticlesAndBoundaryBouncing => 'This example introduces how to create random particles and handle boundary bounce logic, which is a great starting point for learning particle motion. Special operation: click to stop running '; + + @override + String get particleCollision => 'This example introduces how to perform collision detection on a particle and split multiple particles, which is an interesting case. \n Special operation: Click Reset'; + + @override + String get particle => 'This example introduces using particles to represent images and animating them to achieve explosive effects. \nSpecial operation: Click to run'; + + @override + String get rectangleAndRandomNumbers => 'This example introduces drawing rectangles and handling random numbers. Determine the rectangular position information through a set of points and draw it. Can practice the ability to control data. \nSpecial operation: Click to randomly generate'; + + @override + String get bingDwenDwen => 'This example is to draw the shape of the mascot Bing Dwen Dwen for the 2022 Beijing Winter Olympics, from which you can learn knowledge such as path drawing and gradient colors.'; + + @override + String get pufengInjectionTest => 'This sample implements the testing process of the Pufeng needle injection test, estimating pi based on probability. You can learn some drawing tips and logical processing of data.'; + + @override + String get ticTacToe => 'This example combines important skills such as gestures, drawing, animation, and verification through the drawing and logical verification of the Chinese checkerboard, making it a very good case study. \n Special operation: Double click to reset'; + + @override + String get tiledLines => 'The root cause of this example comes from generateArchistry.com tiled-lines,Implemented by xrr 2016 using Flutter。Warehouse address:flutter-generative-artistry'; + + @override + String get joyDivision => 'The root cause of this example comes from generateArchistry.com joy-division,Implemented by xrr 2016 using Flutter。Warehouse address:flutter-generative-artistry'; + + @override + String get cubicDisarray => 'The root cause of this example comes from generateArchistry.com cubic-disarray,Implemented by xrr 2016 using Flutter。Warehouse address:flutter-generative-artistry'; + + @override + String get triangularMesh => 'The root cause of this example comes from generateArchistry.com triangular-mesh,Implemented by xrr 2016 using Flutter。Warehouse address:flutter-generative-artistry'; + + @override + String get unDeuxTrois => 'The root cause of this example comes from generateArchistry.com un-deux-trois,Implemented by xrr 2016 using Flutter。Warehouse address:flutter-generative-artistry'; + + @override + String get circlePacking => 'The root cause of this example comes from generateArchistry.com circle-packing,Implemented by xrr 2016 using Flutter。Warehouse address:flutter-generative-artistry'; + + @override + String get hypnoticSquares => 'The root cause of this example comes from generateArchistry.com hypnotic-squares,Implemented by xrr 2016 using Flutter。Warehouse address:flutter-generative-artistry'; + + @override + String get pietMondrian => 'The root cause of this example comes from generateArchistry.com piet-mondrian,Implemented by xrr 2016 using Flutter。Warehouse address:flutter-generative-artistry'; + + @override + String get downloadCompressedPackage => 'Usage: \n1. Select the icon in iconfont.cn, add the project, and download the compressed file. \n2. Select the Flutter project address, configure resource and product file locations. \n3.Click the Generate Code button to generate the relevant code.'; + + @override + String get qAIssues => 'The QA data in the key points collection is included in FlutterUnit\'s issues labeled with points. If data needs to be provided, simply ask and answer in the issues section.'; + + @override + String get tips => 'tips:'; + + @override + String get visualSorting => 'Visual sorting'; + + @override + String get visual => 'Visual sorting'; + + @override + String get insertion => 'Insert sorting'; + + @override + String get bubble => 'Bubble sorting'; + + @override + String get cocktail => 'Cocktail sorting (bidirectional bubble sorting)'; + + @override + String get comb => 'Comb sorting'; + + @override + String get pigeonHole => 'Pigeonhole sorting '; + + @override + String get shell => 'Shell sorting '; + + @override + String get selection => 'Select sorting'; + + @override + String get gnome => 'Dwarf Sorting'; + + @override + String get cycle => 'Circular sorting'; + + @override + String get heap => 'Heap sorting'; + + @override + String get quick => 'Quick sorting'; + + @override + String get merge => 'Merge sorting'; + + @override + String get sortingAlgorithmConfiguration => 'Sorting algorithm configuration'; + + @override + String get dataCount => 'Data quantity (number)'; + + @override + String get timeInterval => 'Time interval (microseconds)'; + + @override + String get randomSeed => 'Random Seed'; + + @override + String get codeGeneration => 'Code generation'; + + @override + String get generateCode => 'Generate Code'; + + @override + String get artifactLocation => 'Product location'; + + @override + String get codeClassLocation => 'Code class storage location'; + + @override + String get resourceDirectory => 'Resource Catalog'; + + @override + String get iconfontResourceLocation => 'iconfont Resource storage location'; + + @override + String get projectPath => 'Project Path'; + + @override + String get inputProjectAddress => 'Please select or enter the project address'; + + @override + String get iconfontCompressedPackagePath => 'Iconfont Compressed package path'; + + @override + String get pleaseSelectOrInputIconfontCompressedPackagePath => 'Please select or enter the compressed file path for iconfont download'; + + @override + String get stayTuned => 'Stay tuned'; + + @override + String get iconFont => 'IconFont'; + + @override + String get dataClass => 'Data class'; + + @override + String get stateManagement => 'State management'; + + @override + String get jsonParsing => 'Json Parsing'; + + @override + String get clickHereToJump => 'Click here to jump to'; + + @override + String get knowledgeTabToly => 'Toly Articles'; + + @override + String get knowledgeTabAlgo => 'Algo Player'; + + @override + String get knowledgeTabLayout => 'Layout Treasury'; + + @override + String get knowledgeTabPoint => 'Key Points'; + + @override + String get knowledgeConstruction => 'In Construction'; + + @override + String get knowledgeToJuejin => 'To Juejin'; + + @override + String get srcPath => 'Source Path'; + + @override + String get widgetsInn => 'Widgets Inn'; + + @override + String get likedWidgets => 'Liked Widgets'; + + @override + String get relatedComponents => 'Related Widgets'; + + @override + String get backupFavoritesCollectionData => 'Backup Collection Data'; + + @override + String get syncFavoritesCollectionData => 'Synchronize collection data'; + + @override + String get favoritesCollectionDataReset => 'Reset Collection Data'; + + @override + String get resetSuccess => 'Reset successful!'; + + @override + String get dataSetBackupSuccess => 'Dataset backup successful!'; + + @override + String get dataSetBackupFailure => 'Dataset backup failed!'; + + @override + String get dataSynchronizationCopySuccess => 'Data synchronization successful!'; + + @override + String get dataSynchronizationCopyFailure => 'Data synchronization failed!'; + + @override + String get destructionRed => 'Destruction Red '; + + @override + String get rageOrange => 'Anger Orange'; + + @override + String get warningYellow => 'Warning Yellow'; + + @override + String get camouflageGreen => 'Disguising Green'; + + @override + String get coldBlue => 'Indifferent Blue'; + + @override + String get infiniteBlue => 'Infinite Indigo'; + + @override + String get mysteryPurple => 'Mysterious Purple'; + + @override + String get destinyBlack => 'Destiny Black'; + + @override + String get showBackground => 'Display Background'; + + @override + String get toly => 'toly'; + + @override + String get dartHandbook => 'Dart Handbook'; + + @override + String get codeCopiedSuccessfully => 'Code copied successfully'; + + @override + String get favoriteFolderManagement => 'Favorite folder management'; + + @override + String get assembly => 'Assembly'; + + @override + String get draw => 'Draw'; + + @override + String get knowledge => 'Knowledge'; + + @override + String get collection => 'Collection'; + + @override + String get my => 'My'; + + @override + String get picture => 'pics'; + + @override + String get widgetInn => 'Widget Collection'; + + @override + String get emptySearch => 'No Result \n(≡ _ ≡)/~┴┴'; + + @override + String get searchSomething => 'Search Something ≧◔◡◔≦'; + + @override + String get slogan => 'The unity of flutter, The unity of coder.'; +} diff --git a/modules/basic_system/l10n/lib/gen_l10n/app_localizations_ja.dart b/modules/basic_system/l10n/lib/gen_l10n/app_localizations_ja.dart new file mode 100644 index 000000000..8c4d33a2a --- /dev/null +++ b/modules/basic_system/l10n/lib/gen_l10n/app_localizations_ja.dart @@ -0,0 +1,560 @@ +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Japanese (`ja`). +class AppLocalizationsJa extends AppLocalizations { + AppLocalizationsJa([String locale = 'ja']) : super(locale); + + @override + String get widgetCollection => 'ウィジェットコレクション'; + + @override + String get paintCollection => '描画コレクション'; + + @override + String get knowledgeCollection => '知識集'; + + @override + String get treasureTools => 'ツールボックス'; + + @override + String get collectCollection => 'コレクション集'; + + @override + String get essentialCollection => '要点集'; + + @override + String get homeAccount => 'アプリ情報'; + + @override + String get homeAccountTabInfo => 'アプリについて'; + + @override + String get homeAccountTabMe => '本王に連絡'; + + @override + String get homeAccountSupport => 'プロジェクトをサポート'; + + @override + String get searchWidget => 'ウィジェットを検索'; + + @override + String get stateless => 'ステートレス'; + + @override + String get stateful => 'ステートフル'; + + @override + String get single => 'シングルレンダリング'; + + @override + String get multi => 'マルチレンダリング'; + + @override + String get sliver => 'スライバー'; + + @override + String get proxy => 'プロキシ'; + + @override + String get other => 'その他'; + + @override + String get homeTabWidget => 'ウィジェット'; + + @override + String get homeTabPaint => '描画'; + + @override + String get homeTabKnowledge => '知識'; + + @override + String get homeTabTools => 'ツール'; + + @override + String get homeTabMine => 'マイ'; + + @override + String get dataManagement => 'データ管理'; + + @override + String get userCollection => 'マイコレクション'; + + @override + String get aboutApplications => 'アプリについて'; + + @override + String get contactThisKing => '本王に連絡'; + + @override + String get appSettings => 'アプリ設定'; + + @override + String get darkMode => 'ダークモード'; + + @override + String get themeColorSetting => 'テーマカラー設定'; + + @override + String get fontSetting => 'フォント設定'; + + @override + String get settingLanguageText => '多言語'; + + @override + String get codeHighlightStyle => 'コードハイライトスタイル'; + + @override + String get versionInformation => 'バージョン情報'; + + @override + String get displayPerformanceFloatingLayer => 'パフォーマンスフローティングレイヤーを表示'; + + @override + String get showFloatingTools => 'フローティングツールを表示'; + + @override + String get followSystem => 'システムに従う'; + + @override + String get afterOpeningWillFollowTheSystemToOpenOrCloseDarkMode => '有効にすると、システムに従ってダークモードをオンまたはオフにします'; + + @override + String get manualSetting => '手動設定'; + + @override + String get lightMode => 'ライトモード'; + + @override + String get settingLanguage => '言語設定'; + + @override + String get appDetails => 'アプリ詳細'; + + @override + String get checkUpdate => '新しいバージョンを確認'; + + @override + String get downloadNewVersion => '新しいバージョンをダウンロード'; + + @override + String get downloadingNewVersion => '新しいバージョンをダウンロード中...'; + + @override + String get currentIsNew => '現在のアプリは最新バージョンです!'; + + @override + String get checkDatabaseNewVersion => 'データベースの新しいバージョンを確認'; + + @override + String get viewThisProjectGithubRepository => '《このプロジェクトのGithubリポジトリを表示》'; + + @override + String get favorite => 'お気に入り'; + + @override + String get enterComponentName => 'コンポーネント名を入力'; + + @override + String get containerComponents => 'コンテナコンポーネント'; + + @override + String get componentTavern => 'コンポーネント酒場'; + + @override + String get cherishedComponents => '大切なコンポーネント'; + + @override + String get textImageCollection => 'テキスト画像集'; + + @override + String get layoutCollection => 'レイアウト集'; + + @override + String get eventCollection => 'イベント集'; + + @override + String get animationCollection => 'アニメーション集'; + + @override + String get slidingCollection => 'スライド集'; + + @override + String get decorationCollection => '装飾集'; + + @override + String get assemblyCollection => 'アセンブリ集'; + + @override + String get functionCollection => '機能集'; + + @override + String get popupCollection => 'ポップアップ集'; + + @override + String get themeCollection => 'テーマ集'; + + @override + String get derivativeCollection => '派生集'; + + @override + String get hardToCategorize => '分類が難しい'; + + @override + String get basicDrawing => '基本描画'; + + @override + String get basicDrawingDesc => '基本的な図形描画のケースを収録しています。これらのケースは、描画を始めたばかりのプログラマーにとって非常に役立ちます。これらのケースを通じて、点、線、矩形、円、円弧、テキスト、画像などの基本的な図形の描画方法を学び、Canvas、Paint、Pathなどの描画の核心オブジェクトの使用方法を理解できます。'; + + @override + String get animationGesture => 'アニメーションとジェスチャー'; + + @override + String get animationGestureDesc => 'アニメーションとジェスチャーの描画ケースを収録しています。これらのケースは、描画をより操作可能にします。これらのケースを通じて、スライド、回転、拡大縮小、移動などの効果を学び、描画が静的な表現だけでなくなることを理解できます。'; + + @override + String get particleDrawing => '粒子描画'; + + @override + String get particleDrawingDesc => '粒子関連の描画ケースを収録しています。これらのケースは、描画のトップレベルの操作です。これらのケースを通じて、粒子を使用して驚くべき視覚効果を描画する方法を学び、粒子時計、粒子爆発、粒子背景などの効果を実現し、描画に無限の可能性を与えます。'; + + @override + String get interestingDrawing => '面白い描画'; + + @override + String get interestingDrawingDesc => 'いくつかの面白い描画ケースを収録しています。ここで一緒に描画の楽しさ、プログラミングの楽しさ、そして知恵の楽しさを体験しましょう。'; + + @override + String get artGallery => 'アートギャラリー'; + + @override + String get artGalleryDesc => '殿堂級の描画ケースを収録しています。これらのケースは、描画の頂点作品であり、それらは実用性がなく、いかなるニーズのためでもありません。それらは存在するために存在し、人間の知恵と表現の媒体であり、芸術と呼ばれます。'; + + @override + String get drawingOfImages => 'このサンプルでは、画像を描画する方法を紹介します。画像をロードし、指定された領域に画像リソースを描画します。上層に45度傾いたグリッド線を描画し、線の描画を練習します。'; + + @override + String get digitalDisplayTube => 'このサンプルでは、LEDデジタル表示管を描画する方法を紹介し、パスPathの使用、変換、組み合わせ、およびコンポーネントのカプセル化の知識を練習します。非常に良い描画ケースです。'; + + @override + String get pathDrawing => 'このサンプルでは、簡単なパスの描画とキャンバスの回転を紹介し、アニメーションと組み合わせて風車を回転させます。これは、描画とアニメーションを組み合わせた非常に簡潔なケースです。'; + + @override + String get gridCoordinateSystem => 'このサンプルでは、線パスとテキストを使用してグリッド座標系を描画し、描画オブジェクトをカプセル化して再利用しやすくします。座標系は描画時に参考を提供し、入門に最適です。'; + + @override + String get polarCoordinateSystemOfFaces => 'このサンプルでは、平面の極座標系を使用して描画し、関数方程式に基づいて極座標を収集して描画する方法を紹介します。'; + + @override + String get drawFunctionCurvesForPathPairs => 'このサンプルでは、パスを使用して関数曲線を描画し、関数曲線上の少数の点をベジェ曲線でフィッティングする方法を紹介します。'; + + @override + String get drawRegularPolygons => 'このサンプルでは、円内で点を収集し、正多角形を描画する方法を紹介します。描画とパス形成の練習に最適なケースです。\n特殊操作:+、- で辺の数を変更'; + + @override + String get randomNumberProcessing => 'このサンプルでは、矩形の描画と乱数処理を紹介します。点の集合を使用して矩形の位置情報を決定し、それを描画します。データの制御能力を練習できます。'; + + @override + String get clockDrawing => 'このサンプルでは、時計の描画を通じて、Flutterでの回転目盛りの描画テクニックを練習し、アニメーションで時計の針を回転させます。'; + + @override + String get drawSprings => 'このサンプルでは、バネを描画し、垂直にドラッグして伸縮し、手を離すと復元アニメーションを行う方法を紹介します。非常に良い総合的な小ケースです。特殊操作:上下にドラッグしてバネを伸縮'; + + @override + String get theApplicationOfAnglesInDrawing => 'このサンプルでは、ある点を中心に回転運動を行う方法を紹介します。これにより、2点間の角度を描画に適用する方法を学びます。\n特殊操作:クリックして実行'; + + @override + String get usingShadersAndFilters => 'このサンプルでは、描画でシェーダーとフィルターを使用し、アニメーションで数値を変化させて回転する光の効果を実現する方法を紹介します。'; + + @override + String get pathDrawingFunctionCurve => 'このサンプルでは、パスを使用して関数曲線を描画し、パス測定を使用してアニメーションを行う方法を紹介します。'; + + @override + String get thePathOfBingDwenDwen => 'このサンプルでは、2022年北京冬季オリンピックのマスコットであるビンドゥンドゥンのパスを描画し、パス測定を使用してアニメーションを行います。\n特殊操作:クリックして実行'; + + @override + String get drawCubicBesselCurve => 'このサンプルでは、3次ベジェ曲線を描画し、タッチポイントを使用してある点がアクティブかどうかを判断し、それに応じて点の位置を制御してドラッグ制御効果を実現する方法を紹介します。\n特殊操作:クリックで点を描画、ダブルクリックでクリア'; + + @override + String get theEffectOfAnimationCurve => 'このサンプルでは、アニメーションカーブの効果を直感的に確認し、アニメーションに対する理解を深めます。\n特殊操作:クリックして実行'; + + @override + String get randomParticlesAndBoundaryBouncing => 'このサンプルでは、ランダムな粒子を作成し、境界でのバウンスロジックを処理する方法を紹介します。粒子運動を学ぶのに非常に良い入門ケースです。特殊操作:クリックで停止/実行'; + + @override + String get particleCollision => 'このサンプルでは、個々の粒子の衝突検出を行い、複数の粒子に分裂させる方法を紹介します。非常に面白いケースです。\n特殊操作:クリックでリセット'; + + @override + String get particle => 'このサンプルでは、画像を粒子として表現し、粒子にアニメーションを適用して爆発効果を実現する方法を紹介します。\n特殊操作:クリックして実行'; + + @override + String get rectangleAndRandomNumbers => 'このサンプルでは、矩形の描画と乱数処理を紹介します。点の集合を使用して矩形の位置情報を決定し、それを描画します。データの制御能力を練習できます。\n特殊操作:クリックしてランダム生成'; + + @override + String get bingDwenDwen => 'このサンプルでは、2022年北京冬季オリンピックのマスコットであるビンドゥンドゥンの形を描画し、パス描画やグラデーションなどの知識を学びます。'; + + @override + String get pufengInjectionTest => 'このサンプルでは、蒲豊の針投げテストのプロセスを実装し、確率を使用して円周率を推定します。描画の小技やデータの論理処理を学ぶことができます。'; + + @override + String get ticTacToe => 'このサンプルでは、三目並べの描画と論理検証を通じて、ジェスチャー、描画、アニメーション、検証などの重要なスキルを組み合わせます。非常に良い練習ケースです。\n特殊操作:ダブルクリックでリセット'; + + @override + String get tiledLines => 'このサンプルは、generativeartistry.comのtiled-linesに由来し、xrr2016がFlutterで実装しました。リポジトリアドレス:flutter-generative-artistry'; + + @override + String get joyDivision => 'このサンプルは、generativeartistry.comのjoy-divisionに由来し、xrr2016がFlutterで実装しました。リポジトリアドレス:flutter-generative-artistry'; + + @override + String get cubicDisarray => 'このサンプルは、generativeartistry.comのcubic-disarrayに由来し、xrr2016がFlutterで実装しました。リポジトリアドレス:flutter-generative-artistry'; + + @override + String get triangularMesh => 'このサンプルは、generativeartistry.comのtriangular-meshに由来し、xrr2016がFlutterで実装しました。リポジトリアドレス:flutter-generative-artistry'; + + @override + String get unDeuxTrois => 'このサンプルは、generativeartistry.comのun-deux-troisに由来し、xrr2016がFlutterで実装しました。リポジトリアドレス:flutter-generative-artistry'; + + @override + String get circlePacking => 'このサンプルは、generativeartistry.comのcircle-packingに由来し、xrr2016がFlutterで実装しました。リポジトリアドレス:flutter-generative-artistry'; + + @override + String get hypnoticSquares => 'このサンプルは、generativeartistry.comのhypnotic-squaresに由来し、xrr2016がFlutterで実装しました。リポジトリアドレス:flutter-generative-artistry'; + + @override + String get pietMondrian => 'このサンプルは、generativeartistry.comのpiet-mondrianに由来し、xrr2016がFlutterで実装しました。リポジトリアドレス:flutter-generative-artistry'; + + @override + String get downloadCompressedPackage => '使用方法:\n1. iconfont.cnでアイコンを選び、プロジェクトに追加し、圧縮パッケージをダウンロードします。\n2. Flutterプロジェクトのアドレスを選択し、リソースと生成物ファイルの場所を設定します。\n3. コード生成ボタンをクリックすると、関連するコードが生成されます。'; + + @override + String get qAIssues => '要点集録のQAデータは、rUnitのpointタグが付いたissuesに収録されています。データを提供する必要がある場合は、issuesで質問してください。'; + + @override + String get tips => '注:'; + + @override + String get visualSorting => '可視化ソート'; + + @override + String get visual => '可視ソート'; + + @override + String get insertion => '挿入ソート'; + + @override + String get bubble => 'バブルソート'; + + @override + String get cocktail => 'カクテルソート(双方向バブルソート)'; + + @override + String get comb => 'コームソート'; + + @override + String get pigeonHole => '鳩の巣ソート'; + + @override + String get shell => 'シェルソート'; + + @override + String get selection => '選択ソート'; + + @override + String get gnome => 'ノームソート'; + + @override + String get cycle => 'サイクルソート'; + + @override + String get heap => 'ヒープソート'; + + @override + String get quick => 'クイックソート'; + + @override + String get merge => 'マージソート'; + + @override + String get sortingAlgorithmConfiguration => 'ソートアルゴリズム設定'; + + @override + String get dataCount => 'データ数(個数)'; + + @override + String get timeInterval => '時間間隔(マイクロ秒)'; + + @override + String get randomSeed => 'ランダムシード'; + + @override + String get codeGeneration => 'コード生成'; + + @override + String get generateCode => 'コードを生成'; + + @override + String get artifactLocation => '生成物の場所'; + + @override + String get codeClassLocation => 'コードクラスの保存場所'; + + @override + String get resourceDirectory => 'リソースディレクトリ'; + + @override + String get iconfontResourceLocation => 'iconfontリソースの保存場所'; + + @override + String get projectPath => 'プロジェクトパス'; + + @override + String get inputProjectAddress => 'プロジェクトアドレスを選択または入力してください'; + + @override + String get iconfontCompressedPackagePath => 'Iconfont圧縮パッケージのパス'; + + @override + String get pleaseSelectOrInputIconfontCompressedPackagePath => 'iconfontのダウンロードした圧縮パッケージのパスを選択または入力してください'; + + @override + String get stayTuned => 'お楽しみに'; + + @override + String get iconFont => 'IconFont'; + + @override + String get dataClass => 'データクラス'; + + @override + String get stateManagement => '状態管理'; + + @override + String get jsonParsing => 'Json解析'; + + @override + String get clickHereToJump => 'ここをクリックしてジャンプ'; + + @override + String get knowledgeTabToly => 'ジェット文庫'; + + @override + String get knowledgeTabAlgo => 'アルゴリズム演繹'; + + @override + String get knowledgeTabLayout => 'レイアウト宝庫'; + + @override + String get knowledgeTabPoint => '要点宝庫'; + + @override + String get knowledgeConstruction => '建設中'; + + @override + String get knowledgeToJuejin => '掘金へ'; + + @override + String get srcPath => 'ソースアドレス'; + + @override + String get widgetsInn => 'ウィジェット酒場'; + + @override + String get likedWidgets => '大切なウィジェット'; + + @override + String get relatedComponents => '関連コンポーネント'; + + @override + String get backupFavoritesCollectionData => 'お気に入りコレクションデータのバックアップ'; + + @override + String get syncFavoritesCollectionData => 'お気に入りコレクションデータの同期'; + + @override + String get favoritesCollectionDataReset => 'お気に入りコレクションデータのリセット'; + + @override + String get resetSuccess => 'リセット成功!'; + + @override + String get dataSetBackupSuccess => 'データセットのバックアップ成功!'; + + @override + String get dataSetBackupFailure => 'データセットのバックアップ失敗!'; + + @override + String get dataSynchronizationCopySuccess => 'データ同期コピー成功!'; + + @override + String get dataSynchronizationCopyFailure => 'データ同期コピー失敗!'; + + @override + String get destructionRed => '破滅の赤'; + + @override + String get rageOrange => '怒りのオレンジ'; + + @override + String get warningYellow => '警告の黄'; + + @override + String get camouflageGreen => '偽装の緑'; + + @override + String get coldBlue => '冷徹な青'; + + @override + String get infiniteBlue => '無限の藍'; + + @override + String get mysteryPurple => '神秘の紫'; + + @override + String get destinyBlack => '帰宿の黒'; + + @override + String get showBackground => '背景を表示'; + + @override + String get toly => '張風捷特烈'; + + @override + String get dartHandbook => 'Dart 手引き'; + + @override + String get codeCopiedSuccessfully => 'コードがコピーされました'; + + @override + String get favoriteFolderManagement => 'お気に入りフォルダの管理'; + + @override + String get assembly => 'コンポーネント'; + + @override + String get draw => '描画'; + + @override + String get knowledge => '知識'; + + @override + String get collection => 'コレクション'; + + @override + String get my => '私の'; + + @override + String get picture => '枚'; + + @override + String get widgetInn => 'コンポーネント酒場'; + + @override + String get emptySearch => 'データがありません、俺もどうしようもない\n(≡ _ ≡)/~┴┴'; + + @override + String get searchSomething => '友よ、何か検索しよう…≧◔◡◔≦'; + + @override + String get slogan => 'Flutterの連携、プログラマーの連携'; +} diff --git a/modules/basic_system/l10n/lib/gen_l10n/app_localizations_ko.dart b/modules/basic_system/l10n/lib/gen_l10n/app_localizations_ko.dart new file mode 100644 index 000000000..5cd7f7925 --- /dev/null +++ b/modules/basic_system/l10n/lib/gen_l10n/app_localizations_ko.dart @@ -0,0 +1,560 @@ +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Korean (`ko`). +class AppLocalizationsKo extends AppLocalizations { + AppLocalizationsKo([String locale = 'ko']) : super(locale); + + @override + String get widgetCollection => '위젯 컬렉션'; + + @override + String get paintCollection => '그림 컬렉션'; + + @override + String get knowledgeCollection => '지식 모음'; + + @override + String get treasureTools => '도구 보물상자'; + + @override + String get collectCollection => '컬렉션 모음'; + + @override + String get essentialCollection => '핵심 모음'; + + @override + String get homeAccount => '앱 정보'; + + @override + String get homeAccountTabInfo => '앱 정보'; + + @override + String get homeAccountTabMe => '저에게 연락'; + + @override + String get homeAccountSupport => '프로젝트 지원'; + + @override + String get searchWidget => '위젯 검색'; + + @override + String get stateless => '무상태'; + + @override + String get stateful => '상태 있음'; + + @override + String get single => '단일 렌더링'; + + @override + String get multi => '다중 렌더링'; + + @override + String get sliver => '슬라이버'; + + @override + String get proxy => '프록시'; + + @override + String get other => '기타'; + + @override + String get homeTabWidget => '위젯'; + + @override + String get homeTabPaint => '그리기'; + + @override + String get homeTabKnowledge => '지식'; + + @override + String get homeTabTools => '도구'; + + @override + String get homeTabMine => '내 정보'; + + @override + String get dataManagement => '데이터 관리'; + + @override + String get userCollection => '내 컬렉션'; + + @override + String get aboutApplications => '앱 정보'; + + @override + String get contactThisKing => '저에게 연락'; + + @override + String get appSettings => '앱 설정'; + + @override + String get darkMode => '다크 모드'; + + @override + String get themeColorSetting => '테마 색상 설정'; + + @override + String get fontSetting => '폰트 설정'; + + @override + String get settingLanguageText => '다국어'; + + @override + String get codeHighlightStyle => '코드 하이라이트 스타일'; + + @override + String get versionInformation => '버전 정보'; + + @override + String get displayPerformanceFloatingLayer => '성능 플로팅 레이어 표시'; + + @override + String get showFloatingTools => '플로팅 도구 표시'; + + @override + String get followSystem => '시스템 따라가기'; + + @override + String get afterOpeningWillFollowTheSystemToOpenOrCloseDarkMode => '시스템 설정에 따라 다크 모드를 켜거나 끕니다.'; + + @override + String get manualSetting => '수동 설정'; + + @override + String get lightMode => '라이트 모드'; + + @override + String get settingLanguage => '언어 설정'; + + @override + String get appDetails => '앱 상세 정보'; + + @override + String get checkUpdate => '새 버전 확인'; + + @override + String get downloadNewVersion => '새 버전 다운로드'; + + @override + String get downloadingNewVersion => '새 버전 다운로드 중...'; + + @override + String get currentIsNew => '현재 버전이 최신입니다!'; + + @override + String get checkDatabaseNewVersion => '데이터베이스 새 버전 확인'; + + @override + String get viewThisProjectGithubRepository => '《이 프로젝트의 Github 저장소 보기》'; + + @override + String get favorite => '즐겨찾기'; + + @override + String get enterComponentName => '컴포넌트 이름 입력'; + + @override + String get containerComponents => '컨테이너 컴포넌트'; + + @override + String get componentTavern => '컴포넌트 선술집'; + + @override + String get cherishedComponents => '소중한 컴포넌트'; + + @override + String get textImageCollection => '텍스트 이미지 모음'; + + @override + String get layoutCollection => '레이아웃 모음'; + + @override + String get eventCollection => '이벤트 모음'; + + @override + String get animationCollection => '애니메이션 모음'; + + @override + String get slidingCollection => '슬라이딩 모음'; + + @override + String get decorationCollection => '장식 모음'; + + @override + String get assemblyCollection => '조립 모음'; + + @override + String get functionCollection => '기능 모음'; + + @override + String get popupCollection => '팝업 모음'; + + @override + String get themeCollection => '테마 모음'; + + @override + String get derivativeCollection => '파생 모음'; + + @override + String get hardToCategorize => '분류하기 어려움'; + + @override + String get basicDrawing => '기본 그리기'; + + @override + String get basicDrawingDesc => '기본 도형 그리기 예제를 포함합니다. 이 예제들은 그리기를 처음 접하는 프로그래머에게 매우 친숙할 것입니다. 이 예제들을 통해 점, 선, 사각형, 원, 호, 텍스트, 이미지 등 기본 도형을 그리는 방법을 배우고, Canvas, Paint, Path 등 그리기의 핵심 객체 사용법을 익힐 수 있습니다.'; + + @override + String get animationGesture => '애니메이션 제스처'; + + @override + String get animationGestureDesc => '애니메이션과 제스처 그리기 예제를 포함합니다. 이 예제들은 그리기를 더욱 상호작용적으로 만듭니다. 이 예제들을 통해 슬라이드, 회전, 확대/축소, 이동 등의 효과를 배울 수 있으며, 그리기가 단순히 정적인 표현이 아니게 됩니다.'; + + @override + String get particleDrawing => '입자 그리기'; + + @override + String get particleDrawingDesc => '입자 관련 그리기 예제를 포함합니다. 이 예제들은 그리기의 최고 수준의 작업입니다. 이 예제들을 통해 입자를 사용하여 놀라운 시각적 효과를 그리는 방법을 배울 수 있습니다. 입자 시계, 입자 폭발, 입자 배경 등 다양한 효과를 통해 그리기의 무한한 가능성을 경험할 수 있습니다.'; + + @override + String get interestingDrawing => '재미있는 그리기'; + + @override + String get interestingDrawingDesc => '재미있는 그리기 예제를 포함합니다. 여기서 그리기, 프로그래밍, 지혜의 즐거움을 함께 경험해 보세요.'; + + @override + String get artGallery => '예술 갤러리'; + + @override + String get artGalleryDesc => '최고 수준의 그리기 예제를 포함합니다. 이 예제들은 그리기의 정점에 있는 작품들로, 실용성은 없지만 존재 자체로 의미가 있는 예술 작품입니다.'; + + @override + String get drawingOfImages => '이 예제는 이미지를 그리는 방법을 소개합니다: 이미지를 로드하고 지정된 영역에 그립니다. 상단에 45도 각도의 그리드 선을 그려 선 그리기를 연습합니다.'; + + @override + String get digitalDisplayTube => '이 예제는 LED 디지털 디스플레이 튜브를 그리는 방법을 소개하며, Path 사용, 변환, 조합 및 컴포넌트 캡슐화를 연습합니다. 매우 좋은 그리기 예제입니다.'; + + @override + String get pathDrawing => '이 예제는 간단한 경로 그리기 및 캔버스 회전을 소개하며, 애니메이션을 결합하여 풍차를 회전시킵니다. 매우 간결한 그리기와 애니메이션 결합 예제입니다.'; + + @override + String get gridCoordinateSystem => '이 예제는 선 경로와 텍스트를 사용하여 그리드 좌표계를 그리는 방법을 소개하며, 그리기 객체를 캡슐화하여 재사용하기 쉽게 만듭니다. 좌표계는 그리기 시 참조를 제공하며, 입문자에게 필수입니다.'; + + @override + String get polarCoordinateSystemOfFaces => '이 예제는 평면의 극좌표계를 그리는 방법을 소개하며, 함수 방정식에 따라 극좌표를 수집하여 그립니다.'; + + @override + String get drawFunctionCurvesForPathPairs => '이 예제는 경로를 사용하여 함수 곡선을 그리는 방법을 소개하며, 함수 곡선 상의 소량의 점을 베지어 곡선으로 피팅합니다.'; + + @override + String get drawRegularPolygons => '이 예제는 원 안에서 점을 수집하여 정다각형을 그리는 방법을 소개하며, 그리기 및 경로 형성 연습에 매우 좋은 예제입니다.\n특수 조작: +, - 로 변 수정'; + + @override + String get randomNumberProcessing => '이 예제는 사각형 그리기 및 난수 처리를 소개합니다. 점 집합을 통해 사각형 위치 정보를 결정하고 그립니다. 데이터 제어 능력을 연습할 수 있습니다.'; + + @override + String get clockDrawing => '이 예제는 시계 그리기를 통해 Flutter에서 회전 눈금 그리기 기술을 연습하고, 애니메이션을 통해 시계 바늘이 회전하도록 합니다.'; + + @override + String get drawSprings => '이 예제는 스프링을 그리는 방법을 소개하며, 수직 드래그로 스프링을 늘이거나 줄이고, 놓을 때 복원 애니메이션을 수행합니다. 매우 좋은 종합 예제입니다. 특수 조작: 위아래 드래그로 스프링 늘이기/줄이기'; + + @override + String get theApplicationOfAnglesInDrawing => '이 예제는 특정 점을 중심으로 회전 운동을 수행하는 방법을 소개합니다. 이를 통해 두 점 사이의 각도가 그리기에서 어떻게 적용되는지 배울 수 있습니다.\n특수 조작: 클릭하여 실행'; + + @override + String get usingShadersAndFilters => '이 예제는 그리기에서 셰이더와 필터를 사용하는 방법을 소개하며, 애니메이션을 통해 수치를 변경하여 회전하는 빛의 효과를 만듭니다.'; + + @override + String get pathDrawingFunctionCurve => '이 예제는 경로를 사용하여 함수 곡선을 그리는 방법을 소개하며, 경로 측정을 사용하여 애니메이션을 만듭니다.'; + + @override + String get thePathOfBingDwenDwen => '이 예제는 2022년 베이징 동계 올림픽 마스코트 빙둔둔의 경로를 그리며, 경로 측정을 사용하여 애니메이션을 만듭니다.\n특수 조작: 클릭하여 실행'; + + @override + String get drawCubicBesselCurve => '이 예제는 3차 베지어 곡선을 그리는 방법을 소개하며, 접점을 통해 특정 점이 활성화되었는지 판단하여 점의 위치를 제어합니다.\n특수 조작: 클릭하여 점 그리기, 더블 클릭하여 지우기'; + + @override + String get theEffectOfAnimationCurve => '이 예제는 애니메이션 곡선의 효과를 직관적으로 보여줍니다. 이를 통해 애니메이션에 대한 이해를 높일 수 있습니다.\n특수 조작: 클릭하여 실행'; + + @override + String get randomParticlesAndBoundaryBouncing => '이 예제는 무작위 입자를 생성하고 경계에서 튕기는 로직을 처리하는 방법을 소개합니다. 입자 운동을 배우기에 매우 좋은 입문 예제입니다. 특수 조작: 클릭하여 정지/실행'; + + @override + String get particleCollision => '이 예제는 개별 입자에 대한 충돌 감지를 수행하고, 여러 입자로 분열시키는 방법을 소개합니다. 매우 재미있는 예제입니다.\n특수 조작: 클릭하여 재설정'; + + @override + String get particle => '이 예제는 이미지를 입자로 표현하고, 입자에 애니메이션을 적용하여 폭발 효과를 만드는 방법을 소개합니다.\n특수 조작: 클릭하여 실행'; + + @override + String get rectangleAndRandomNumbers => '이 예제는 사각형 그리기 및 난수 처리를 소개합니다. 점 집합을 통해 사각형 위치 정보를 결정하고 그립니다. 데이터 제어 능력을 연습할 수 있습니다.\n특수 조작: 클릭하여 무작위 생성'; + + @override + String get bingDwenDwen => '이 예제는 2022년 베이징 동계 올림픽 마스코트 빙둔둔의 형태를 그리는 방법을 소개하며, 경로 그리기 및 그라데이션 색상 사용법을 배울 수 있습니다.'; + + @override + String get pufengInjectionTest => '이 예제는 푸펑 투시험의 테스트 과정을 구현하며, 확률을 통해 원주율을 추정합니다. 그리기 기술 및 데이터 논리 처리 방법을 배울 수 있습니다.'; + + @override + String get ticTacToe => '이 예제는 틱택토 그리기 및 논리 검증을 통해 제스처, 그리기, 애니메이션, 검증 등의 중요한 기술을 종합적으로 연습할 수 있는 매우 좋은 예제입니다.\n특수 조작: 더블 클릭하여 재설정'; + + @override + String get tiledLines => '이 예제는 generativeartistry.com의 tiled-lines에서 유래되었으며, xrr2016이 Flutter로 구현했습니다. 저장소 주소: flutter-generative-artistry'; + + @override + String get joyDivision => '이 예제는 generativeartistry.com의 joy-division에서 유래되었으며, xrr2016이 Flutter로 구현했습니다. 저장소 주소: flutter-generative-artistry'; + + @override + String get cubicDisarray => '이 예제는 generativeartistry.com의 cubic-disarray에서 유래되었으며, xrr2016이 Flutter로 구현했습니다. 저장소 주소: flutter-generative-artistry'; + + @override + String get triangularMesh => '이 예제는 generativeartistry.com의 triangular-mesh에서 유래되었으며, xrr2016이 Flutter로 구현했습니다. 저장소 주소: flutter-generative-artistry'; + + @override + String get unDeuxTrois => '이 예제는 generativeartistry.com의 un-deux-trois에서 유래되었으며, xrr2016이 Flutter로 구현했습니다. 저장소 주소: flutter-generative-artistry'; + + @override + String get circlePacking => '이 예제는 generativeartistry.com의 circle-packing에서 유래되었으며, xrr2016이 Flutter로 구현했습니다. 저장소 주소: flutter-generative-artistry'; + + @override + String get hypnoticSquares => '이 예제는 generativeartistry.com의 hypnotic-squares에서 유래되었으며, xrr2016이 Flutter로 구현했습니다. 저장소 주소: flutter-generative-artistry'; + + @override + String get pietMondrian => '이 예제는 generativeartistry.com의 piet-mondrian에서 유래되었으며, xrr2016이 Flutter로 구현했습니다. 저장소 주소: flutter-generative-artistry'; + + @override + String get downloadCompressedPackage => '사용 방법:\n1. iconfont.cn에서 아이콘을 선택하고 프로젝트에 추가한 후 압축 파일을 다운로드합니다.\n2. Flutter 프로젝트 경로를 선택하고 리소스 및 산출물 파일 위치를 설정합니다.\n3. 코드 생성 버튼을 클릭하여 관련 코드를 생성합니다.'; + + @override + String get qAIssues => '핵심 모음의 QA 데이터는 rUnit의 point 태그가 있는 issues에 포함되어 있습니다. 데이터를 제공하려면 issues에서 질문하세요.'; + + @override + String get tips => '참고:'; + + @override + String get visualSorting => '시각적 정렬'; + + @override + String get visual => '시각적 정렬'; + + @override + String get insertion => '삽입 정렬'; + + @override + String get bubble => '버블 정렬'; + + @override + String get cocktail => '칵테일 정렬(양방향 버블 정렬)'; + + @override + String get comb => '빗질 정렬'; + + @override + String get pigeonHole => '비둘기집 정렬'; + + @override + String get shell => '셸 정렬'; + + @override + String get selection => '선택 정렬'; + + @override + String get gnome => '노움 정렬'; + + @override + String get cycle => '순환 정렬'; + + @override + String get heap => '힙 정렬'; + + @override + String get quick => '퀵 정렬'; + + @override + String get merge => '병합 정렬'; + + @override + String get sortingAlgorithmConfiguration => '정렬 알고리즘 설정'; + + @override + String get dataCount => '데이터 수(개수)'; + + @override + String get timeInterval => '시간 간격(마이크로초)'; + + @override + String get randomSeed => '무작위 시드'; + + @override + String get codeGeneration => '코드 생성'; + + @override + String get generateCode => '코드 생성'; + + @override + String get artifactLocation => '산출물 위치'; + + @override + String get codeClassLocation => '코드 클래스 위치'; + + @override + String get resourceDirectory => '리소스 디렉토리'; + + @override + String get iconfontResourceLocation => 'iconfont 리소스 위치'; + + @override + String get projectPath => '프로젝트 경로'; + + @override + String get inputProjectAddress => '프로젝트 주소를 선택하거나 입력하세요'; + + @override + String get iconfontCompressedPackagePath => 'Iconfont 압축 파일 경로'; + + @override + String get pleaseSelectOrInputIconfontCompressedPackagePath => 'iconfont 다운로드 압축 파일 경로를 선택하거나 입력하세요'; + + @override + String get stayTuned => '기대해 주세요'; + + @override + String get iconFont => 'IconFont'; + + @override + String get dataClass => '데이터 클래스'; + + @override + String get stateManagement => '상태 관리'; + + @override + String get jsonParsing => 'Json 파싱'; + + @override + String get clickHereToJump => '여기를 클릭하여 이동'; + + @override + String get knowledgeTabToly => '제트 문고'; + + @override + String get knowledgeTabAlgo => '알고리즘 연습'; + + @override + String get knowledgeTabLayout => '레이아웃 보물'; + + @override + String get knowledgeTabPoint => '핵심 보물'; + + @override + String get knowledgeConstruction => '구축 중'; + + @override + String get knowledgeToJuejin => 'Juejin으로 이동'; + + @override + String get srcPath => '소스 경로'; + + @override + String get widgetsInn => '위젯 선술집'; + + @override + String get likedWidgets => '소중한 위젯'; + + @override + String get relatedComponents => '관련 컴포넌트'; + + @override + String get backupFavoritesCollectionData => '즐겨찾기 컬렉션 데이터 백업'; + + @override + String get syncFavoritesCollectionData => '즐겨찾기 컬렉션 데이터 동기화'; + + @override + String get favoritesCollectionDataReset => '즐겨찾기 컬렉션 데이터 재설정'; + + @override + String get resetSuccess => '재설정 성공!'; + + @override + String get dataSetBackupSuccess => '데이터 세트 백업 성공!'; + + @override + String get dataSetBackupFailure => '데이터 세트 백업 실패!'; + + @override + String get dataSynchronizationCopySuccess => '데이터 동기화 복사 성공!'; + + @override + String get dataSynchronizationCopyFailure => '데이터 동기화 복사 실패!'; + + @override + String get destructionRed => '파괴의 빨강'; + + @override + String get rageOrange => '분노의 주황'; + + @override + String get warningYellow => '경고의 노랑'; + + @override + String get camouflageGreen => '위장의 초록'; + + @override + String get coldBlue => '냉담의 파랑'; + + @override + String get infiniteBlue => '무한의 남색'; + + @override + String get mysteryPurple => '신비의 보라'; + + @override + String get destinyBlack => '운명의 검정'; + + @override + String get showBackground => '배경 표시'; + + @override + String get toly => '장풍제특렬'; + + @override + String get dartHandbook => 'Dart 핸드북'; + + @override + String get codeCopiedSuccessfully => '코드 복사 성공'; + + @override + String get favoriteFolderManagement => '즐겨찾기 폴더 관리'; + + @override + String get assembly => '컴포넌트'; + + @override + String get draw => '그리기'; + + @override + String get knowledge => '지식'; + + @override + String get collection => '컬렉션'; + + @override + String get my => '내 정보'; + + @override + String get picture => '그림'; + + @override + String get widgetInn => '위젯 선술집'; + + @override + String get emptySearch => '데이터가 없습니다, 형님도 방법이 없어요\n(≡ _ ≡)/~┴┴'; + + @override + String get searchSomething => '형님, 뭘 검색하시겠어요...≧◔◡◔≦'; + + @override + String get slogan => 'Flutter의 연합, 프로그래머의 연합'; +} diff --git a/modules/basic_system/l10n/lib/gen_l10n/app_localizations_ru.dart b/modules/basic_system/l10n/lib/gen_l10n/app_localizations_ru.dart new file mode 100644 index 000000000..ed59e2ec3 --- /dev/null +++ b/modules/basic_system/l10n/lib/gen_l10n/app_localizations_ru.dart @@ -0,0 +1,560 @@ +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Russian (`ru`). +class AppLocalizationsRu extends AppLocalizations { + AppLocalizationsRu([String locale = 'ru']) : super(locale); + + @override + String get widgetCollection => 'Сборник компонентов'; + + @override + String get paintCollection => 'Сборник рисования'; + + @override + String get knowledgeCollection => 'Сборник знаний'; + + @override + String get treasureTools => 'Сундучок инструментов'; + + @override + String get collectCollection => 'Сборник избранного'; + + @override + String get essentialCollection => 'Сборник ключевых моментов'; + + @override + String get homeAccount => 'Информация о приложении'; + + @override + String get homeAccountTabInfo => 'О приложении'; + + @override + String get homeAccountTabMe => 'Связаться с этим королём'; + + @override + String get homeAccountSupport => 'Проекты поддержки'; + + @override + String get searchWidget => 'Поиск компонентов'; + + @override + String get stateless => 'Без состояния'; + + @override + String get stateful => 'С состоянием'; + + @override + String get single => 'Один рендеринг'; + + @override + String get multi => 'Многократный рендеринг'; + + @override + String get sliver => 'Скользящая панель'; + + @override + String get proxy => 'Прокси'; + + @override + String get other => 'Другие'; + + @override + String get homeTabWidget => 'Компоненты'; + + @override + String get homeTabPaint => 'Рисование'; + + @override + String get homeTabKnowledge => 'Знания'; + + @override + String get homeTabTools => 'Инструменты'; + + @override + String get homeTabMine => 'Мой профиль'; + + @override + String get dataManagement => 'Управление данными'; + + @override + String get userCollection => 'Мои избранные'; + + @override + String get aboutApplications => 'О приложении'; + + @override + String get contactThisKing => 'Связаться с этим королём'; + + @override + String get appSettings => 'Настройки приложения'; + + @override + String get darkMode => 'Тёмный режим'; + + @override + String get themeColorSetting => 'Настройки темы'; + + @override + String get fontSetting => 'Настройки шрифта'; + + @override + String get settingLanguageText => 'Многоязычность'; + + @override + String get codeHighlightStyle => 'Стиль подсветки кода'; + + @override + String get versionInformation => 'Информация о версии'; + + @override + String get displayPerformanceFloatingLayer => 'Показать плавающий слой производительности'; + + @override + String get showFloatingTools => 'Показать плавающие инструменты'; + + @override + String get followSystem => 'Следовать за системой'; + + @override + String get afterOpeningWillFollowTheSystemToOpenOrCloseDarkMode => 'После включения будет следовать за системой для включения или отключения тёмного режима'; + + @override + String get manualSetting => 'Ручные настройки'; + + @override + String get lightMode => 'Светлый режим'; + + @override + String get settingLanguage => 'Настройки языка'; + + @override + String get appDetails => 'Детали приложения'; + + @override + String get checkUpdate => 'Проверить обновление'; + + @override + String get downloadNewVersion => 'Скачать новое обновление'; + + @override + String get downloadingNewVersion => 'Скачивание нового обновления...'; + + @override + String get currentIsNew => 'Текущая версия приложения уже самая новая!'; + + @override + String get checkDatabaseNewVersion => 'Проверить обновление базы данных'; + + @override + String get viewThisProjectGithubRepository => 'Посмотреть репозиторий проекта на Github'; + + @override + String get favorite => 'Добавлено в избранное'; + + @override + String get enterComponentName => 'Введите название компонента'; + + @override + String get containerComponents => 'Контейнерные компоненты'; + + @override + String get componentTavern => 'Таверна компонентов'; + + @override + String get cherishedComponents => 'Ценные компоненты'; + + @override + String get textImageCollection => 'Сборник изображений и текста'; + + @override + String get layoutCollection => 'Сборник макетов'; + + @override + String get eventCollection => 'Сборник событий'; + + @override + String get animationCollection => 'Сборник анимаций'; + + @override + String get slidingCollection => 'Сборник слайдов'; + + @override + String get decorationCollection => 'Сборник декораций'; + + @override + String get assemblyCollection => 'Сборник сборки'; + + @override + String get functionCollection => 'Сборник функций'; + + @override + String get popupCollection => 'Сборник всплывающих окон'; + + @override + String get themeCollection => 'Сборник тем'; + + @override + String get derivativeCollection => 'Сборник производных'; + + @override + String get hardToCategorize => 'Трудно классифицировать'; + + @override + String get basicDrawing => 'Основы рисования'; + + @override + String get basicDrawingDesc => 'Включает несколько примеров базового рисования, которые будут полезны для начинающих программистов. Эти примеры научат рисованию базовых фигур, таких как точки, линии, прямоугольники, круги и текст.'; + + @override + String get animationGesture => 'Анимация жестов'; + + @override + String get animationGestureDesc => 'Содержит примеры рисования анимаций и жестов, которые сделают рисование более интерактивным. Эти примеры научат использовать анимации и жесты, такие как скольжение, вращение, масштабирование и перемещение.'; + + @override + String get particleDrawing => 'Частицы в рисовании'; + + @override + String get particleDrawingDesc => 'Включает примеры рисования с использованием частиц. Эти примеры покажут, как использовать частицы для создания зрелищных эффектов, таких как взрывы или анимации с частицами.'; + + @override + String get interestingDrawing => 'Интересные рисунки'; + + @override + String get interestingDrawingDesc => 'Примеры интересных и забавных рисунков, где мы можем наслаждаться процессом рисования и программирования.'; + + @override + String get artGallery => 'Галерея искусства'; + + @override + String get artGalleryDesc => 'Содержит примеры высокого искусства, которые не предназначены для практических целей, а являются выражением человеческого интеллекта и творчества.'; + + @override + String get drawingOfImages => 'Пример рисования изображений: загрузка изображения и его отрисовка в определенной области. На верхнем слое добавлены линии сетки для практики рисования линий.'; + + @override + String get digitalDisplayTube => 'Пример рисования цифровых трубок с использованием пути Path и анимации.'; + + @override + String get pathDrawing => 'Пример рисования простых путей с анимацией.'; + + @override + String get gridCoordinateSystem => 'Пример рисования сетки координат с линиями и текстом для новичков.'; + + @override + String get polarCoordinateSystemOfFaces => 'Пример рисования полярной координатной системы с использованием функций.'; + + @override + String get drawFunctionCurvesForPathPairs => 'Пример рисования функциональных кривых с использованием пути и Беезье.'; + + @override + String get drawRegularPolygons => 'Пример рисования правильных многоугольников внутри круга.'; + + @override + String get randomNumberProcessing => 'Пример рисования прямоугольников с обработкой случайных чисел.'; + + @override + String get clockDrawing => 'Пример рисования часов с анимацией.'; + + @override + String get drawSprings => 'Пример рисования пружины с анимацией сжатия и растяжения.'; + + @override + String get theApplicationOfAnglesInDrawing => 'Пример рисования с использованием углов для вращения.'; + + @override + String get usingShadersAndFilters => 'Пример использования шейдеров и фильтров для создания анимации.'; + + @override + String get pathDrawingFunctionCurve => 'Пример рисования кривых с помощью пути и измерения анимации.'; + + @override + String get thePathOfBingDwenDwen => 'Пример рисования пути маскота Олимпиады Пекин-2022 с использованием анимации пути.'; + + @override + String get drawCubicBesselCurve => 'Пример рисования кривой кубического Безье с возможностью перетаскивания точек.'; + + @override + String get theEffectOfAnimationCurve => 'Пример демонстрации эффекта анимационной кривой для углубленного понимания анимаций.'; + + @override + String get randomParticlesAndBoundaryBouncing => 'Пример создания случайных частиц с отскоком от границ.'; + + @override + String get particleCollision => 'Пример столкновений частиц с анимацией их распада.'; + + @override + String get particle => 'Пример взрыва изображений с использованием частиц.'; + + @override + String get rectangleAndRandomNumbers => 'Пример рисования прямоугольников с использованием случайных чисел.'; + + @override + String get bingDwenDwen => 'Пример рисования формы маскота Олимпиады Пекин-2022.'; + + @override + String get pufengInjectionTest => 'Пример теста для вычисления числа Пи с использованием вероятности.'; + + @override + String get ticTacToe => 'Пример игры в крестики-нолики с логикой и анимацией.'; + + @override + String get tiledLines => 'Пример, основанный на генеративном искусстве, реализованный с помощью Flutter.'; + + @override + String get joyDivision => 'Пример, основанный на генеративном искусстве, реализованный с помощью Flutter.'; + + @override + String get cubicDisarray => 'Пример, основанный на генеративном искусстве, реализованный с помощью Flutter.'; + + @override + String get triangularMesh => 'Пример, основанный на генеративном искусстве, реализованный с помощью Flutter.'; + + @override + String get unDeuxTrois => 'Пример, основанный на генеративном искусстве, реализованный с помощью Flutter.'; + + @override + String get circlePacking => 'Пример, основанный на генеративном искусстве, реализованный с помощью Flutter.'; + + @override + String get hypnoticSquares => 'Пример, основанный на генеративном искусстве, реализованный с помощью Flutter.'; + + @override + String get pietMondrian => 'Пример, основанный на генеративном искусстве, реализованный с помощью Flutter.'; + + @override + String get downloadCompressedPackage => 'Инструкция по использованию:\n1. Выберите и скачайте иконки на iconfont.cn.\n2. Настройте путь к проекту Flutter.\n3. Нажмите кнопку для генерации кода.'; + + @override + String get qAIssues => 'Данные QA из сборника важных моментов rUnit будут добавлены в issues с меткой point.'; + + @override + String get tips => 'Совет:'; + + @override + String get visualSorting => 'Визуальная сортировка'; + + @override + String get visual => 'Визуальная сортировка'; + + @override + String get insertion => 'Сортировка вставками'; + + @override + String get bubble => 'Пузырьковая сортировка'; + + @override + String get cocktail => 'Коктейльная сортировка'; + + @override + String get comb => 'Сортировка расческой'; + + @override + String get pigeonHole => 'Сортировка по принципу дырок для голубей'; + + @override + String get shell => 'Сортировка Шелла'; + + @override + String get selection => 'Сортировка выбором'; + + @override + String get gnome => 'Сортировка гномом'; + + @override + String get cycle => 'Циклическая сортировка'; + + @override + String get heap => 'Сортировка кучей'; + + @override + String get quick => 'Быстрая сортировка'; + + @override + String get merge => 'Сортировка слиянием'; + + @override + String get sortingAlgorithmConfiguration => 'Настройки сортировки'; + + @override + String get dataCount => 'Количество данных'; + + @override + String get timeInterval => 'Интервал времени'; + + @override + String get randomSeed => 'Случайное зерно'; + + @override + String get codeGeneration => 'Генерация кода'; + + @override + String get generateCode => 'Сгенерировать код'; + + @override + String get artifactLocation => 'Местоположение артефакта'; + + @override + String get codeClassLocation => 'Местоположение класса кода'; + + @override + String get resourceDirectory => 'Каталог ресурсов'; + + @override + String get iconfontResourceLocation => 'Местоположение ресурсов iconfont'; + + @override + String get projectPath => 'Путь проекта'; + + @override + String get inputProjectAddress => 'Выберите или введите путь к проекту'; + + @override + String get iconfontCompressedPackagePath => 'Путь к сжатию иконок'; + + @override + String get pleaseSelectOrInputIconfontCompressedPackagePath => 'Пожалуйста, выберите или введите путь к сжатому пакету iconfont'; + + @override + String get stayTuned => 'Ожидайте'; + + @override + String get iconFont => 'IconFont'; + + @override + String get dataClass => 'Класс данных'; + + @override + String get stateManagement => 'Управление состоянием'; + + @override + String get jsonParsing => 'Парсинг JSON'; + + @override + String get clickHereToJump => 'Нажмите здесь для перехода'; + + @override + String get knowledgeTabToly => 'Библиотека Jet'; + + @override + String get knowledgeTabAlgo => 'Алгоритмы'; + + @override + String get knowledgeTabLayout => 'Библиотека макетов'; + + @override + String get knowledgeTabPoint => 'Библиотека ключевых моментов'; + + @override + String get knowledgeConstruction => 'На стадии строительства'; + + @override + String get knowledgeToJuejin => 'Перейти на Juejin'; + + @override + String get srcPath => 'Путь к исходному коду'; + + @override + String get widgetsInn => 'Таверна компонентов'; + + @override + String get likedWidgets => 'Избранные компоненты'; + + @override + String get relatedComponents => 'Связанные компоненты'; + + @override + String get backupFavoritesCollectionData => 'Резервное копирование данных избранного'; + + @override + String get syncFavoritesCollectionData => 'Синхронизация данных избранного'; + + @override + String get favoritesCollectionDataReset => 'Сброс данных избранного'; + + @override + String get resetSuccess => 'Сброс успешен!'; + + @override + String get dataSetBackupSuccess => 'Резервное копирование данных прошло успешно!'; + + @override + String get dataSetBackupFailure => 'Ошибка резервного копирования данных!'; + + @override + String get dataSynchronizationCopySuccess => 'Синхронизация данных успешна!'; + + @override + String get dataSynchronizationCopyFailure => 'Ошибка синхронизации данных!'; + + @override + String get destructionRed => 'Красный разрушения'; + + @override + String get rageOrange => 'Оранжевый гнева'; + + @override + String get warningYellow => 'Желтый предупреждения'; + + @override + String get camouflageGreen => 'Зеленый камуфляжа'; + + @override + String get coldBlue => 'Холодный синий'; + + @override + String get infiniteBlue => 'Бесконечный синий'; + + @override + String get mysteryPurple => 'Тайный фиолетовый'; + + @override + String get destinyBlack => 'Черный судьбы'; + + @override + String get showBackground => 'Показать фон'; + + @override + String get toly => 'Толь'; + + @override + String get dartHandbook => 'Руководство Dart'; + + @override + String get codeCopiedSuccessfully => 'Код успешно скопирован'; + + @override + String get favoriteFolderManagement => 'Управление папками избранного'; + + @override + String get assembly => 'Сборка'; + + @override + String get draw => 'Рисование'; + + @override + String get knowledge => 'Знания'; + + @override + String get collection => 'Коллекция'; + + @override + String get my => 'Мои'; + + @override + String get picture => 'Картинка'; + + @override + String get widgetInn => 'Таверна компонентов'; + + @override + String get emptySearch => 'Данных нет, я тоже не знаю, что делать\n(≡ _ ≡)/~┴┴'; + + @override + String get searchSomething => 'Друзья, давайте что-то искать…≧◔◡◔≦'; + + @override + String get slogan => 'Связь Flutter, связь программистов'; +} diff --git a/modules/basic_system/l10n/lib/gen_l10n/app_localizations_zh.dart b/modules/basic_system/l10n/lib/gen_l10n/app_localizations_zh.dart new file mode 100644 index 000000000..eb94d85f0 --- /dev/null +++ b/modules/basic_system/l10n/lib/gen_l10n/app_localizations_zh.dart @@ -0,0 +1,560 @@ +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Chinese (`zh`). +class AppLocalizationsZh extends AppLocalizations { + AppLocalizationsZh([String locale = 'zh']) : super(locale); + + @override + String get widgetCollection => '组件集录'; + + @override + String get paintCollection => '绘制集录'; + + @override + String get knowledgeCollection => '知识集锦'; + + @override + String get treasureTools => '工具宝箱'; + + @override + String get collectCollection => '收藏集录'; + + @override + String get essentialCollection => '要点集录'; + + @override + String get homeAccount => '应用信息'; + + @override + String get homeAccountTabInfo => '关于应用'; + + @override + String get homeAccountTabMe => '联系本王'; + + @override + String get homeAccountSupport => '支持项目'; + + @override + String get searchWidget => '搜索组件'; + + @override + String get stateless => '无态'; + + @override + String get stateful => '有态'; + + @override + String get single => '单渲'; + + @override + String get multi => '多渲'; + + @override + String get sliver => '滑片'; + + @override + String get proxy => '代理'; + + @override + String get other => '其他'; + + @override + String get homeTabWidget => '组件'; + + @override + String get homeTabPaint => '绘制'; + + @override + String get homeTabKnowledge => '知识'; + + @override + String get homeTabTools => '工具'; + + @override + String get homeTabMine => '我的'; + + @override + String get dataManagement => '数据管理'; + + @override + String get userCollection => '我的收藏'; + + @override + String get aboutApplications => '关于应用'; + + @override + String get contactThisKing => '联系本王'; + + @override + String get appSettings => '应用设置'; + + @override + String get darkMode => '深色模式'; + + @override + String get themeColorSetting => '主题色设置'; + + @override + String get fontSetting => '字体设置'; + + @override + String get settingLanguageText => '多语言'; + + @override + String get codeHighlightStyle => '代码高亮样式'; + + @override + String get versionInformation => '版本信息'; + + @override + String get displayPerformanceFloatingLayer => '显示性能浮层'; + + @override + String get showFloatingTools => '显示浮动工具'; + + @override + String get followSystem => '跟随系统'; + + @override + String get afterOpeningWillFollowTheSystemToOpenOrCloseDarkMode => '开启后,将跟随系统打开或关闭深色模式'; + + @override + String get manualSetting => '手动设置'; + + @override + String get lightMode => '浅色模式'; + + @override + String get settingLanguage => '设置语言'; + + @override + String get appDetails => '应用详情'; + + @override + String get checkUpdate => '检查新版本'; + + @override + String get downloadNewVersion => '下载新版本'; + + @override + String get downloadingNewVersion => '新版本下载中...'; + + @override + String get currentIsNew => '当前应用已是最新版本!'; + + @override + String get checkDatabaseNewVersion => '检查数据库新版本'; + + @override + String get viewThisProjectGithubRepository => '《查看本项目Github仓库》'; + + @override + String get favorite => '已收藏'; + + @override + String get enterComponentName => '输入组件名称'; + + @override + String get containerComponents => '容器组件'; + + @override + String get componentTavern => '组件酒肆'; + + @override + String get cherishedComponents => '珍藏组件'; + + @override + String get textImageCollection => '图文集'; + + @override + String get layoutCollection => '布局集'; + + @override + String get eventCollection => '事件集'; + + @override + String get animationCollection => '动画集'; + + @override + String get slidingCollection => '滑动集'; + + @override + String get decorationCollection => '装饰集'; + + @override + String get assemblyCollection => '组装集'; + + @override + String get functionCollection => '功能集'; + + @override + String get popupCollection => '弹出集'; + + @override + String get themeCollection => '主题集'; + + @override + String get derivativeCollection => '衍生集'; + + @override + String get hardToCategorize => '很难分'; + + @override + String get basicDrawing => '基础绘制'; + + @override + String get basicDrawingDesc => '收录一些基础图形绘制案例,这些案例对初涉绘制的编程者会非常友好。通过这些案例,可以学会点、线、矩形、圆、圆弧、文字、图片等基本图形的绘制方法,了解 Canvas、Paint、Path 等绘制中核心对象的使用。'; + + @override + String get animationGesture => '动画手势'; + + @override + String get animationGestureDesc => '收录一些动画和手势的绘制案例,这些案例会让绘制更具有操作性。通过这些案例,可以学会动画和手势的使用,如滑动、旋转、缩放、移动等效果,让绘制不再只是静态展现。'; + + @override + String get particleDrawing => '粒子绘制'; + + @override + String get particleDrawingDesc => '收录一些粒子相关的绘制案例,这些案例将是绘制的顶级操作。通过这些案例,可以学会如何使用粒子来绘制惊艳的视觉效果,如粒子时钟、粒子爆炸、粒子背景等效果,让绘制拥有无限可能。'; + + @override + String get interestingDrawing => '趣味绘制'; + + @override + String get interestingDrawingDesc => '收录一些比较有趣的绘制案例,让我们一起在这里一起体验绘制的乐趣、编程的乐趣和智慧的乐趣吧。'; + + @override + String get artGallery => '艺术画廊'; + + @override + String get artGalleryDesc => '收录一些殿堂级的绘制案例,这些案例将是绘制的巅峰作品,它们的没有任何的实用性,也不为任何需求而生,它们仅是因为存在而存在,是人类智慧和表达的媒介,称谓艺术。'; + + @override + String get drawingOfImages => '本样例介绍如何进行图片的绘制: 通过加载图片并将图片资源绘制到指定的区域。在上层绘制一批 45”倾角的栅格线,来练习线条的绘制 '; + + @override + String get digitalDisplayTube => '本样例介绍如何绘制 LED 数字显示管,以此练习对路径 Path 的使用、变换、组合,以及组件封装的知识。是一个非常好的绘制案例 '; + + @override + String get pathDrawing => '本样例介绍如何进行简单的路径绘制,以及画板的旋转,再结合动画让风车旋转。这是一个非常精简的绘制与动画结合的案例。 '; + + @override + String get gridCoordinateSystem => '本样例介绍如何使用线路径和文字绘制网格坐标系,并将绘制对象进行封装,方便重用。坐标系也会在绘制时提供参考,入门必备。 '; + + @override + String get polarCoordinateSystemOfFaces => '本样例介绍如何使用绘制平面的极坐标系,并根据函数方程收集极坐标进行绘制。 '; + + @override + String get drawFunctionCurvesForPathPairs => '本样例介绍如何使用路径对函数曲线进行绘制,通过函数曲线上的少量点通过贝塞尔曲线进行拟合。 '; + + @override + String get drawRegularPolygons => '本样例介绍如何在圆中收集点位,绘制正多边形,是练习绘制及形成路径的很好案例。\n特殊操作:+、- 修改边数'; + + @override + String get randomNumberProcessing => '本样例介绍绘制矩形及随机数处理。通过点位集合确定矩形位置信息,将其绘制出来。可以练习对数据的控制能力。'; + + @override + String get clockDrawing => '本样例通过时钟的绘制,练习 Flutter 中旋转刻度类型的绘制技巧,并通过动画使表盘指针转动。'; + + @override + String get drawSprings => ' 本样例介绍如何绘制弹簧,通过触点竖直拖拽拉伸、压缩,放手时进行恢复动画,是一个很好的综合小案例。特殊操作:上下拖拽伸缩弹簧 '; + + @override + String get theApplicationOfAnglesInDrawing => '本样例介绍如何根据以某个点为中心,进行旋转运动。以此学习两点间的角度在绘制中的应用。\n特殊操作:点击运行'; + + @override + String get usingShadersAndFilters => '本样例介绍如何在绘制中使用着色器和过滤器,并通过动画进行数值变化达到旋转流光效果。'; + + @override + String get pathDrawingFunctionCurve => '本样例介绍如何使用路径绘制函数曲线,并使用路径测量进行动画'; + + @override + String get thePathOfBingDwenDwen => '本样例会绘制 2022 年北京冬奥会吉祥物冰墩墩的路径,并使用路径测量进行动画。\n特殊操作:点击运行'; + + @override + String get drawCubicBesselCurve => '本样例介绍如何绘制三次贝塞尔曲线,通过触点判断某点是否激活,据此控制点的位置达到拖动控制效果。\n特殊操作:单击绘点,双击清除'; + + @override + String get theEffectOfAnimationCurve => '本样例通过直观的方式,来查看动画曲线 curve 的作用效果,让大家对动画有更深的理解。\n特殊操作:点击运行'; + + @override + String get randomParticlesAndBoundaryBouncing => ' 本样例介绍如何创建随机粒子及边界反弹逻辑处理,是学习粒子运动非常好的入门案例特殊操作:单击停止/运行 '; + + @override + String get particleCollision => '本样例介绍如何对个粒子进行碰撞检测,并分裂处多个粒子,是一个比较有趣的案例。\n特殊操作:单击重置'; + + @override + String get particle => '本样例介绍将图片使用粒子表示,并对粒子进行动画处理,达到爆炸的效果。\n特殊操作:单击运行'; + + @override + String get rectangleAndRandomNumbers => '本样例介绍绘制矩形及随机数处理。通过点位集合确定矩形位置信息,将其绘制出来。可以练习对数据的控制能力。\n特殊操作:点击随机生成'; + + @override + String get bingDwenDwen => '本样例是绘制 2022 年北京冬奥会吉祥物冰墩墩的形体,从中可以学到路径绘制、渐变色等知识。'; + + @override + String get pufengInjectionTest => '本样实现蒲丰投针试验的测试过程,根据概率来估算圆周率。其中可以学习到一些绘制小技巧已经数据的逻辑处理。'; + + @override + String get ticTacToe => '本例通过井字棋的绘制与逻辑校验,集合了手势、绘制、动画、校验等重要的技能,是一个非常好的联系案例。\n特殊操作:双击重置'; + + @override + String get tiledLines => '本样例根源来自generativeartistry.com的tiled-lines,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry'; + + @override + String get joyDivision => '本样例根源来自generativeartistry.com的joy-division,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry'; + + @override + String get cubicDisarray => '本样例根源来自generativeartistry.com的cubic-disarray,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry'; + + @override + String get triangularMesh => '本样例根源来自generativeartistry.com的triangular-mesh,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry'; + + @override + String get unDeuxTrois => '本样例根源来自generativeartistry.com的un-deux-trois,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry'; + + @override + String get circlePacking => '本样例根源来自generativeartistry.com的circle-packing,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry'; + + @override + String get hypnoticSquares => '本样例根源来自generativeartistry.com的hypnotic-squares,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry'; + + @override + String get pietMondrian => '本样例根源来自generativeartistry.com的piet-mondrian,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry'; + + @override + String get downloadCompressedPackage => '使用方式:\n1. 在 iconfont.cn 挑选图标,加入项目,下载压缩包。\n2. 选择 Flutter 项目地址,配置资源、产物文件位置。\n3. 点击生成代码按钮,即可生成相关代码。'; + + @override + String get qAIssues => '要点集录中的 QA 数据收录rUnit 以 point 为标签的 issues 中。如果需要提供数据,在 issues 中问答即可。'; + + @override + String get tips => '注:'; + + @override + String get visualSorting => '可视化排序'; + + @override + String get visual => '可视排序'; + + @override + String get insertion => '插入排序'; + + @override + String get bubble => '冒泡排序'; + + @override + String get cocktail => '鸡尾酒排序(双向冒泡排序)'; + + @override + String get comb => '梳排序'; + + @override + String get pigeonHole => '鸽巢排序'; + + @override + String get shell => '希尔排序'; + + @override + String get selection => '选择排序'; + + @override + String get gnome => '侏儒排序'; + + @override + String get cycle => '循环排序'; + + @override + String get heap => '堆排序'; + + @override + String get quick => '快速排序'; + + @override + String get merge => '归并排序'; + + @override + String get sortingAlgorithmConfiguration => '排序算法配置'; + + @override + String get dataCount => '数据数量(个数)'; + + @override + String get timeInterval => '时间间隔(微秒)'; + + @override + String get randomSeed => '随机种子'; + + @override + String get codeGeneration => '代码生成'; + + @override + String get generateCode => '生成代码'; + + @override + String get artifactLocation => '产物位置'; + + @override + String get codeClassLocation => '代码类存放位置'; + + @override + String get resourceDirectory => '资源目录'; + + @override + String get iconfontResourceLocation => 'iconfont 资源存放位置'; + + @override + String get projectPath => '项目路径'; + + @override + String get inputProjectAddress => '请选择或输入项目地址'; + + @override + String get iconfontCompressedPackagePath => 'Iconfont 压缩包路径'; + + @override + String get pleaseSelectOrInputIconfontCompressedPackagePath => '请选择或输入 iconfont 下载的压缩包路径'; + + @override + String get stayTuned => '敬请期待'; + + @override + String get iconFont => 'IconFont'; + + @override + String get dataClass => '数据类'; + + @override + String get stateManagement => '状态管理'; + + @override + String get jsonParsing => 'Json 解析'; + + @override + String get clickHereToJump => '点击这里跳转'; + + @override + String get knowledgeTabToly => '捷特文库'; + + @override + String get knowledgeTabAlgo => '算法演绎'; + + @override + String get knowledgeTabLayout => '布局宝库'; + + @override + String get knowledgeTabPoint => '要点宝库'; + + @override + String get knowledgeConstruction => '正在建设中'; + + @override + String get knowledgeToJuejin => '前往掘金'; + + @override + String get srcPath => '源码地址'; + + @override + String get widgetsInn => '组件酒肆'; + + @override + String get likedWidgets => '珍藏组件'; + + @override + String get relatedComponents => '相关组件'; + + @override + String get backupFavoritesCollectionData => '备份收藏集数据'; + + @override + String get syncFavoritesCollectionData => '同步收藏集数据'; + + @override + String get favoritesCollectionDataReset => '收藏集数据重置'; + + @override + String get resetSuccess => '重置成功!'; + + @override + String get dataSetBackupSuccess => '数据集备份成功!'; + + @override + String get dataSetBackupFailure => '数据集备份失败!'; + + @override + String get dataSynchronizationCopySuccess => '数据同步份成功!'; + + @override + String get dataSynchronizationCopyFailure => '数据同步份失败!'; + + @override + String get destructionRed => '毁灭之红'; + + @override + String get rageOrange => '愤怒之橙'; + + @override + String get warningYellow => '警告之黄'; + + @override + String get camouflageGreen => '伪装之绿'; + + @override + String get coldBlue => '冷漠之蓝'; + + @override + String get infiniteBlue => '无限之靛'; + + @override + String get mysteryPurple => '神秘之紫'; + + @override + String get destinyBlack => '归宿之黑'; + + @override + String get showBackground => '显示背景'; + + @override + String get toly => '张风捷特烈'; + + @override + String get dartHandbook => 'Dart 手册'; + + @override + String get codeCopiedSuccessfully => '代码复制成功'; + + @override + String get favoriteFolderManagement => '收藏夹管理'; + + @override + String get assembly => '组件'; + + @override + String get draw => '绘制'; + + @override + String get knowledge => '知识'; + + @override + String get collection => '收藏'; + + @override + String get my => '我的'; + + @override + String get picture => '幅'; + + @override + String get widgetInn => '组件酒肆'; + + @override + String get emptySearch => '没数据,哥也没办法\n(≡ _ ≡)/~┴┴'; + + @override + String get searchSomething => '哥们,搜点啥...≧◔◡◔≦'; + + @override + String get slogan => 'Flutter 的联合,编程者的联合'; +} diff --git a/modules/basic_system/l10n/lib/l10n.dart b/modules/basic_system/l10n/lib/l10n.dart new file mode 100644 index 000000000..74a26a66f --- /dev/null +++ b/modules/basic_system/l10n/lib/l10n.dart @@ -0,0 +1,4 @@ +library l10n; +export 'ext.dart'; +export 'enum/language.dart'; + diff --git a/modules/basic_system/l10n/pubspec.yaml b/modules/basic_system/l10n/pubspec.yaml new file mode 100644 index 000000000..d5b23bf5f --- /dev/null +++ b/modules/basic_system/l10n/pubspec.yaml @@ -0,0 +1,50 @@ +name: l10n +description: "A new Flutter package project." +version: 0.0.1 +homepage: + +environment: + sdk: ">=3.5.0 <4.0.0" + flutter: ">=1.17.0" +resolution: workspace +dependencies: + flutter: + sdk: flutter + flutter_localizations: + sdk: flutter + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + # To add assets to your package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # To add custom fonts to your package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/modules/basic_system/l10n/test/l10n_copy.dart b/modules/basic_system/l10n/test/l10n_copy.dart new file mode 100644 index 000000000..9118552fb --- /dev/null +++ b/modules/basic_system/l10n/test/l10n_copy.dart @@ -0,0 +1,22 @@ +import 'dart:io'; + +import 'package:path/path.dart' as path; + +void main() async { + Directory distDir = + Directory(path.join(Directory.current.path, '.dart_tool', 'flutter_gen', 'gen_l10n')); + if (!distDir.existsSync()) return; + + Directory srcDir = Directory(path.join(Directory.current.path, 'lib', 'gen_l10n')); + if (srcDir.existsSync()) { + await srcDir.delete(recursive: true); + } else { + await srcDir.create(recursive: true); + } + List entity = distDir.listSync(); + for (FileSystemEntity e in entity) { + if (e is File) { + e.copy(path.join(srcDir.path, path.basename(e.path))); + } + } +} diff --git a/modules/basic_system/l10n/test/l10n_test.dart b/modules/basic_system/l10n/test/l10n_test.dart new file mode 100644 index 000000000..a30976257 --- /dev/null +++ b/modules/basic_system/l10n/test/l10n_test.dart @@ -0,0 +1,6 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:l10n/l10n.dart'; + +void main() { +} diff --git a/packages/authentication/.gitignore b/modules/basic_system/storage/.gitignore similarity index 100% rename from packages/authentication/.gitignore rename to modules/basic_system/storage/.gitignore diff --git a/packages/algorithm/.metadata b/modules/basic_system/storage/.metadata similarity index 100% rename from packages/algorithm/.metadata rename to modules/basic_system/storage/.metadata diff --git a/packages/components/CHANGELOG.md b/modules/basic_system/storage/CHANGELOG.md similarity index 100% rename from packages/components/CHANGELOG.md rename to modules/basic_system/storage/CHANGELOG.md diff --git a/packages/components/LICENSE b/modules/basic_system/storage/LICENSE similarity index 100% rename from packages/components/LICENSE rename to modules/basic_system/storage/LICENSE diff --git a/packages/components/README.md b/modules/basic_system/storage/README.md similarity index 100% rename from packages/components/README.md rename to modules/basic_system/storage/README.md diff --git a/packages/components/analysis_options.yaml b/modules/basic_system/storage/analysis_options.yaml similarity index 100% rename from packages/components/analysis_options.yaml rename to modules/basic_system/storage/analysis_options.yaml diff --git a/modules/basic_system/storage/lib/src/app_storage.dart b/modules/basic_system/storage/lib/src/app_storage.dart new file mode 100644 index 000000000..70af2ad9f --- /dev/null +++ b/modules/basic_system/storage/lib/src/app_storage.dart @@ -0,0 +1,34 @@ +import 'package:storage/storage.dart'; + +import 'db_storage/flutter/article_db_store.dart'; +import 'db_storage/flutter/flutter_db_store.dart'; +import 'db_storage/flutter_unit/flutter_unit_db_store.dart'; + +class AppStorage { + AppStorage._(); + + static AppStorage? _instance; + + factory AppStorage() => _instance ??= AppStorage._(); + + final FlutterDbStore _flutterDb = FlutterDbStore(); + final FlutterUnitDbStore _flutterUnitDb = FlutterUnitDbStore(); + + final ArticleDbStore _articleDb = ArticleDbStore(); + + FlutterDbStore get flutter => _flutterDb; + ArticleDbStore get article => _articleDb; + FlutterUnitDbStore get flutterUnit => _flutterUnitDb; + + Future init() async { + await _flutterDb.open(); + await _flutterUnitDb.open(); + await _articleDb.open(); + } + + void close() async { + await _flutterDb.close(); + await _flutterUnitDb.close(); + await _articleDb.close(); + } +} diff --git a/modules/basic_system/storage/lib/src/db_storage/flutter/article_db_store.dart b/modules/basic_system/storage/lib/src/db_storage/flutter/article_db_store.dart new file mode 100644 index 000000000..2bf579e54 --- /dev/null +++ b/modules/basic_system/storage/lib/src/db_storage/flutter/article_db_store.dart @@ -0,0 +1,27 @@ +import 'dart:async'; +import 'package:sqflite/sqflite.dart'; +import 'package:sqflite_common_ffi/sqflite_ffi.dart'; +import 'package:fx_dao/fx_dao.dart'; + +import 'package:widget_module/widget_module.dart'; +import 'package:artifact/artifact.dart'; + +class ArticleDbStore extends FxDb { + @override + String get dbname => 'article.db'; + + @override + int get version => 1; + + @override + Future onCreate(Database db, int version) async {} + + @override + Iterable<(int, MigrationOperation)> get migrations => []; + + @override + Iterable get tables => [ + ColumnizeDao(), + ArticleDao(), + ]; +} diff --git a/modules/basic_system/storage/lib/src/db_storage/flutter/flutter_db_store.dart b/modules/basic_system/storage/lib/src/db_storage/flutter/flutter_db_store.dart new file mode 100644 index 000000000..3386bd79c --- /dev/null +++ b/modules/basic_system/storage/lib/src/db_storage/flutter/flutter_db_store.dart @@ -0,0 +1,36 @@ +import 'dart:async'; +import 'package:sqflite/sqflite.dart'; +import 'package:sqflite_common_ffi/sqflite_ffi.dart'; +import 'package:fx_dao/fx_dao.dart'; + +import 'package:widget_module/widget_module.dart'; +import 'package:widget_repository/widget_repository.dart'; + +class FlutterDbStore extends FxDb { + @override + String get dbname => 'flutter.db'; + + @override + int get version => 1; + + @override + Future onCreate(Database db, int version) async {} + + @override + void afterOpen(String dbpath) { + super.afterOpen(dbpath); + print("===DbPath:$dbpath=============="); + } + + @override + Iterable<(int, MigrationOperation)> get migrations => []; + + @override + Iterable get tables => [ + CategoryDao(), + WidgetDao(), + WidgetStatisticsDao(), + NodeDao(), + LikeDao(), + ]; +} diff --git a/modules/basic_system/storage/lib/src/db_storage/flutter_unit/dao/cache_dao.dart b/modules/basic_system/storage/lib/src/db_storage/flutter_unit/dao/cache_dao.dart new file mode 100644 index 000000000..423a28d3c --- /dev/null +++ b/modules/basic_system/storage/lib/src/db_storage/flutter_unit/dao/cache_dao.dart @@ -0,0 +1,76 @@ + +import 'package:fx_dao/fx_dao.dart'; +import 'package:sqflite/sqlite_api.dart'; + +import '../model/cache_po.dart'; + + +class CacheDao with HasDatabase, DbTable{ + @override + String get createSql =>""" +CREATE TABLE IF NOT EXISTS `$name` ( +`id` INTEGER PRIMARY KEY AUTOINCREMENT, +`filter` TEXT, +`content` TEXT, +`update` INTEGER, +`create` INTEGER, +`type` INTEGER +)"""; + + @override + String get name => 'app_cache'; + + + Future insert(CachePo po) => database.insert( + name, + po.toJson(), + conflictAlgorithm: ConflictAlgorithm.replace, + ); + + Future insertOrUpdate(CachePo po) async { + bool canUpdate = await shouldUpdate(po.id, po.update); + return database.insert( + name, + po.toJson(), + conflictAlgorithm: + canUpdate ? ConflictAlgorithm.replace : ConflictAlgorithm.ignore, + ); + } + + /// 当前数据是否需要更新 + Future shouldUpdate(int id, int updateAt) async { + List> data = await database + .rawQuery("SELECT `update` FROM $name WHERE id = ?", [id]); + // 没有数据,可以更新 + if (data.isEmpty) { + return true; + } + // 服务器中数据更新时间,大于本地数据库内容,可以更新 + return updateAt > data.first['update']; + } + + Future> query({ + required int type, + int page = 1, + int pageSize = 20, + String? filter, + }) async { + String queryArgs = ''; + List args = [type]; + queryArgs = "WHERE type = ? "; + if(filter!=null){ + queryArgs+="AND filter = ? "; + args.add(filter); + } + queryArgs += 'LIMIT ? OFFSET ?'; + args.addAll([pageSize, (page - 1) * pageSize]); + + List> data = await database.rawQuery( + "SELECT * FROM $name $queryArgs", + args, + ); + + List result = data.map((e) => CachePo.fromJson(e)).toList(); + return result; + } +} diff --git a/modules/basic_system/storage/lib/src/db_storage/flutter_unit/flutter_unit.dart b/modules/basic_system/storage/lib/src/db_storage/flutter_unit/flutter_unit.dart new file mode 100644 index 000000000..2b1d6a603 --- /dev/null +++ b/modules/basic_system/storage/lib/src/db_storage/flutter_unit/flutter_unit.dart @@ -0,0 +1,2 @@ +export 'dao/cache_dao.dart'; +export 'model/cache_po.dart'; \ No newline at end of file diff --git a/modules/basic_system/storage/lib/src/db_storage/flutter_unit/flutter_unit_db_store.dart b/modules/basic_system/storage/lib/src/db_storage/flutter_unit/flutter_unit_db_store.dart new file mode 100644 index 000000000..868f4ac69 --- /dev/null +++ b/modules/basic_system/storage/lib/src/db_storage/flutter_unit/flutter_unit_db_store.dart @@ -0,0 +1,28 @@ +import 'package:fx_dao/fx_dao.dart'; +import 'dao/cache_dao.dart'; + + + +class FlutterUnitDbStore extends FxDb { + + @override + Iterable get tables => [ + CacheDao() + ]; + + @override + String get dbname => 'flutter_unit.db'; + + @override + int get version => 1; + + @override + void afterOpen(String dbpath) { + super.afterOpen(dbpath); + print("====Opend:$dbpath==========="); + } + + @override + Iterable<(int, MigrationOperation)> get migrations => []; + +} diff --git a/packages/storage/lib/src/db_storage/models/cache_po.dart b/modules/basic_system/storage/lib/src/db_storage/flutter_unit/model/cache_po.dart similarity index 100% rename from packages/storage/lib/src/db_storage/models/cache_po.dart rename to modules/basic_system/storage/lib/src/db_storage/flutter_unit/model/cache_po.dart diff --git a/modules/basic_system/storage/lib/src/db_storage/storage.dart b/modules/basic_system/storage/lib/src/db_storage/storage.dart new file mode 100644 index 000000000..c07707a46 --- /dev/null +++ b/modules/basic_system/storage/lib/src/db_storage/storage.dart @@ -0,0 +1 @@ +export 'flutter_unit/flutter_unit.dart'; \ No newline at end of file diff --git a/packages/storage/lib/src/sp_storage/cao/app_config_cao.dart b/modules/basic_system/storage/lib/src/sp_storage/cao/app_config_cao.dart similarity index 100% rename from packages/storage/lib/src/sp_storage/cao/app_config_cao.dart rename to modules/basic_system/storage/lib/src/sp_storage/cao/app_config_cao.dart diff --git a/packages/storage/lib/src/sp_storage/exp.dart b/modules/basic_system/storage/lib/src/sp_storage/exp.dart similarity index 100% rename from packages/storage/lib/src/sp_storage/exp.dart rename to modules/basic_system/storage/lib/src/sp_storage/exp.dart diff --git a/packages/storage/lib/src/sp_storage/models/app_config_po.dart b/modules/basic_system/storage/lib/src/sp_storage/models/app_config_po.dart similarity index 88% rename from packages/storage/lib/src/sp_storage/models/app_config_po.dart rename to modules/basic_system/storage/lib/src/sp_storage/models/app_config_po.dart index c3eaeb234..3f5399fd4 100644 --- a/packages/storage/lib/src/sp_storage/models/app_config_po.dart +++ b/modules/basic_system/storage/lib/src/sp_storage/models/app_config_po.dart @@ -5,6 +5,7 @@ class AppConfigPo { final bool showOverlayTool; final bool showPerformanceOverlay; final int fontFamilyIndex; + final int languageIndex; final int codeStyleIndex; final int themeModeIndex; final int itemStyleIndex; @@ -15,6 +16,7 @@ class AppConfigPo { this.showOverlayTool = false, this.showPerformanceOverlay = false, this.fontFamilyIndex = 1, + this.languageIndex = 0, this.themeColorIndex = 4, this.codeStyleIndex = 0, this.themeModeIndex = 0, @@ -27,10 +29,11 @@ class AppConfigPo { showOverlayTool: map['showOverlayTool'] ?? false, showPerformanceOverlay: map['showPerformanceOverlay'] ?? false, fontFamilyIndex: map['fontFamilyIndex'] ?? 1, - themeColorIndex: map['themeColorIndex'] ?? 4, + themeColorIndex: map['themeColorIndex'] ?? 5, codeStyleIndex: map['codeStyleIndex'] ?? 0, themeModeIndex: map['themeModeIndex'] ?? 0, itemStyleIndex: map['itemStyleIndex'] ?? 0, + languageIndex: map['languageIndex'] ?? 0, ); } @@ -43,5 +46,6 @@ class AppConfigPo { 'codeStyleIndex': codeStyleIndex, 'themeModeIndex': themeModeIndex, 'itemStyleIndex': itemStyleIndex, + 'languageIndex': languageIndex, }; } diff --git a/packages/storage/lib/src/sp_storage/sp_storage.dart b/modules/basic_system/storage/lib/src/sp_storage/sp_storage.dart similarity index 76% rename from packages/storage/lib/src/sp_storage/sp_storage.dart rename to modules/basic_system/storage/lib/src/sp_storage/sp_storage.dart index a02d43376..98b06559c 100644 --- a/packages/storage/lib/src/sp_storage/sp_storage.dart +++ b/modules/basic_system/storage/lib/src/sp_storage/sp_storage.dart @@ -5,12 +5,9 @@ import 'cao/app_config_cao.dart'; class SpStorage { SpStorage._(); - static SpStorage? _storage; + static final SpStorage _instance = SpStorage._(); - static SpStorage get instance { - _storage = _storage ?? SpStorage._(); - return _storage!; - } + factory SpStorage() => _instance; SharedPreferences? _sp; diff --git a/modules/basic_system/storage/lib/storage.dart b/modules/basic_system/storage/lib/storage.dart new file mode 100644 index 000000000..3e9dff1f2 --- /dev/null +++ b/modules/basic_system/storage/lib/storage.dart @@ -0,0 +1,6 @@ +library storage; + + +export 'src/db_storage/storage.dart'; +export 'src/sp_storage/exp.dart'; +export 'src/app_storage.dart'; diff --git a/modules/basic_system/storage/pubspec.yaml b/modules/basic_system/storage/pubspec.yaml new file mode 100644 index 000000000..df10e1933 --- /dev/null +++ b/modules/basic_system/storage/pubspec.yaml @@ -0,0 +1,51 @@ +name: storage +description: A new Flutter project. +version: 0.0.1 +homepage: + +environment: + sdk: ">=3.5.0 <4.0.0" + flutter: ">=1.17.0" + +resolution: workspace + +dependencies: + flutter: + sdk: flutter + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # To add assets to your package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # To add custom fonts to your package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/storage/test/db_storage_test.dart b/modules/basic_system/storage/test/db_storage_test.dart similarity index 100% rename from packages/storage/test/db_storage_test.dart rename to modules/basic_system/storage/test/db_storage_test.dart diff --git a/modules/basic_system/toly_ui/.gitignore b/modules/basic_system/toly_ui/.gitignore new file mode 100644 index 000000000..ac5aa9893 --- /dev/null +++ b/modules/basic_system/toly_ui/.gitignore @@ -0,0 +1,29 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +build/ diff --git a/modules/basic_system/toly_ui/.metadata b/modules/basic_system/toly_ui/.metadata new file mode 100644 index 000000000..fe59252be --- /dev/null +++ b/modules/basic_system/toly_ui/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9" + channel: "stable" + +project_type: package diff --git a/packages/storage/CHANGELOG.md b/modules/basic_system/toly_ui/CHANGELOG.md similarity index 100% rename from packages/storage/CHANGELOG.md rename to modules/basic_system/toly_ui/CHANGELOG.md diff --git a/packages/storage/LICENSE b/modules/basic_system/toly_ui/LICENSE similarity index 100% rename from packages/storage/LICENSE rename to modules/basic_system/toly_ui/LICENSE diff --git a/packages/storage/README.md b/modules/basic_system/toly_ui/README.md similarity index 100% rename from packages/storage/README.md rename to modules/basic_system/toly_ui/README.md diff --git a/packages/storage/analysis_options.yaml b/modules/basic_system/toly_ui/analysis_options.yaml similarity index 100% rename from packages/storage/analysis_options.yaml rename to modules/basic_system/toly_ui/analysis_options.yaml diff --git a/modules/basic_system/toly_ui/lib/adapter/platform_view_adapter.dart b/modules/basic_system/toly_ui/lib/adapter/platform_view_adapter.dart new file mode 100644 index 000000000..d5d5f8344 --- /dev/null +++ b/modules/basic_system/toly_ui/lib/adapter/platform_view_adapter.dart @@ -0,0 +1,77 @@ +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +typedef WidthConditionFun = bool Function(double width); +typedef WidgetBuilder = Widget Function(BuildContext context); + +enum ViewAdapterType { width, platform } + +class PlatformViewAdapter extends StatelessWidget { + final Widget mobile; + final Widget desk; + final ViewAdapterType type; + + const PlatformViewAdapter({ + super.key, + required this.mobile, + required this.desk, + this.type = ViewAdapterType.width, + }); + + @override + Widget build(BuildContext context) { + switch (type) { + case ViewAdapterType.width: + return WidthConditionBuilder( + conditionMap: { + phoneSize: (_) => mobile, + deskSize: (_) => desk, + }, + ); + case ViewAdapterType.platform: + bool isDesk = kIsWeb || + Platform.isMacOS || + Platform.isWindows || + Platform.isLinux; + return isDesk ? desk : mobile; + } + } + + bool phoneSize(double size) { + return size > 0 && size <= 500; + } + + bool deskSize(double size) { + return size > 500; + } +} + +class WidthConditionBuilder extends StatelessWidget { + final Map conditionMap; + final Widget unMatchWidget; + + const WidthConditionBuilder({ + super.key, + required this.conditionMap, + this.unMatchWidget = const SizedBox.shrink(), + }); + + @override + Widget build(BuildContext context) { + return LayoutBuilder(builder: (_, c) { + List conditions = conditionMap.keys.toList(); + WidthConditionFun? active; + for (int i = 0; i < conditions.length; i++) { + if (conditions[i](c.maxHeight)) { + active = conditions[i]; + } + } + if (active != null) { + return conditionMap[active]!(context); + } + return unMatchWidget; + }); + } +} diff --git a/packages/components/lib/toly_ui/button/feedback_widget.dart b/modules/basic_system/toly_ui/lib/button/feedback_widget.dart similarity index 82% rename from packages/components/lib/toly_ui/button/feedback_widget.dart rename to modules/basic_system/toly_ui/lib/button/feedback_widget.dart index f46e1c0cb..f90777d74 100644 --- a/packages/components/lib/toly_ui/button/feedback_widget.dart +++ b/modules/basic_system/toly_ui/lib/button/feedback_widget.dart @@ -73,17 +73,21 @@ class _FeedBackState extends State with SingleTickerProviderStat @override Widget build(BuildContext context) { - return GestureDetector( - onLongPress: widget.onLongPressed, - onTap: () { - _controller.forward(); - widget.onPressed?.call(); - }, - child: AnimatedBuilder( - animation: _controller, - child: widget.child, - builder: (ctx, child) => _buildByMode(child, widget.mode), - )); + return MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + onLongPress: widget.onLongPressed, + behavior: HitTestBehavior.opaque, + onTap: () { + _controller.forward(); + widget.onPressed?.call(); + }, + child: AnimatedBuilder( + animation: _controller, + child: widget.child, + builder: (ctx, child) => _buildByMode(child, widget.mode), + )), + ); } Widget _buildByMode(Widget? child, FeedMode mode) { diff --git a/packages/components/lib/toly_ui/code/code.dart b/modules/basic_system/toly_ui/lib/code/code.dart similarity index 100% rename from packages/components/lib/toly_ui/code/code.dart rename to modules/basic_system/toly_ui/lib/code/code.dart diff --git a/modules/basic_system/toly_ui/lib/code/code_widget.dart b/modules/basic_system/toly_ui/lib/code/code_widget.dart new file mode 100644 index 000000000..c0cc9cc4c --- /dev/null +++ b/modules/basic_system/toly_ui/lib/code/code_widget.dart @@ -0,0 +1,55 @@ +/// create by 张风捷特烈 on 2020-04-15 +/// contact me by email 1981462002@qq.com +/// 说明: +import 'package:flutter/material.dart'; + +import 'high_light_code.dart'; +import 'highlighter_style.dart'; +import 'language/dart_languge.dart'; + +class CodeWidget extends StatelessWidget { + CodeWidget( + {Key? key, + required this.code, + required this.style, + this.fontSize = 13, + this.fontFamily}) + : super(key: key); + + final String code; + final HighlighterStyle style; + final double fontSize; + final String? fontFamily; + + @override + Widget build(BuildContext context) { + Widget body; + Widget codeWidget; + try { + codeWidget = SelectableText.rich( + selectionControls: MaterialTextSelectionControls(), + TextSpan( + style: TextStyle(fontSize: fontSize, fontFamily: fontFamily), + children: [ + CodeHighlighter(style: style, language: const DartLanguage()) + .format(code) + ], + ), + ); + } catch (err) { + print(err); + codeWidget = SelectableText(code); + } + body = SingleChildScrollView( + child: Container( + alignment: Alignment.centerLeft, + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: style.backgroundColor ?? const Color(0xffF6F8FA), + borderRadius: const BorderRadius.all(Radius.circular(5.0))), + child: codeWidget, + ), + ); + return body; + } +} diff --git a/packages/components/lib/toly_ui/code/high_light_code.dart b/modules/basic_system/toly_ui/lib/code/high_light_code.dart similarity index 98% rename from packages/components/lib/toly_ui/code/high_light_code.dart rename to modules/basic_system/toly_ui/lib/code/high_light_code.dart index d83a00e8e..fc15af2dc 100644 --- a/packages/components/lib/toly_ui/code/high_light_code.dart +++ b/modules/basic_system/toly_ui/lib/code/high_light_code.dart @@ -19,7 +19,7 @@ import 'language/language.dart'; abstract class Highlighter { // ignore: one_member_abstracts - Language language; + ProgramLanguage language; Highlighter({required this.language}); @@ -30,7 +30,7 @@ abstract class Highlighter { //暗黑模式下的高亮样式 class CodeHighlighter extends Highlighter { CodeHighlighter( - {Language language = const DartLanguage(), HighlighterStyle? style}):super(language: language) { + {ProgramLanguage language = const DartLanguage(), HighlighterStyle? style}):super(language: language) { _spans = <_HighlightSpan>[]; _style = style ?? HighlighterStyle.fromColors(HighlighterStyle.lightColor); } diff --git a/packages/components/lib/toly_ui/code/highlighter_style.dart b/modules/basic_system/toly_ui/lib/code/highlighter_style.dart similarity index 100% rename from packages/components/lib/toly_ui/code/highlighter_style.dart rename to modules/basic_system/toly_ui/lib/code/highlighter_style.dart diff --git a/packages/components/lib/toly_ui/code/language/dart_languge.dart b/modules/basic_system/toly_ui/lib/code/language/dart_languge.dart similarity index 96% rename from packages/components/lib/toly_ui/code/language/dart_languge.dart rename to modules/basic_system/toly_ui/lib/code/language/dart_languge.dart index 1c82d8edc..8341d1473 100644 --- a/packages/components/lib/toly_ui/code/language/dart_languge.dart +++ b/modules/basic_system/toly_ui/lib/code/language/dart_languge.dart @@ -5,7 +5,7 @@ import 'language.dart'; /// contact me by email 1981462002@qq.com /// 说明: -class DartLanguage extends Language{ +class DartLanguage extends ProgramLanguage{ const DartLanguage() : super('Dart'); diff --git a/modules/basic_system/toly_ui/lib/code/language/language.dart b/modules/basic_system/toly_ui/lib/code/language/language.dart new file mode 100644 index 000000000..812a3db46 --- /dev/null +++ b/modules/basic_system/toly_ui/lib/code/language/language.dart @@ -0,0 +1,17 @@ +/// create by 张风捷特烈 on 2021/1/21 +/// contact me by email 1981462002@qq.com +/// 说明: + +abstract class ProgramLanguage { + final String name; + + const ProgramLanguage(this.name); + + bool containsKeywords(String word); + + bool containsInTypes(String word); + + List get keywords; + + List get inTypes; +} diff --git a/packages/components/lib/toly_ui/decorations/round_rect_rab_indicator.dart b/modules/basic_system/toly_ui/lib/decorations/round_rect_rab_indicator.dart similarity index 100% rename from packages/components/lib/toly_ui/decorations/round_rect_rab_indicator.dart rename to modules/basic_system/toly_ui/lib/decorations/round_rect_rab_indicator.dart diff --git a/packages/components/lib/toly_ui/default/loading/planet_loading.dart b/modules/basic_system/toly_ui/lib/default/loading/planet_loading.dart similarity index 95% rename from packages/components/lib/toly_ui/default/loading/planet_loading.dart rename to modules/basic_system/toly_ui/lib/default/loading/planet_loading.dart index 029c01ee2..29ba49b86 100644 --- a/packages/components/lib/toly_ui/default/loading/planet_loading.dart +++ b/modules/basic_system/toly_ui/lib/default/loading/planet_loading.dart @@ -1,8 +1,10 @@ import 'dart:math'; -import 'package:components/toly_ui/toly_ui.dart'; import 'package:flutter/material.dart'; +import '../../ti/circle.dart'; +import '../../ti/math_runner.dart'; + /// create by 张风捷特烈 on 2020/10/24 /// contact me by email 1981462002@qq.com diff --git a/packages/components/lib/toly_ui/dialog/alert_conform_dialog.dart b/modules/basic_system/toly_ui/lib/dialog/alert_conform_dialog.dart similarity index 100% rename from packages/components/lib/toly_ui/dialog/alert_conform_dialog.dart rename to modules/basic_system/toly_ui/lib/dialog/alert_conform_dialog.dart diff --git a/packages/components/lib/toly_ui/dialog/delete_message_panel.dart b/modules/basic_system/toly_ui/lib/dialog/delete_message_panel.dart similarity index 99% rename from packages/components/lib/toly_ui/dialog/delete_message_panel.dart rename to modules/basic_system/toly_ui/lib/dialog/delete_message_panel.dart index e4ad12947..32a89c39c 100644 --- a/packages/components/lib/toly_ui/dialog/delete_message_panel.dart +++ b/modules/basic_system/toly_ui/lib/dialog/delete_message_panel.dart @@ -1,5 +1,5 @@ -import 'package:components/toly_ui/toly_ui.dart'; + import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; typedef AsyncTask = Future Function(BuildContext context); diff --git a/packages/components/lib/toly_ui/input/edit_panel.dart b/modules/basic_system/toly_ui/lib/input/edit_panel.dart similarity index 100% rename from packages/components/lib/toly_ui/input/edit_panel.dart rename to modules/basic_system/toly_ui/lib/input/edit_panel.dart diff --git a/packages/components/lib/toly_ui/input/icon_input.dart b/modules/basic_system/toly_ui/lib/input/icon_input.dart similarity index 100% rename from packages/components/lib/toly_ui/input/icon_input.dart rename to modules/basic_system/toly_ui/lib/input/icon_input.dart diff --git a/packages/components/lib/toly_ui/input/input_button.dart b/modules/basic_system/toly_ui/lib/input/input_button.dart similarity index 100% rename from packages/components/lib/toly_ui/input/input_button.dart rename to modules/basic_system/toly_ui/lib/input/input_button.dart diff --git a/packages/components/lib/toly_ui/markdown/markdown_widget.dart b/modules/basic_system/toly_ui/lib/markdown/markdown_widget.dart similarity index 100% rename from packages/components/lib/toly_ui/markdown/markdown_widget.dart rename to modules/basic_system/toly_ui/lib/markdown/markdown_widget.dart diff --git a/packages/components/lib/toly_ui/markdown/md_text_styles.dart b/modules/basic_system/toly_ui/lib/markdown/md_text_styles.dart similarity index 100% rename from packages/components/lib/toly_ui/markdown/md_text_styles.dart rename to modules/basic_system/toly_ui/lib/markdown/md_text_styles.dart diff --git a/packages/components/lib/toly_ui/markdown/syntax_high_lighter.dart b/modules/basic_system/toly_ui/lib/markdown/syntax_high_lighter.dart similarity index 100% rename from packages/components/lib/toly_ui/markdown/syntax_high_lighter.dart rename to modules/basic_system/toly_ui/lib/markdown/syntax_high_lighter.dart diff --git a/packages/components/lib/toly_ui/object/windmill.dart b/modules/basic_system/toly_ui/lib/object/windmill.dart similarity index 100% rename from packages/components/lib/toly_ui/object/windmill.dart rename to modules/basic_system/toly_ui/lib/object/windmill.dart diff --git a/packages/components/lib/toly_ui/popable/drop_selectable_widget.dart b/modules/basic_system/toly_ui/lib/popable/drop_selectable_widget.dart similarity index 100% rename from packages/components/lib/toly_ui/popable/drop_selectable_widget.dart rename to modules/basic_system/toly_ui/lib/popable/drop_selectable_widget.dart diff --git a/packages/components/lib/toly_ui/selector/burst_menu.dart b/modules/basic_system/toly_ui/lib/selector/burst_menu.dart similarity index 100% rename from packages/components/lib/toly_ui/selector/burst_menu.dart rename to modules/basic_system/toly_ui/lib/selector/burst_menu.dart diff --git a/packages/components/lib/toly_ui/selector/color_chooser.dart b/modules/basic_system/toly_ui/lib/selector/color_chooser.dart similarity index 98% rename from packages/components/lib/toly_ui/selector/color_chooser.dart rename to modules/basic_system/toly_ui/lib/selector/color_chooser.dart index 2c7fdf291..9d70c648a 100644 --- a/packages/components/lib/toly_ui/selector/color_chooser.dart +++ b/modules/basic_system/toly_ui/lib/selector/color_chooser.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:components/toly_ui/toly_ui.dart'; +import '../button/feedback_widget.dart'; + typedef CheckCallback = void Function(T color); diff --git a/packages/components/lib/toly_ui/selector/multi_chip_filter.dart b/modules/basic_system/toly_ui/lib/selector/multi_chip_filter.dart similarity index 100% rename from packages/components/lib/toly_ui/selector/multi_chip_filter.dart rename to modules/basic_system/toly_ui/lib/selector/multi_chip_filter.dart diff --git a/packages/components/lib/toly_ui/sliver_header/sliver_pinned_header.dart b/modules/basic_system/toly_ui/lib/sliver_header/sliver_pinned_header.dart similarity index 100% rename from packages/components/lib/toly_ui/sliver_header/sliver_pinned_header.dart rename to modules/basic_system/toly_ui/lib/sliver_header/sliver_pinned_header.dart diff --git a/packages/components/lib/toly_ui/sliver_header/sliver_snap_header.dart b/modules/basic_system/toly_ui/lib/sliver_header/sliver_snap_header.dart similarity index 100% rename from packages/components/lib/toly_ui/sliver_header/sliver_snap_header.dart rename to modules/basic_system/toly_ui/lib/sliver_header/sliver_snap_header.dart diff --git a/packages/components/lib/toly_ui/ti/circle.dart b/modules/basic_system/toly_ui/lib/ti/circle.dart similarity index 100% rename from packages/components/lib/toly_ui/ti/circle.dart rename to modules/basic_system/toly_ui/lib/ti/circle.dart diff --git a/modules/basic_system/toly_ui/lib/ti/circle_image.dart b/modules/basic_system/toly_ui/lib/ti/circle_image.dart new file mode 100644 index 000000000..bb7d20ccf --- /dev/null +++ b/modules/basic_system/toly_ui/lib/ti/circle_image.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; +import 'package:storage/storage.dart'; +import 'package:widget_module/widget_module.dart'; + +class CircleImage extends StatelessWidget { + + const CircleImage({ + Key? key, + this.borderSize = 3, + required this.image, + this.size = 70, + this.shadowColor, + this.roundColor, + }) : super(key: key); + + final ImageProvider image; //图片 + final double size; //大小 + final Color? shadowColor; //阴影颜色 + final Color? roundColor; //边框颜色 + final double borderSize; + + @override + Widget build(BuildContext context) { + return Container( + width: size, + height: size, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: roundColor ?? Colors.white, + boxShadow: [ + BoxShadow( + color: shadowColor ?? Colors.grey.withOpacity(0.3), + offset: const Offset(0.0, 0.0), + blurRadius: 3.0, + spreadRadius: 0.0, + ), + ], + ), + child: Padding( + padding: EdgeInsets.all(borderSize), + child: GestureDetector( + onTap: () async{ + var data = await AppStorage().flutter().queryWidgetByName('Container'); + print(data); + }, + child: DecoratedBox( + decoration: BoxDecoration( + image: DecorationImage( + image: image, + fit: BoxFit.cover, + filterQuality: FilterQuality.low), + shape: BoxShape.circle, + ), + ), + ), + ), + ); + } +} diff --git a/packages/components/lib/toly_ui/ti/circle_text.dart b/modules/basic_system/toly_ui/lib/ti/circle_text.dart similarity index 100% rename from packages/components/lib/toly_ui/ti/circle_text.dart rename to modules/basic_system/toly_ui/lib/ti/circle_text.dart diff --git a/packages/components/lib/toly_ui/ti/color_wrapper.dart b/modules/basic_system/toly_ui/lib/ti/color_wrapper.dart similarity index 100% rename from packages/components/lib/toly_ui/ti/color_wrapper.dart rename to modules/basic_system/toly_ui/lib/ti/color_wrapper.dart diff --git a/packages/components/lib/toly_ui/ti/math_runner.dart b/modules/basic_system/toly_ui/lib/ti/math_runner.dart similarity index 100% rename from packages/components/lib/toly_ui/ti/math_runner.dart rename to modules/basic_system/toly_ui/lib/ti/math_runner.dart diff --git a/modules/basic_system/toly_ui/lib/ti/panel.dart b/modules/basic_system/toly_ui/lib/ti/panel.dart new file mode 100644 index 000000000..cc6d0e2fb --- /dev/null +++ b/modules/basic_system/toly_ui/lib/ti/panel.dart @@ -0,0 +1,34 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +class Panel extends StatelessWidget { + final double radius; + final Color? color; + final Widget? child; + final EdgeInsetsGeometry? margin; + final BoxConstraints? constraints; + final AlignmentGeometry? alignment; + + const Panel( + {Key? key, + this.radius = 5.0, + this.color, + this.child, + this.margin = const EdgeInsets.all(10), + this.constraints, + this.alignment}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + alignment: alignment ?? Alignment.centerLeft, + padding: margin, + constraints: constraints, + decoration: BoxDecoration( + color: color ?? const Color(0xffF6F8FA), + borderRadius: BorderRadius.all(Radius.circular(radius))), + child: child, + ); + } +} diff --git a/packages/components/lib/toly_ui/ti/tag.dart b/modules/basic_system/toly_ui/lib/ti/tag.dart similarity index 100% rename from packages/components/lib/toly_ui/ti/tag.dart rename to modules/basic_system/toly_ui/lib/ti/tag.dart diff --git a/packages/components/lib/toly_ui/ti/text_typer.dart b/modules/basic_system/toly_ui/lib/ti/text_typer.dart similarity index 100% rename from packages/components/lib/toly_ui/ti/text_typer.dart rename to modules/basic_system/toly_ui/lib/ti/text_typer.dart diff --git a/packages/components/lib/toly_ui/ti/toly_switch_list_tile.dart b/modules/basic_system/toly_ui/lib/ti/toly_switch_list_tile.dart similarity index 100% rename from packages/components/lib/toly_ui/ti/toly_switch_list_tile.dart rename to modules/basic_system/toly_ui/lib/ti/toly_switch_list_tile.dart diff --git a/packages/components/lib/toly_ui/toly_ui.dart b/modules/basic_system/toly_ui/lib/toly_ui.dart similarity index 89% rename from packages/components/lib/toly_ui/toly_ui.dart rename to modules/basic_system/toly_ui/lib/toly_ui.dart index 78d42e24c..39daae04c 100644 --- a/packages/components/lib/toly_ui/toly_ui.dart +++ b/modules/basic_system/toly_ui/lib/toly_ui.dart @@ -22,4 +22,6 @@ export 'object/windmill.dart'; export 'sliver_header/sliver_pinned_header.dart'; export 'sliver_header/sliver_snap_header.dart'; export 'ti/toly_switch_list_tile.dart'; -export 'markdown/markdown_widget.dart' hide Highlighter; \ No newline at end of file +export 'markdown/markdown_widget.dart' hide Highlighter; + +export 'adapter/platform_view_adapter.dart'; \ No newline at end of file diff --git a/modules/basic_system/toly_ui/pubspec.yaml b/modules/basic_system/toly_ui/pubspec.yaml new file mode 100644 index 000000000..7959688d8 --- /dev/null +++ b/modules/basic_system/toly_ui/pubspec.yaml @@ -0,0 +1,50 @@ +name: toly_ui +description: "A new Flutter package project." +version: 0.0.1 +homepage: + +environment: + sdk: ">=3.5.0 <4.0.0" + flutter: ">=1.17.0" +resolution: workspace +dependencies: + flutter: + sdk: flutter + + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # To add assets to your package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # To add custom fonts to your package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/modules/basic_system/toly_ui/test/toly_ui_test.dart b/modules/basic_system/toly_ui/test/toly_ui_test.dart new file mode 100644 index 000000000..a31c1f805 --- /dev/null +++ b/modules/basic_system/toly_ui/test/toly_ui_test.dart @@ -0,0 +1,12 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:toly_ui/toly_ui.dart'; + +void main() { + // test('adds one to input values', () { + // final calculator = Calculator(); + // expect(calculator.addOne(2), 3); + // expect(calculator.addOne(-7), -6); + // expect(calculator.addOne(0), 1); + // }); +} diff --git a/packages/components/.gitignore b/modules/basic_system/utils/.gitignore similarity index 100% rename from packages/components/.gitignore rename to modules/basic_system/utils/.gitignore diff --git a/packages/artifact/.metadata b/modules/basic_system/utils/.metadata similarity index 100% rename from packages/artifact/.metadata rename to modules/basic_system/utils/.metadata diff --git a/packages/utils/CHANGELOG.md b/modules/basic_system/utils/CHANGELOG.md similarity index 100% rename from packages/utils/CHANGELOG.md rename to modules/basic_system/utils/CHANGELOG.md diff --git a/packages/utils/LICENSE b/modules/basic_system/utils/LICENSE similarity index 100% rename from packages/utils/LICENSE rename to modules/basic_system/utils/LICENSE diff --git a/packages/utils/README.md b/modules/basic_system/utils/README.md similarity index 100% rename from packages/utils/README.md rename to modules/basic_system/utils/README.md diff --git a/packages/utils/analysis_options.yaml b/modules/basic_system/utils/analysis_options.yaml similarity index 100% rename from packages/utils/analysis_options.yaml rename to modules/basic_system/utils/analysis_options.yaml diff --git a/modules/basic_system/utils/lib/src/color_utils.dart b/modules/basic_system/utils/lib/src/color_utils.dart new file mode 100644 index 000000000..f95fdcaed --- /dev/null +++ b/modules/basic_system/utils/lib/src/color_utils.dart @@ -0,0 +1,71 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; + +import 'random_provider.dart'; + +class ColorUtils { + static Color randomColor({ + int limitA = 120, + int limitR = 0, + int limitG = 0, + int limitB = 0, + }) { + Random random = RandomProvider.random; + int a = limitA + random.nextInt(256 - limitA); //透明度值 + int r = limitR + random.nextInt(256 - limitR); //红值 + int g = limitG + random.nextInt(256 - limitG); //绿值 + int b = limitB + random.nextInt(256 - limitB); //蓝值 + return Color.fromARGB(a, r, g, b); //生成argb模式的颜色 + } + + + /// 使用方法: + /// var color1=ColorUtils.parse("#33428A43"); + /// var color2=ColorUtils.parse("33428A43"); + /// var color3=ColorUtils.parse("#428A43"); + ///var color4=ColorUtils.parse("428A43"); + /// + static Color parse(String code) { + Color result =Colors.red; + int value = 0 ; + if (code.contains("#")) { + try { + value = int.parse(code.substring(1), radix: 16); + } catch (e) { + print(e); + } + switch (code.length) { + case 7://6位 + result = Color(value + 0xFF000000); + break; + case 9://8位 + result = Color(value); + break; + default: + result =Colors.red; + } + }else { + try { + value = int.parse(code, radix: 16); + } catch (e) { + print(e); + } + switch (code.length) { + case 6: + result = Color(value + 0xFF000000); + break; + case 8: + result = Color(value); + break; + default: + result =Colors.red; + } + } + return result; + } + + static String colorString(Color color) => + "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; +} + diff --git a/lib/app/utils/convert_man.dart b/modules/basic_system/utils/lib/src/convert_man.dart similarity index 100% rename from lib/app/utils/convert_man.dart rename to modules/basic_system/utils/lib/src/convert_man.dart diff --git a/packages/utils/lib/src/http_utils/http_util.dart b/modules/basic_system/utils/lib/src/http_utils/http_util.dart similarity index 100% rename from packages/utils/lib/src/http_utils/http_util.dart rename to modules/basic_system/utils/lib/src/http_utils/http_util.dart diff --git a/packages/utils/lib/src/http_utils/http_utils.dart b/modules/basic_system/utils/lib/src/http_utils/http_utils.dart similarity index 100% rename from packages/utils/lib/src/http_utils/http_utils.dart rename to modules/basic_system/utils/lib/src/http_utils/http_utils.dart diff --git a/packages/utils/lib/src/http_utils/logs_interceptor.dart b/modules/basic_system/utils/lib/src/http_utils/logs_interceptor.dart similarity index 100% rename from packages/utils/lib/src/http_utils/logs_interceptor.dart rename to modules/basic_system/utils/lib/src/http_utils/logs_interceptor.dart diff --git a/packages/utils/lib/src/http_utils/response_interceptor.dart b/modules/basic_system/utils/lib/src/http_utils/response_interceptor.dart similarity index 100% rename from packages/utils/lib/src/http_utils/response_interceptor.dart rename to modules/basic_system/utils/lib/src/http_utils/response_interceptor.dart diff --git a/packages/utils/lib/src/http_utils/task_result.dart b/modules/basic_system/utils/lib/src/http_utils/task_result.dart similarity index 100% rename from packages/utils/lib/src/http_utils/task_result.dart rename to modules/basic_system/utils/lib/src/http_utils/task_result.dart diff --git a/packages/utils/lib/src/http_utils/token_interceptor.dart b/modules/basic_system/utils/lib/src/http_utils/token_interceptor.dart similarity index 100% rename from packages/utils/lib/src/http_utils/token_interceptor.dart rename to modules/basic_system/utils/lib/src/http_utils/token_interceptor.dart diff --git a/lib/app/utils/random_provider.dart b/modules/basic_system/utils/lib/src/random_provider.dart similarity index 100% rename from lib/app/utils/random_provider.dart rename to modules/basic_system/utils/lib/src/random_provider.dart diff --git a/packages/utils/lib/src/toast.dart b/modules/basic_system/utils/lib/src/toast.dart similarity index 100% rename from packages/utils/lib/src/toast.dart rename to modules/basic_system/utils/lib/src/toast.dart diff --git a/packages/utils/lib/utils.dart b/modules/basic_system/utils/lib/utils.dart similarity index 78% rename from packages/utils/lib/utils.dart rename to modules/basic_system/utils/lib/utils.dart index 43bcda4c7..f83611f36 100644 --- a/packages/utils/lib/utils.dart +++ b/modules/basic_system/utils/lib/utils.dart @@ -3,3 +3,4 @@ library utils; export 'src/color_utils.dart'; export 'src/http_utils/http_utils.dart'; export 'src/toast.dart'; +export 'src/convert_man.dart'; diff --git a/modules/basic_system/utils/pubspec.yaml b/modules/basic_system/utils/pubspec.yaml new file mode 100644 index 000000000..f6885348c --- /dev/null +++ b/modules/basic_system/utils/pubspec.yaml @@ -0,0 +1,51 @@ +name: utils +description: utils +version: 0.0.1 +homepage: + +environment: + sdk: ">=3.5.0 <4.0.0" + flutter: ">=1.17.0" +resolution: workspace +dependencies: + flutter: + sdk: flutter + + + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # To add assets to your package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # To add custom fonts to your package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/algorithm/test/utils_test.dart b/modules/basic_system/utils/test/utils_test.dart similarity index 100% rename from packages/algorithm/test/utils_test.dart rename to modules/basic_system/utils/test/utils_test.dart diff --git a/packages/storage/.gitignore b/modules/knowledge_system/algorithm/.gitignore similarity index 100% rename from packages/storage/.gitignore rename to modules/knowledge_system/algorithm/.gitignore diff --git a/packages/storage/.metadata b/modules/knowledge_system/algorithm/.metadata similarity index 100% rename from packages/storage/.metadata rename to modules/knowledge_system/algorithm/.metadata diff --git a/packages/widget_module/CHANGELOG.md b/modules/knowledge_system/algorithm/CHANGELOG.md similarity index 100% rename from packages/widget_module/CHANGELOG.md rename to modules/knowledge_system/algorithm/CHANGELOG.md diff --git a/packages/widget_module/LICENSE b/modules/knowledge_system/algorithm/LICENSE similarity index 100% rename from packages/widget_module/LICENSE rename to modules/knowledge_system/algorithm/LICENSE diff --git a/packages/widget_module/README.md b/modules/knowledge_system/algorithm/README.md similarity index 100% rename from packages/widget_module/README.md rename to modules/knowledge_system/algorithm/README.md diff --git a/packages/widget_module/analysis_options.yaml b/modules/knowledge_system/algorithm/analysis_options.yaml similarity index 100% rename from packages/widget_module/analysis_options.yaml rename to modules/knowledge_system/algorithm/analysis_options.yaml diff --git a/modules/knowledge_system/algorithm/lib/algorithm.dart b/modules/knowledge_system/algorithm/lib/algorithm.dart new file mode 100644 index 000000000..8c8957d75 --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/algorithm.dart @@ -0,0 +1,11 @@ +library algorithm; + +export 'src/sort/sort_parper.dart'; +export 'src/sort/top_bar/sort_bar.dart'; +export 'src/sort/top_bar/sort_button.dart'; +export 'src/sort/sort_setting.dart'; +export 'src/sort/sort_page.dart'; +export 'src/data_scope/sort_config.dart'; +export 'src/data_scope/state.dart'; + +export 'src/views/algo_page.dart'; \ No newline at end of file diff --git a/modules/knowledge_system/algorithm/lib/src/algorithm/finding/functions.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/finding/functions.dart new file mode 100644 index 000000000..e0585797d --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/algorithm/finding/functions.dart @@ -0,0 +1,35 @@ + +import '../../finding/data_scope/finding_state.dart'; +import 'functions/AStar.dart'; +import 'functions/BestFS.dart'; +import 'functions/BFS.dart'; +import 'functions/DFS.dart'; +import 'functions/dijkstra.dart'; + +typedef XY = (int,int); + +typedef FindFunction = Future Function(FindingState state); + +enum FindingAlgo { + bfs('BFS'), + dfs('DFS'), + aStar('AStar'), + bestFS('BestFS'), + dijkstra('dijkstra'), + ; + + final String path; + + const FindingAlgo(this.path); + + FindFunction get function{ + return switch(this){ + FindingAlgo.bfs => findingBFS, + FindingAlgo.dfs => findingDFS, + FindingAlgo.aStar => findingAStar, + FindingAlgo.bestFS => findingBestFS, + FindingAlgo.dijkstra => findingDijkstra, + }; + } +} + diff --git a/modules/knowledge_system/algorithm/lib/src/algorithm/finding/functions/AStar.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/finding/functions/AStar.dart new file mode 100644 index 000000000..6f4f54245 --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/algorithm/finding/functions/AStar.dart @@ -0,0 +1,109 @@ +// Copyright 2014 The 张风捷特烈 . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Author: 张风捷特烈 +// CreateTime: 2024-07-07 +// Contact Me: 1981462002@qq.com + +import '../../../finding/data_scope/finding_state.dart'; +import '../functions.dart'; + +/// AStar算法 +Future findingAStar(FindingState state) async { + XY start = state.start; + XY end = state.end; + // 创建优先队列作为开放列表 + PriorityQueue openList = PriorityQueue(); + openList.add(Node(start, 0, heuristic(start, end))); + var (row, col) = state.config.size; + // 初始化路径记录和访问标记 + state.visitedList = List.generate(col, (_) => List.filled(row, false)); + state.pathList = List.generate(col, (_) => List.filled(row, false)); + + while (!openList.isEmpty) { + // 从开放列表中移除f值最小的节点作为当前节点 + Node current = openList.removeFirst(); + int x = current.p.$1; + int y = current.p.$2; + + // 检查当前节点是否超出地图范围 + if (!state.isInMap(x, y)) { + throw "坐标越界"; + } + + // 如果当前节点已经访问过,则继续下一个节点 + if (state.visitedList[x][y]) continue; + state.visitedList[x][y] = true; + state.tick(); + + // 检查是否到达终点 + if (x == state.end.$1 && y == state.end.$2) { + print("找到路径了"); + // 回溯路径 + List> path = []; + Node? curNode = current; + while (curNode != null) { + path.add([curNode.p.$1, curNode.p.$2]); + curNode = curNode.parent; + } + // 将路径标记到pathList中,并显示路径动画 + state.pathList = List.generate(col, (_) => List.filled(row, false)); + for (var point in path) { + state.setPath(point[0], point[1], true); + await Future.delayed(const Duration(milliseconds: 50)); + } + return true; + } + + // 遍历当前节点的四个方向 + for (int i = 0; i < 4; i++) { + int newX = x + state.direction[i][0]; + int newY = y + state.direction[i][1]; + + // 如果新节点在地图范围内且是可通行的空地且未被访问过 + if (state.isInMap(newX, newY) && + state.blockList[newX][newY] == state.road && + !state.visitedList[newX][newY]) { + int g = current.g + 1; // 更新新节点的g值 + double h = heuristic((newX, newY), end); // 计算新节点的启发值h + openList.add(Node((newX, newY), g, h, current)); // 将新节点加入开放列表 + state.tick(); + await Future.delayed(const Duration(milliseconds: 50)); // 延迟显示效果 + } + } + } + + return false; // 开放列表为空,未找到路径,搜索失败 +} + +// 估价函数 +double heuristic(XY start, XY end) { + return ((start.$1 - end.$1).abs() + (start.$2 - end.$2).abs()).toDouble(); +} + +class Node { + XY p; // 坐标 + int g; // 节点的坐标和当前路径长度g值 + double h; // 启发值h + Node? parent; // 父节点 + + Node(this.p, this.g, this.h, [this.parent]); + + double get f => g + h; // 计算节点的总代价f值 +} + +class PriorityQueue { + final List _queue = []; // 优先队列使用的列表 + + bool get isEmpty => _queue.isEmpty; // 判断队列是否为空 + + void add(Node node) { + _queue.add(node); // 添加节点到队列 + _queue.sort((a, b) => a.f.compareTo(b.f)); // 根据f值排序,f值小的在前面 + } + + Node removeFirst() { + return _queue.removeAt(0); // 移除并返回队列中f值最小的节点 + } +} diff --git a/modules/knowledge_system/algorithm/lib/src/algorithm/finding/functions/BFS.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/finding/functions/BFS.dart new file mode 100644 index 000000000..db37b8dcc --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/algorithm/finding/functions/BFS.dart @@ -0,0 +1,88 @@ +// Copyright 2014 The 张风捷特烈 . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Author: 张风捷特烈 +// CreateTime: 2024-07-07 +// Contact Me: 1981462002@qq.com + +import 'dart:collection'; + +import '../../../finding/data_scope/finding_state.dart'; +import '../functions.dart'; + +/// 广度优先搜索(BFS)寻找路径 +Future findingBFS(FindingState state) async { + XY start = state.start; + XY end = state.end; + + // 创建队列并添加起始位置 + Queue> queue = Queue(); + queue.add([start.$1, start.$2]); + var (row,col) = state.config.size; + + // 初始化访问标记、路径记录和父节点记录 + state.visitedList = List.generate(col, (_) => List.filled(row, false)); + state.pathList = List.generate(col, (_) => List.filled(row, false)); + List>> parent = List.generate(col, (_) => List.generate(row, (_) => [-1, -1])); + + // 开始广度优先搜索 + while (queue.isNotEmpty) { + List current = queue.removeFirst(); + int x = current[0]; + int y = current[1]; + + // 检查当前位置是否越界 + if (!state.isInMap(x, y)) { + throw "坐标越界"; + } + + // 如果已经访问过当前位置,则继续下一个循环 + if (state.visitedList[x][y]) continue; + state.visitedList[x][y] = true; // 标记当前位置为已访问 + state.tick(); + + // 检查是否到达终点 + if (x == state.end.$1 && y == state.end.$2) { + print("找到路径了"); + + // 回溯路径并记录到 path 列表中 + List> path = []; + int curX = x; + int curY = y; + while (curX != -1 && curY != -1) { + path.add([curX, curY]); + List prev = parent[curX][curY]; + curX = prev[0]; + curY = prev[1]; + } + + // 清空原路径记录,并设置新的路径记录为找到的路径 + state.pathList = + List.generate(col, (_) => List.filled(row, false)); + for (var point in path) { + state.setPath(point[0], point[1], true); // 设置路径标记 + await Future.delayed(const Duration(milliseconds: 50)); + } + return true; // 返回找到路径的结果 + } + + // 遍历当前位置的四个方向 + for (int i = 0; i < 4; i++) { + int newX = x + state.direction[i][0]; + int newY = y + state.direction[i][1]; + + // 检查新位置是否在地图范围内,并且是可通过的道路,并且未访问过 + if (state.isInMap(newX, newY) && + state.blockList[newX][newY] == state.road && + !state.visitedList[newX][newY]) { + queue.add([newX, newY]); // 将新位置添加到队列中 + parent[newX][newY] = [x, y]; // 记录新位置的父节点 + state.tick(); + await Future.delayed(const Duration(milliseconds: 50)); // 等待一段时间,以便显示搜索过程(如果在 Flutter 中) + } + } + } + + return false; // 如果队列为空仍未找到路径,则返回 false +} \ No newline at end of file diff --git a/modules/knowledge_system/algorithm/lib/src/algorithm/finding/functions/BestFS.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/finding/functions/BestFS.dart new file mode 100644 index 000000000..2d462797f --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/algorithm/finding/functions/BestFS.dart @@ -0,0 +1,101 @@ +// Copyright 2014 The 张风捷特烈 . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Author: 张风捷特烈 +// CreateTime: 2024-07-07 +// Contact Me: 1981462002@qq.com + +import 'dart:math'; + +import '../../../finding/data_scope/finding_state.dart'; +import '../functions.dart'; +import 'AStar.dart'; + +/// BestFS算法 +Future findingBestFS(FindingState state) async { + XY start = state.start; + XY end = state.end; + + // 初始化优先队列、起始节点和父节点数组 + PriorityQueue priorityQueue = PriorityQueue(); + var (row,col) = state.config.size; + + Node startNode = + Node(start, 0, euclideanDistance(start.$1, start.$2, end.$1, end.$2)); + priorityQueue.add(startNode); + + // 初始化 visitedList、pathList 和 parent + // 初始化路径记录和访问标记 + state.visitedList = List.generate(col, (_) => List.filled(row, false)); + state.pathList = List.generate(col, (_) => List.filled(row, false)); + List> parent = + List.generate(col, (_) => List.filled(row, -1)); + + while (!priorityQueue.isEmpty) { + Node currentNode = priorityQueue.removeFirst(); + int x = currentNode.p.$1; + int y = currentNode.p.$2; + + // 如果当前节点已访问过,则跳过 + if (state.visitedList[x][y]) continue; + state.visitedList[x][y] = true; + state.tick(); + + // 检查是否到达终点 + if (x == state.end.$1 && y == state.end.$2) { + print("找到路径了"); + + // 回溯路径 + List> path = []; + int curX = x; + int curY = y; + while (curX != -1 && curY != -1) { + path.add([curX, curY]); + int prevX = parent[curX][curY] ~/ row; + int prevY = parent[curX][curY] % row; + curX = prevX; + curY = prevY; + print("curX:$curX,curY:$curY"); + // 防止出现死循环,检查是否回溯到起点 + if (curX == start.$1 && curY == start.$2) { + break; + } + } + // path = path.reversed.toList(); + + // 绘制路径到界面上 + state.pathList = + List.generate(col, (_) => List.filled(row, false)); + for (var point in path) { + state.setPath(point[0], point[1], true); + await Future.delayed(const Duration(milliseconds: 50)); + } + return true; // 找到路径并绘制完成,返回true + } + + // 探索当前节点的邻居节点 + for (int i = 0; i < 4; i++) { + int newX = x + state.direction[i][0]; + int newY = y + state.direction[i][1]; + + // 如果邻居节点在地图范围内且是可通行的道路且未访问过 + if (state.isInMap(newX, newY) && + state.blockList[newX][newY] == state.road && + !state.visitedList[newX][newY]) { + Node newNode = Node((newX, newY), currentNode.g + 1, + euclideanDistance(newX, newY, state.end.$1, state.end.$2)); + priorityQueue.add(newNode); + parent[newX][newY] = x * row + y; // 记录父节点 + state.tick(); + await Future.delayed(const Duration(milliseconds: 50)); + } + } + } + + return false; // 未找到路径,返回false +} + +double euclideanDistance(int x, int y, int endX, int endY) { + return sqrt(pow((x - endX).toDouble(), 2) + pow((y - endY).toDouble(), 2)); +} \ No newline at end of file diff --git a/modules/knowledge_system/algorithm/lib/src/algorithm/finding/functions/DFS.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/finding/functions/DFS.dart new file mode 100644 index 000000000..ffad96b7c --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/algorithm/finding/functions/DFS.dart @@ -0,0 +1,84 @@ +// Copyright 2014 The 张风捷特烈 . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Author: 张风捷特烈 +// CreateTime: 2024-07-07 +// Contact Me: 1981462002@qq.com + + +import '../../../finding/data_scope/finding_state.dart'; +import '../functions.dart'; + + /// 深度优先搜索(DFS)寻找路径 + Future findingDFS(FindingState state) async { + XY start = state.start; + XY end = state.end; + var (row,col) = state.config.size; + + // 初始化访问标记、路径记录和父节点记录 + state.visitedList = List.generate(col, (_) => List.filled(row, false)); + state.pathList = List.generate(col, (_) => List.filled(row, false)); + List>> parent = List.generate(col, (_) => List.generate(row, (_) => [-1, -1])); + + // 定义DFS递归函数 + Future dfs(int x, int y) async { + // 检查当前位置是否越界 + if (!state.isInMap(x, y)) { + throw "坐标越界"; + } + + // 如果已经访问过当前位置,则返回 false + if (state.visitedList[x][y]) return false; + state.visitedList[x][y] = true; // 标记当前位置为已访问 + state.tick(); + + // 检查是否到达终点 + if (x == state.end.$1 && y == state.end.$2) { + print("找到路径了"); + + // 回溯路径并记录到 path 列表中 + List> path = []; + int curX = x; + int curY = y; + while (curX != -1 && curY != -1) { + path.add([curX, curY]); + List prev = parent[curX][curY]; + curX = prev[0]; + curY = prev[1]; + } + + // 清空原路径记录,并设置新的路径记录为找到的路径 + state.pathList = + List.generate(col, (_) => List.filled(row, false)); + for (var point in path) { + state.setPath(point[0], point[1], true); // 设置路径标记 + await Future.delayed(const Duration(milliseconds: 50)); + } + return true; // 返回找到路径的结果 + } + + // 遍历当前位置的四个方向 + for (int i = 0; i < 4; i++) { + int newX = x + state.direction[i][0]; + int newY = y + state.direction[i][1]; + + // 检查新位置是否在地图范围内,并且是可通过的道路,并且未访问过 + if (state.isInMap(newX, newY) && + state.blockList[newX][newY] == state.road && + !state.visitedList[newX][newY]) { + parent[newX][newY] = [x, y]; // 记录新位置的父节点 + state.tick(); // 更新界面状态(如果在 Flutter 中) + await Future.delayed(const Duration(milliseconds: 50)); + if (await dfs(newX, newY)) { + return true; // 如果找到路径,则返回 true + } + } + } + + return false; // 如果当前位置无法继续搜索,则返回 false + } + + // 调用DFS函数并返回最终结果 + return await dfs(start.$1, start.$2); + } \ No newline at end of file diff --git a/modules/knowledge_system/algorithm/lib/src/algorithm/finding/functions/dijkstra.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/finding/functions/dijkstra.dart new file mode 100644 index 000000000..d028929a6 --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/algorithm/finding/functions/dijkstra.dart @@ -0,0 +1,101 @@ +// Copyright 2014 The 张风捷特烈 . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Author: 张风捷特烈 +// CreateTime: 2024-07-07 +// Contact Me: 1981462002@qq.com + +import '../../../finding/data_scope/finding_state.dart'; + +import '../functions.dart'; +import 'AStar.dart'; + +/// Dijkstra算法 +Future findingDijkstra(FindingState state) async { + XY start = state.start; + XY end = state.end; + var (row, col) = state.config.size; + // 初始化优先队列、距离数组和父节点数组 + PriorityQueue priorityQueue = PriorityQueue(); + List> distance = + List.generate(col, (_) => List.filled(row, -1)); + List> parent = + List.generate(col, (_) => List.filled(row, -1)); + + state.visitedList = List.generate(col, (_) => List.filled(row, false)); + state.pathList = List.generate(col, (_) => List.filled(row, false)); + // 将起始节点加入优先队列并初始化距离 + priorityQueue.add(Node(start, 0, 0.0)); + distance[start.$1][start.$2] = 0; + + while (!priorityQueue.isEmpty) { + // 从优先队列中取出当前节点 + Node currentNode = priorityQueue.removeFirst(); + int x = currentNode.p.$1; + int y = currentNode.p.$2; + int currentDistance = currentNode.g; + + // 如果当前节点已访问过,则跳过 + if (state.visitedList[x][y]) continue; + state.visitedList[x][y] = true; + state.tick(); + + // 检查是否到达终点 + if (x == state.end.$1 && y == state.end.$2) { + print("找到路径了"); + + // 回溯路径 + List> path = []; + int curX = x; + int curY = y; + while (curX != -1 && curY != -1) { + path.add([curX, curY]); + int prevX = parent[curX][curY] ~/ row; + int prevY = parent[curX][curY] % row; + curX = prevX; + curY = prevY; + print("curX:$curX,curY:$curY"); + // 防止出现死循环,检查是否回溯到起点 + if (curX == start.$1 && curY == start.$2) { + break; + } + } + // path = path.reversed.toList(); + + // 绘制路径到界面上 + state.pathList = + List.generate(col, (_) => List.filled(row, false)); + for (var point in path) { + state.setPath(point[0], point[1], true); + await Future.delayed(const Duration(milliseconds: 50)); + } + return true; // 找到路径并绘制完成,返回true + } + + // 探索当前节点的邻居节点 + for (int i = 0; i < 4; i++) { + int newX = x + state.direction[i][0]; + int newY = y + state.direction[i][1]; + + // 如果邻居节点在地图范围内且是可通行的道路且未访问过 + if (state.isInMap(newX, newY) && + state.blockList[newX][newY] == state.road && + !state.visitedList[newX][newY]) { + int newDistance = currentDistance + 1; // 更新距离 + + // 如果是第一次访问或找到更短的路径,则更新距离和父节点,并加入优先队列 + if (distance[newX][newY] == -1 || newDistance < distance[newX][newY]) { + distance[newX][newY] = newDistance; + Node newNode = Node((newX, newY), newDistance, 0.0); // Dijkstra没有启发式函数,h设为0 + priorityQueue.add(newNode); + parent[newX][newY] = x * row + y; // 记录父节点 + state.tick(); + await Future.delayed(const Duration(milliseconds: 50)); + } + } + } + } + + return false; // 未找到路径,返回false +} \ No newline at end of file diff --git a/packages/algorithm/lib/src/algorithm/sort/functions.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions.dart similarity index 100% rename from packages/algorithm/lib/src/algorithm/sort/functions.dart rename to modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions.dart diff --git a/packages/algorithm/lib/src/algorithm/sort/functions/bubble.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/bubble.dart similarity index 100% rename from packages/algorithm/lib/src/algorithm/sort/functions/bubble.dart rename to modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/bubble.dart diff --git a/packages/algorithm/lib/src/algorithm/sort/functions/cocktail.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/cocktail.dart similarity index 100% rename from packages/algorithm/lib/src/algorithm/sort/functions/cocktail.dart rename to modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/cocktail.dart diff --git a/packages/algorithm/lib/src/algorithm/sort/functions/comb.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/comb.dart similarity index 100% rename from packages/algorithm/lib/src/algorithm/sort/functions/comb.dart rename to modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/comb.dart diff --git a/packages/algorithm/lib/src/algorithm/sort/functions/cycle.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/cycle.dart similarity index 100% rename from packages/algorithm/lib/src/algorithm/sort/functions/cycle.dart rename to modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/cycle.dart diff --git a/packages/algorithm/lib/src/algorithm/sort/functions/gnome.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/gnome.dart similarity index 100% rename from packages/algorithm/lib/src/algorithm/sort/functions/gnome.dart rename to modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/gnome.dart diff --git a/packages/algorithm/lib/src/algorithm/sort/functions/heap.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/heap.dart similarity index 100% rename from packages/algorithm/lib/src/algorithm/sort/functions/heap.dart rename to modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/heap.dart diff --git a/packages/algorithm/lib/src/algorithm/sort/functions/insertion.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/insertion.dart similarity index 100% rename from packages/algorithm/lib/src/algorithm/sort/functions/insertion.dart rename to modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/insertion.dart diff --git a/packages/algorithm/lib/src/algorithm/sort/functions/merge.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/merge.dart similarity index 100% rename from packages/algorithm/lib/src/algorithm/sort/functions/merge.dart rename to modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/merge.dart diff --git a/packages/algorithm/lib/src/algorithm/sort/functions/oddEven.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/oddEven.dart similarity index 100% rename from packages/algorithm/lib/src/algorithm/sort/functions/oddEven.dart rename to modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/oddEven.dart diff --git a/packages/algorithm/lib/src/algorithm/sort/functions/pigeonHole.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/pigeonHole.dart similarity index 100% rename from packages/algorithm/lib/src/algorithm/sort/functions/pigeonHole.dart rename to modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/pigeonHole.dart diff --git a/packages/algorithm/lib/src/algorithm/sort/functions/quick.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/quick.dart similarity index 100% rename from packages/algorithm/lib/src/algorithm/sort/functions/quick.dart rename to modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/quick.dart diff --git a/packages/algorithm/lib/src/algorithm/sort/functions/selection.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/selection.dart similarity index 100% rename from packages/algorithm/lib/src/algorithm/sort/functions/selection.dart rename to modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/selection.dart diff --git a/packages/algorithm/lib/src/algorithm/sort/functions/shell.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/shell.dart similarity index 100% rename from packages/algorithm/lib/src/algorithm/sort/functions/shell.dart rename to modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/shell.dart diff --git a/packages/algorithm/lib/src/data_scope/sort_config.dart b/modules/knowledge_system/algorithm/lib/src/data_scope/sort_config.dart similarity index 100% rename from packages/algorithm/lib/src/data_scope/sort_config.dart rename to modules/knowledge_system/algorithm/lib/src/data_scope/sort_config.dart diff --git a/modules/knowledge_system/algorithm/lib/src/data_scope/state.dart b/modules/knowledge_system/algorithm/lib/src/data_scope/state.dart new file mode 100644 index 000000000..a5a4b4791 --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/data_scope/state.dart @@ -0,0 +1,70 @@ +import 'dart:math'; + +import 'package:flutter/cupertino.dart'; + +import '../algorithm/sort/functions.dart'; +import 'sort_config.dart'; + +enum SortStatus{ + none, // 未操作 + sorting, // 排序中 + sorted, // 排序完成 +} + +class SortState with ChangeNotifier{ + + SortState(){ + reset(); + } + + SortStatus status = SortStatus.none; + List data = []; + SortConfig _config = SortConfig(); + SortConfig get config => _config; + Random random = Random(); + + set config(SortConfig config){ + _config = config; + reset(); + notifyListeners(); + } + + void reset(){ + data.clear(); + status = SortStatus.none; + notifyListeners(); + int count = config.count; + if(config.seed!=-1){ + random = Random(config.seed); + } + for (int i = 0; i < count; i++) { + data.add(random.nextInt(1000)); + } + } + + void sort() async{ + status = SortStatus.sorting; + notifyListeners(); + SortFunction? sortFunction = sortFunctionMap[config.name]; + print(config.name); + if(sortFunction!=null){ + await sortFunction(data,(arr) async { + await Future.delayed(config.duration); + notifyListeners(); + }); + } + status = SortStatus.sorted; + notifyListeners(); + } +} + +class SortStateScope extends InheritedNotifier { + const SortStateScope({ + required super.notifier, + required super.child, + super.key, + }); + + static SortState of(BuildContext context) => + context.dependOnInheritedWidgetOfExactType()!.notifier!; +} \ No newline at end of file diff --git a/modules/knowledge_system/algorithm/lib/src/finding/data_scope/finding_config.dart b/modules/knowledge_system/algorithm/lib/src/finding/data_scope/finding_config.dart new file mode 100644 index 000000000..61dc99a77 --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/finding/data_scope/finding_config.dart @@ -0,0 +1,25 @@ +import '../../algorithm/finding/functions.dart'; + + +class FindingConfig { + final XY size; + final int seed; + final FindingAlgo algo; + + FindingConfig({ + this.seed = -1, + this.algo = FindingAlgo.bfs, + this.size = (31, 31), + }); + + FindingConfig copyWith({ + XY? size, + int? seed, + FindingAlgo? algo, + }) => + FindingConfig( + size: size ?? this.size, + seed: seed ?? this.seed, + algo: algo ?? this.algo, + ); +} diff --git a/modules/knowledge_system/algorithm/lib/src/finding/data_scope/finding_state.dart b/modules/knowledge_system/algorithm/lib/src/finding/data_scope/finding_state.dart new file mode 100644 index 000000000..360bbc690 --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/finding/data_scope/finding_state.dart @@ -0,0 +1,250 @@ +// Copyright 2014 The 张风捷特烈 . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Author: 张风捷特烈 +// CreateTime: 2024-07-07 +// Contact Me: 1981462002@qq.com + +import 'dart:math'; +import 'package:flutter/material.dart'; + +import '../../algorithm/finding/functions.dart'; +import 'finding_config.dart'; +import 'position.dart'; +import 'random_queue.dart'; + +enum FindingStatus{ + none, // 未操作 + finding, // 寻路中 + find, // 寻路完成 +} + +class FindingState with ChangeNotifier { + + int road = 1; //路 + int wall = 0; //墙 + + bool lockMap = false; + + //路径是否被访问过 + List> visitedList = []; + + //迷宫地图 + List> blockList = []; + + //寻找到的正确路线 + List> pathList = []; + + //方向 + List> direction = [ + [-1, 0], + [0, -1], + [1, 0], + [0, 1] + ]; + + FindingState() { + reset(); + } + + List> cacheBlockList = [[]]; + + void lock(){ + print("============lock====="); + lockMap=true; + cacheBlockList.clear(); + var (row,col) = _config.size; + + List.generate(row, (i) { + List tempMazeList = []; + List.generate(col, (j) { + tempMazeList.add(blockList[i][j]); //设置为墙 + }); + cacheBlockList.add(tempMazeList); + }); + notifyListeners(); + } + + void unlock(){ + lockMap=false; + cacheBlockList.clear(); + notifyListeners(); + } + + void tick(){ + notifyListeners(); + } + + FindingStatus status = FindingStatus.none; + FindingConfig _config = FindingConfig(); + FindingConfig get config => _config; + + late XY start; + late XY end; + + void reset(){ + status = FindingStatus.none; + if(lockMap){ + resetLocMap(); + notifyListeners(); + return; + } + + var (row,col) = _config.size; + if (row % 2 == 0 || col % 2 == 0) { + throw "地图行数和列数不能为偶数"; + } + + start = (1,0); + end = (row-2,col-1); + + blockList = []; + visitedList = []; + pathList = []; + //初始化迷宫遍历的方向(上、左、右、下)顺序(迷宫趋势) + //随机遍历顺序,提高迷宫生成的随机性(共12种可能性) + List.generate(direction.length, (index) { + int random = Random().nextInt(direction.length); + List temp = direction[random]; + direction[random] = direction[index]; + direction[index] = temp; + }); + + List.generate(row, (i) { + List tempVisited = []; + List tempMazeList = []; + List tempPath = []; + List.generate(col, (j) { + //行和列为奇数都设置为路,否则设置为墙 + if (i % 2 == 1 && j % 2 == 1) { + tempMazeList.add(1); //设置为路 + } else { + tempMazeList.add(0); //设置为墙 + } + //初始化访问,所有都没有访问过 + tempVisited.add(false); + tempPath.add(false); + }); + visitedList.add(tempVisited); + blockList.add(tempMazeList); + pathList.add(tempPath); + }); + + blockList[start.$1][start.$2] = 1; + blockList[end.$1][end.$2] = 1; + createMap(start.$1,start.$2+1); + status = FindingStatus.none; + + notifyListeners(); + } + + + ///设置为正确的路径 + void setPath(int x, int y, bool isPath) { + if (isInMap(x, y)) { + pathList[x][y] = isPath; + } + tick(); + } + + void changeAlgo(String name){ + FindingAlgo algo = FindingAlgo.values.firstWhere((e)=>e.path==name); + _config = config.copyWith(algo: algo); + if(lockMap){ + + } + status = FindingStatus.none; + notifyListeners(); + } + + void resetLocMap(){ + cacheBlockList = cacheBlockList; + visitedList = visitedList.map((innerList) => innerList.map((element) => false).toList()).toList(); + pathList = pathList.map((innerList) => innerList.map((element) => false).toList()).toList(); + } + + void run() async{ + status = FindingStatus.finding; + await config.algo.function(this); + status = FindingStatus.find; + notifyListeners(); + } + + /// 创建地图并使用随机队列生成地图结构 + void createMap(int startX, int startY) { + RandomQueue randomQueue = RandomQueue(); // 创建一个随机队列实例 + Position start = Position(startX, startY); // 创建起始位置 + randomQueue.addRandomQueue(start); // 将起始位置加入随机队列 + + visitedList[startX][startY] = true; // 标记起始位置为已访问 + + // 使用随机队列生成地图 + while (randomQueue.getSize() != 0) { + Position position = randomQueue.removeRandomQueue(); // 移除队列中的一个位置 + + // 生成四个方向的新位置 + List.generate(4, (i) { + int newX = position.x! + direction[i][0] * 2; + int newY = position.y! + direction[i][1] * 2; + + // 检查新位置是否在地图内且未被访问过 + if (isInMap(newX, newY) && !visitedList[newX][newY]) { + // 将新位置加入随机队列,并记录为已访问 + randomQueue.addRandomQueue(Position(newX, newY, prePosition: position)); + visitedList[newX][newY] = true; + + // 设置新位置与当前位置之间的路径或道路 + setWithRoad(position.x! + direction[i][0], position.y! + direction[i][1]); + } + }); + } + + // 把visitedList全部设置为没有访问 + visitedList = visitedList.map((innerList) => innerList.map((element) => false).toList()).toList(); + } + + ///设置为路 + void setWithRoad(int x, int y) { + blockList[x][y] = road; + } + + ///用来判断blockList[i][j]是否在地图内 + bool isInMap(int i, int j) { + return i >= 0 && i < config.size.$1 && j >= 0 && j < config.size.$2; + } + + Color getBoxColor(int i, int j) { + Color color = Colors.white; + if (blockList[i][j] == 0) { + color = Colors.black54; + } else if (start.$1 == i && start.$2 == j) { + color = Colors.blue; + } else if (end.$1 == i && end.$2 == j) { + color = Colors.red; + } else if (visitedList[i][j]) { + color = Colors.blue.shade200; + } + if (pathList[i][j]) { + color = Colors.orange; + } + return color; + } + +} + +/// Provides the current [FindingState] to descendant widgets in the tree. +class FindingStateScope extends InheritedNotifier { + const FindingStateScope({ + required super.notifier, + required super.child, + super.key, + }); + + static FindingState of(BuildContext context) => + context.dependOnInheritedWidgetOfExactType()!.notifier!; + + + static FindingState read(BuildContext context) => + context.getInheritedWidgetOfExactType()!.notifier!; +} \ No newline at end of file diff --git a/modules/knowledge_system/algorithm/lib/src/finding/data_scope/position.dart b/modules/knowledge_system/algorithm/lib/src/finding/data_scope/position.dart new file mode 100644 index 000000000..1f739aab1 --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/finding/data_scope/position.dart @@ -0,0 +1,11 @@ +import 'dart:collection'; + +base class Position extends LinkedListEntry { + int? x, y; + Position? prePosition; + + Position(int x, int y, {this.prePosition}) { + this.x = x; + this.y = y; + } +} diff --git a/modules/knowledge_system/algorithm/lib/src/finding/data_scope/random_queue.dart b/modules/knowledge_system/algorithm/lib/src/finding/data_scope/random_queue.dart new file mode 100644 index 000000000..2bf66b70a --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/finding/data_scope/random_queue.dart @@ -0,0 +1,48 @@ +import 'dart:collection'; +import 'dart:math'; + +import 'position.dart'; + +///借助LinkedList链表实现随机队列 +class RandomQueue { + LinkedList _queue = LinkedList(); + + RandomQueue() { + _queue = LinkedList(); + } + + void addRandomQueue(Position position) { + if (Random().nextInt(100) < 50) { + _queue.addFirst(position);// 插入到队列头部 + } else { + _queue.add(position);// 插入到队列尾部 + } + } + + ///返回随机队列中的一个元素 + Position removeRandomQueue() { + if (_queue.isEmpty) { + throw "数组为空"; + } else { + if (Random().nextInt(100) < 50) { + Position position = _queue.first; + _queue.remove(position); + return position; + } else { + Position position = _queue.last; + _queue.remove(position); + return position; + } + } + } + + //返回随机队列元素数量 + int getSize() { + return _queue.length; + } + + //判断随机队列是否为空 + bool isEmpty() { + return _queue.isEmpty; + } +} diff --git a/modules/knowledge_system/algorithm/lib/src/finding/view/board.dart b/modules/knowledge_system/algorithm/lib/src/finding/view/board.dart new file mode 100644 index 000000000..897f34a6d --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/finding/view/board.dart @@ -0,0 +1,51 @@ +// Copyright 2014 The 张风捷特烈 . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Author: 张风捷特烈 +// CreateTime: 2024-07-07 +// Contact Me: 1981462002@qq.com + +import 'package:flutter/material.dart'; + +import '../data_scope/finding_state.dart'; + +class MazeBoard extends StatelessWidget { + const MazeBoard({super.key}); + + @override + Widget build(BuildContext context) { + FindingState state = FindingStateScope.of(context); + + return LayoutBuilder( + builder: (ctx,cts){ + double side = cts.biggest.shortestSide*0.9; + double cellSize = side / state.config.size.$1; + + List rowList = []; + List.generate(state.blockList.length, (i) { + List columnList = []; + List.generate(state.blockList[i].length, (j) { + columnList.add(Container( + width: cellSize, + height: cellSize, + decoration: BoxDecoration( + color: state.getBoxColor(i, j), + ), + )); + }); + + rowList.add(Row( + mainAxisAlignment: MainAxisAlignment.center, + children: columnList, + )); + }); + + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: rowList, + ); + }, + ); + } +} diff --git a/modules/knowledge_system/algorithm/lib/src/finding/view/finding_button.dart b/modules/knowledge_system/algorithm/lib/src/finding/view/finding_button.dart new file mode 100644 index 000000000..5b83760f5 --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/finding/view/finding_button.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; +import 'package:tolyui/tolyui.dart'; + +import '../data_scope/finding_state.dart'; + +class FindingButton extends StatelessWidget { + const FindingButton({super.key}); + + @override + Widget build(BuildContext context) { + FindingState state = FindingStateScope.of(context); + VoidCallback? action; + IconData icon; + String? tip; + Color color; + switch (state.status) { + case FindingStatus.none: + icon = Icons.not_started_outlined; + color = Colors.green; + action = state.run; + tip = '运行'; + break; + case FindingStatus.finding: + icon = Icons.stop_circle_outlined; + color = Colors.grey; + tip = '排序中'; + action = null; + break; + case FindingStatus.find: + icon = Icons.refresh; + color = Colors.black; + action = state.reset; + tip = '重置'; + break; + } + return TolyAction( + onTap: action, + tooltip: tip, + child: Icon(icon, color: color, size: 20), + ); + } + +} + diff --git a/modules/knowledge_system/algorithm/lib/src/finding/view/finding_page.dart b/modules/knowledge_system/algorithm/lib/src/finding/view/finding_page.dart new file mode 100644 index 000000000..4bc13209a --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/finding/view/finding_page.dart @@ -0,0 +1,18 @@ +import 'package:algorithm/src/finding/view/board.dart'; +import 'package:flutter/material.dart'; +import 'finding_tool_bar.dart'; + +class FindingPage extends StatelessWidget { + const FindingPage({super.key}); + + @override + Widget build(BuildContext context) { + return const Scaffold( + appBar: FindingToolBar(), + body: Column( + children: [ + Expanded(child: MazeBoard()), + ], + )); + } +} diff --git a/modules/knowledge_system/algorithm/lib/src/finding/view/finding_tool_bar.dart b/modules/knowledge_system/algorithm/lib/src/finding/view/finding_tool_bar.dart new file mode 100644 index 000000000..0ffbbeebc --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/finding/view/finding_tool_bar.dart @@ -0,0 +1,140 @@ +import 'package:algorithm/algorithm.dart'; +import 'package:algorithm/src/algorithm/sort/functions.dart'; +import 'package:algorithm/src/finding/view/finding_button.dart'; +import 'package:app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:toly_ui/toly_ui.dart'; +import 'package:tolyui/basic/basic.dart'; +import 'package:tolyui/tolyui.dart'; + +import '../data_scope/finding_state.dart'; + + +class FindingToolBar extends StatelessWidget implements PreferredSizeWidget{ + const FindingToolBar({super.key}); + + @override + Widget build(BuildContext context) { + bool isDark = Theme.of(context).brightness == Brightness.dark; + FindingState state = FindingStateScope.of(context); + String name = state.config.algo.name; + + + return DragToMoveWrapper( + child: Container( + padding: EdgeInsets.symmetric(horizontal: 10), + height: 42, + color: isDark ? const Color(0xff2C3036) : Colors.white, + child: Row( + children: [ + const FindingButton(), + TolyAction( + tooltip: '更新地图', + onTap: () { + if(state.status==FindingStatus.finding){ + $message.warning(message: '正在寻路中,请稍后'); + return; + } + state.reset(); + // Scaffold.of(context).openEndDrawer(); + }, + child: const Icon( + Icons.refresh, + color: Colors.orange, + size: 20, + )), + TolyAction( + selected: state.lockMap, + tooltip: state.lockMap?'解除锁定':'锁定地图', + onTap: () { + if(state.status==FindingStatus.finding){ + $message.warning(message: '正在寻路中,请稍后'); + return; + } + if(state.lockMap){ + state.unlock(); + + }else{ + state.lock(); + + } + // Scaffold.of(context).openEndDrawer(); + }, + child: const Icon( + Icons.lock, + color: Colors.blue, + size: 20, + )), + Spacer(), + TolyLink( + href: + 'https://github.com/toly1994328/FlutterUnit/blob/master/packages/algorithm/lib/src/algorithm/finding/functions/${name}.dart', + text: '查看[$name 寻路]源码', + hoverColor: Colors.blue, + style: TextStyle(fontSize: 12,fontFamily: '宋体'), + onTap: jumpURL), + const SizedBox(width: 10), + // TolyAction( + // tooltip: '设置', + // onTap: () { + // Scaffold.of(context).openEndDrawer(); + // }, + // child: const Icon( + // Icons.settings, + // size: 20, + // )), + ], + ), + ), + ); + } + + @override + // TODO: implement preferredSize + Size get preferredSize => Size.fromHeight(42); +} + +class SortBar extends StatelessWidget { + const SortBar({super.key}); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + const SortButton(), + const SizedBox( + width: 10, + ), + const SortSelector(), + const SizedBox( + width: 10, + ), + GestureDetector( + onTap: () { + Scaffold.of(context).openEndDrawer(); + }, + child: const Icon(Icons.settings)) + ], + ); + } +} + +class SortSelector extends StatelessWidget { + const SortSelector({super.key}); + + @override + Widget build(BuildContext context) { + return DropSelectableWidget( + fontSize: 12, + data: sortNameMap.values.toList(), + iconSize: 20, + height: 28, + width: 200, + disableColor: const Color(0xff1F425F), + onDropSelected: (int index) async { + SortState state = SortStateScope.of(context); + state.config = state.config.copyWith(name: sortNameMap.keys.toList()[index]); + }, + ); + } +} diff --git a/modules/knowledge_system/algorithm/lib/src/navigation/menu/algo_menu.dart b/modules/knowledge_system/algorithm/lib/src/navigation/menu/algo_menu.dart new file mode 100644 index 000000000..2118f0a5c --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/navigation/menu/algo_menu.dart @@ -0,0 +1,9 @@ +import 'sort.dart'; +import 'finding.dart'; + +Map get algoMenus => { + 'children': [ + findingMenus, + sortMenus, + ] +}; diff --git a/modules/knowledge_system/algorithm/lib/src/navigation/menu/finding.dart b/modules/knowledge_system/algorithm/lib/src/navigation/menu/finding.dart new file mode 100644 index 000000000..a370c6f00 --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/navigation/menu/finding.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; + +Map get findingMenus => { + 'path': '/finding', + 'icon': Icons.pages_outlined, + 'label': '寻路算法', + 'children': [ + { + 'path': '/BFS', + 'label': '广度优先搜索', + }, + { + 'path': '/DFS', + 'label': '深度优先搜索', + }, + { + 'path': '/AStar', + 'label': 'A* 寻路算法', + }, + { + 'path': '/BestFS', + 'label': '最佳优先算法', + }, + { + 'path': '/dijkstra', + 'label': 'Dijkstra 算法', + }, + ] +}; \ No newline at end of file diff --git a/modules/knowledge_system/algorithm/lib/src/navigation/menu/sort.dart b/modules/knowledge_system/algorithm/lib/src/navigation/menu/sort.dart new file mode 100644 index 000000000..6af6c48f5 --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/navigation/menu/sort.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; + +Map get sortMenus => { + 'path': '/sort', + 'icon': Icons.sort, + 'label': '排序算法', + 'children': [ + { + 'path': '/insertion', + 'label': '插入排序', + }, + { + 'path': '/bubble', + 'label': '冒泡排序', + }, + { + 'path': '/cocktail', + 'label': '鸡尾酒排序', + }, + { + 'path': '/comb', + 'label': '梳排序', + }, + { + 'path': '/pigeonHole', + 'label': '鸽巢排序', + }, + { + 'path': '/shell', + 'label': '希尔排序', + }, + { + 'path': '/selection', + 'label': '选择排序', + }, + { + 'path': '/gnome', + 'label': '侏儒排序', + }, + { + 'path': '/cycle', + 'label': '循环排序', + }, + { + 'path': '/heap', + 'label': '堆排序', + }, + { + 'path': '/quick', + 'label': '快速排序', + }, + { + 'path': '/merge', + 'label': '归并排序', + }, + ] +}; \ No newline at end of file diff --git a/modules/knowledge_system/algorithm/lib/src/navigation/router/router.dart b/modules/knowledge_system/algorithm/lib/src/navigation/router/router.dart new file mode 100644 index 000000000..32c0ea912 --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/navigation/router/router.dart @@ -0,0 +1,40 @@ +// Copyright 2014 The 张风捷特烈 . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Author: 张风捷特烈 +// CreateTime: 2024-07-07 +// Contact Me: 1981462002@qq.com + +import 'package:algorithm/algorithm.dart'; +import 'package:algorithm/src/finding/view/finding_page.dart'; +import 'package:flutter/material.dart'; +import 'package:fx_go_router_ext/fx_go_router_ext.dart'; + +import '../../sort/sort_page.dart'; +import '../view/algo_desk_navigation.dart'; + +RouteBase get algoRoutes => GoRoute( + path: '/', + redirect: (_, __) => null, + routes: [ + ShellRoute( + builder: (BuildContext context, GoRouterState state, Widget child) { + return AppDeskNavigation(content: child); + }, + routes: [ + GoRoute( + path: 'sort/:name', + builder: (BuildContext context, GoRouterState state) { + return const DeskSortPage(); + }, + ), + GoRoute( + path: 'finding/:name', + builder: (BuildContext context, GoRouterState state) { + return const FindingPage(); + }, + ), + ]) + ], +); diff --git a/modules/knowledge_system/algorithm/lib/src/navigation/view/algo_desk_navigation.dart b/modules/knowledge_system/algorithm/lib/src/navigation/view/algo_desk_navigation.dart new file mode 100644 index 000000000..d721ec792 --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/navigation/view/algo_desk_navigation.dart @@ -0,0 +1,26 @@ + +import 'package:flutter/material.dart'; +import 'package:tolyui/tolyui.dart'; + +import 'algo_menu_tree.dart'; + +class AppDeskNavigation extends StatelessWidget { + final Widget content; + + const AppDeskNavigation({super.key, required this.content}); + + @override + Widget build(BuildContext context) { + Color backgroundColor = context.isDark ? Color(0xff001529) : Colors.white; + + return Scaffold( + backgroundColor: backgroundColor, + body: Row( + children: [ + const AppMenuTree(), + Expanded(child: content), + ], + ), + ); + } +} diff --git a/modules/knowledge_system/algorithm/lib/src/navigation/view/algo_menu_cell.dart b/modules/knowledge_system/algorithm/lib/src/navigation/view/algo_menu_cell.dart new file mode 100644 index 000000000..c3b297b7b --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/navigation/view/algo_menu_cell.dart @@ -0,0 +1,187 @@ +// Copyright 2014 The 张风捷特烈 . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Author: 张风捷特烈 +// CreateTime: 2024-05-23 +// Contact Me: 1981462002@qq.com + +import 'dart:math'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:tolyui/tolyui.dart'; + +class AlgoMenuMetaExt extends Extra { + final String? subtitle; + final String? tag; + final bool? isFlutter; + + const AlgoMenuMetaExt({ + required this.subtitle, + required this.tag, + required this.isFlutter, + }); + + factory AlgoMenuMetaExt.fromMap(Map map) { + return AlgoMenuMetaExt( + subtitle: map['subtitle'], + tag: map['tag'], + isFlutter: map['isFlutter'], + ); + } +} + +class AlgoMenuCell extends StatelessWidget { + final MenuNode menuNode; + final DisplayMeta display; + final MenuTreeCellStyle? style; + + const AlgoMenuCell({ + super.key, + required this.menuNode, + required this.display, + this.style, + }); + + MenuTreeCellStyle get effectStyle => + style ?? + (display.isDark ? MenuTreeCellStyle.dark() : MenuTreeCellStyle.light()); + + Color? effectForegroundColor(MenuTreeCellStyle style) { + if (display.selected) { + return display.isDark ? Colors.white : style.activeForegroundColor; + } + if (display.hovered) { + return display.isDark ? Colors.white : style.hoverForegroundColor; + } + return style.inactiveForegroundColor; + } + + double get anim => display.anima ?? 1; + + Color? backgroundColor(MenuTreeCellStyle style) { + if (hasChild) return null; + if (selectOrPlaying) { + return style.activeBackgroundColor.withOpacity(anim); + } + if (display.hovered) { + return style.hoverBackgroundColor; + } + return null; + } + + bool get selectOrPlaying => (display.selected || display.playing); + + bool get hasChild => menuNode.children.isNotEmpty; + + @override + Widget build(BuildContext context) { + MenuTreeCellStyle effectStyle = style ?? + (display.isDark ? MenuTreeCellStyle.dark() : MenuTreeCellStyle.light()); + + Color? bgColor = backgroundColor(effectStyle); + Color? fgColor = effectForegroundColor(effectStyle); + EdgeInsets padding = const EdgeInsets.symmetric(horizontal: 8, vertical: 2); + + IconData? icon; + if (menuNode.data is IconMenu) { + icon = (menuNode.data as IconMenu).icon; + } + Widget cell = DecoratedBox( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(6), + color: bgColor, + ), + child: Row( + children: [ + Expanded( + child: Container( + alignment: Alignment.centerLeft, + padding: EdgeInsets.only( + left: 12.0 + (28 * menuNode.depth), + top: ext?.subtitle == null ? 8 : 8, + bottom: ext?.subtitle == null ? 8 : 8, + ), + child: Wrap( + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + if (icon != null) + Padding( + padding: const EdgeInsets.only(right: 8.0), + child: Icon(icon, size: 20, color: fgColor), + ), + _buildTitle(fgColor) + ], + ), + ), + ), + if (ext?.tag != null) _buildTag(ext), + if (menuNode.children.isNotEmpty) + _buildExpandIndicator(display.expanded, fgColor) + ], + ), + ); + if (selectOrPlaying && effectStyle.showIndicator && !hasChild) { + cell = Stack( + alignment: Alignment.centerLeft, + children: [ + cell, + LineIndicator(progress: anim, color: fgColor), + ], + ); + } + return Padding(padding: padding, child: cell); + } + + AlgoMenuMetaExt? get ext => menuNode.data.ext?.me(); + + Widget _buildTitle(Color? fgColor) { + TextStyle subStyle = const TextStyle(fontSize: 12, color: Colors.grey); + TextStyle titleStyle = TextStyle(color: fgColor); + Widget child = Text( + menuNode.data.label, + overflow: TextOverflow.ellipsis, + maxLines: 1, + style: titleStyle, + ); + if (ext?.isFlutter ?? false) { + child = Wrap( + spacing: 4, + crossAxisAlignment: WrapCrossAlignment.center, + children: [child, const FlutterLogo(size: 14)], + ); + } + if (ext?.subtitle != null) { + child = Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [child, Text(ext!.subtitle!, style: subStyle)], + ); + } + return child; + } + + Widget _buildTag(AlgoMenuMetaExt? ext) { + TextStyle tagStyle = + const TextStyle(color: Colors.white, height: 1, fontSize: 10); + Widget child = Text('${ext?.tag}', + overflow: TextOverflow.ellipsis, maxLines: 1, style: tagStyle); + return Padding( + padding: const EdgeInsets.only(right: 8.0), + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 4), + decoration: BoxDecoration( + color: Colors.blue.withOpacity(0.8), + borderRadius: BorderRadius.circular(4)), + child: child), + ); + } + + Widget _buildExpandIndicator(bool expanded, Color? color) { + return Padding( + padding: const EdgeInsets.only(right: 8.0), + child: Transform.rotate( + angle: display.rate * pi, + child: Icon(CupertinoIcons.chevron_down, size: 16, color: color))); + } +} diff --git a/modules/knowledge_system/algorithm/lib/src/navigation/view/algo_menu_tree.dart b/modules/knowledge_system/algorithm/lib/src/navigation/view/algo_menu_tree.dart new file mode 100644 index 000000000..83be03b60 --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/navigation/view/algo_menu_tree.dart @@ -0,0 +1,96 @@ +import 'package:algorithm/algorithm.dart'; +import 'package:flutter/material.dart'; +import 'package:fx_go_router_ext/fx_go_router_ext.dart'; +import 'package:tolyui/tolyui.dart'; + +import '../../finding/data_scope/finding_state.dart'; +import '../menu/algo_menu.dart'; +import 'package:path/path.dart' as p ; + +import 'algo_menu_cell.dart'; + +class AppMenuTree extends StatefulWidget { + const AppMenuTree({super.key}); + + @override + State createState() => _AppMenuTreeState(); +} + +class _AppMenuTreeState extends State with RouterChangeListenerMixin { + late MenuTreeMeta _menuMeta; + + @override + void initState() { + super.initState(); + _initTreeMeta(); + } + + void _initTreeMeta() { + MenuNode root = MenuNode.fromMap(algoMenus); + List parts = Uri.parse(path).pathSegments; + String parentPath = parts.sublist(0, parts.length - 1).join('/'); + _menuMeta = MenuTreeMeta( + expandMenus: ['/$parentPath'], + activeMenu: root.find(path), + root: root, + ); + } + + @override + Widget build(BuildContext context) { + Color expandBackgroundColor = context.isDark ? Colors.black : Colors.transparent; + Color backgroundColor = context.isDark ? Color(0xff001529) : Colors.white; + + return TolyRailMenuTree( + enableWidthChange: true, + maxWidth: 360, + width: 190, + meta: _menuMeta, + builder: (node, display) => AlgoMenuCell( + menuNode: node, + display: display, + ), + backgroundColor: backgroundColor, + expandBackgroundColor: expandBackgroundColor, + onSelect: _onSelect, + ); + } + + + void _onSelect(MenuNode menu) { + if (menu.isLeaf) { + FindingState state = FindingStateScope.read(context); + if(state.status==FindingStatus.finding){ + $message.warning(message: '正在寻路中,请稍后'); + return; + } + context.go(menu.id); + } else { + _menuMeta = _menuMeta.select(menu, singleExpand: true); + setState(() {}); + } + } + + @override + void reassemble() { + MenuNode root = MenuNode.fromMap(algoMenus); + _menuMeta = _menuMeta.copyWith(root: root); + super.reassemble(); + } + + @override + void onChangeRoute(String path) { + if(path.startsWith('/sort')){ + SortState state = SortStateScope.of(context); + state.config = state.config.copyWith(name: p.basename(path)); + print("=====path:${p.basename(path)}==${state.config.name}======"); + + }else{ + FindingState state = FindingStateScope.read(context); + state.changeAlgo(p.basename(path)); + } + + _menuMeta = _menuMeta.selectPath(path, singleExpand: true); + setState(() {}); + } +} diff --git a/packages/algorithm/lib/src/views/data_painter.dart b/modules/knowledge_system/algorithm/lib/src/sort/data_painter.dart similarity index 100% rename from packages/algorithm/lib/src/views/data_painter.dart rename to modules/knowledge_system/algorithm/lib/src/sort/data_painter.dart diff --git a/packages/algorithm/lib/src/views/sort_page.dart b/modules/knowledge_system/algorithm/lib/src/sort/sort_page.dart similarity index 82% rename from packages/algorithm/lib/src/views/sort_page.dart rename to modules/knowledge_system/algorithm/lib/src/sort/sort_page.dart index efb0f074c..99a65dfbd 100644 --- a/packages/algorithm/lib/src/views/sort_page.dart +++ b/modules/knowledge_system/algorithm/lib/src/sort/sort_page.dart @@ -1,6 +1,9 @@ -import 'package:algorithm/algorithm.dart'; import 'package:flutter/material.dart'; +import 'sort_parper.dart'; +import 'sort_setting.dart'; +import 'top_bar/sort_bar.dart'; + class DeskSortPage extends StatelessWidget{ const DeskSortPage({super.key}); diff --git a/packages/algorithm/lib/src/views/sort_parper.dart b/modules/knowledge_system/algorithm/lib/src/sort/sort_parper.dart similarity index 92% rename from packages/algorithm/lib/src/views/sort_parper.dart rename to modules/knowledge_system/algorithm/lib/src/sort/sort_parper.dart index bdb752aab..f1cd8cac5 100644 --- a/packages/algorithm/lib/src/views/sort_parper.dart +++ b/modules/knowledge_system/algorithm/lib/src/sort/sort_parper.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; -import '../data_scope/state.dart'; +import '../../algorithm.dart'; import 'data_painter.dart'; class SortPaper extends StatelessWidget{ diff --git a/packages/algorithm/lib/src/views/sort_setting.dart b/modules/knowledge_system/algorithm/lib/src/sort/sort_setting.dart similarity index 98% rename from packages/algorithm/lib/src/views/sort_setting.dart rename to modules/knowledge_system/algorithm/lib/src/sort/sort_setting.dart index 71311b8b4..44a27351e 100644 --- a/packages/algorithm/lib/src/views/sort_setting.dart +++ b/modules/knowledge_system/algorithm/lib/src/sort/sort_setting.dart @@ -1,6 +1,7 @@ +import 'package:algorithm/algorithm.dart'; import 'package:flutter/material.dart'; -import '../../algorithm.dart'; + class SortSettings extends StatefulWidget { const SortSettings({super.key,}); diff --git a/modules/knowledge_system/algorithm/lib/src/sort/top_bar/sort_bar.dart b/modules/knowledge_system/algorithm/lib/src/sort/top_bar/sort_bar.dart new file mode 100644 index 000000000..251b90f2a --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/sort/top_bar/sort_bar.dart @@ -0,0 +1,89 @@ +import 'package:algorithm/algorithm.dart'; +import 'package:algorithm/src/algorithm/sort/functions.dart'; +import 'package:app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:toly_ui/toly_ui.dart'; +import 'package:tolyui/basic/basic.dart'; + +import 'sort_button.dart'; + +class DeskSortBar extends StatelessWidget { + const DeskSortBar({super.key}); + + @override + Widget build(BuildContext context) { + bool isDark = Theme.of(context).brightness == Brightness.dark; + String name = SortStateScope.of(context).config.name; + + return DragToMoveWrapper( + child: Container( + padding: EdgeInsets.symmetric(horizontal: 10), + height: 42, + color: isDark ? const Color(0xff2C3036) : Colors.white, + child: Row( + children: [ + const SortButton(), + Spacer(), + TolyLink( + href: + 'https://github.com/toly1994328/FlutterUnit/blob/master/packages/algorithm/lib/src/algorithm/sort/functions/${name}.dart', + text: '查看排序源码', + hoverColor: Colors.blue, + style: TextStyle(fontSize: 12, fontFamily: '宋体'), + onTap: jumpURL), + const SizedBox(width: 10), + TolyAction( + tooltip: '设置', + onTap: Scaffold.of(context).openEndDrawer, + child: const Icon(Icons.settings, size: 20)), + ], + ), + ), + ); + } +} + +class SortBar extends StatelessWidget { + const SortBar({super.key}); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + const SortButton(), + const SizedBox( + width: 10, + ), + const SortSelector(), + const SizedBox( + width: 10, + ), + GestureDetector( + onTap: () { + Scaffold.of(context).openEndDrawer(); + }, + child: const Icon(Icons.settings)) + ], + ); + } +} + +class SortSelector extends StatelessWidget { + const SortSelector({super.key}); + + @override + Widget build(BuildContext context) { + return DropSelectableWidget( + fontSize: 12, + data: sortNameMap.values.toList(), + iconSize: 20, + height: 28, + width: 200, + disableColor: const Color(0xff1F425F), + onDropSelected: (int index) async { + SortState state = SortStateScope.of(context); + state.config = state.config.copyWith(name: sortNameMap.keys.toList()[index]); + }, + ); + } +} diff --git a/packages/algorithm/lib/src/views/top_bar/sort_button.dart b/modules/knowledge_system/algorithm/lib/src/sort/top_bar/sort_button.dart similarity index 75% rename from packages/algorithm/lib/src/views/top_bar/sort_button.dart rename to modules/knowledge_system/algorithm/lib/src/sort/top_bar/sort_button.dart index cce2959b2..b7103001c 100644 --- a/packages/algorithm/lib/src/views/top_bar/sort_button.dart +++ b/modules/knowledge_system/algorithm/lib/src/sort/top_bar/sort_button.dart @@ -1,9 +1,7 @@ -import 'package:algorithm/algorithm.dart'; import 'package:flutter/material.dart'; +import 'package:tolyui/tolyui.dart'; -import '../../data_scope/state.dart'; - - +import '../../../algorithm.dart'; class SortButton extends StatelessWidget { const SortButton({super.key}); @@ -13,31 +11,32 @@ class SortButton extends StatelessWidget { SortState state = SortStateScope.of(context); VoidCallback? action; IconData icon; + String? tip; Color color; switch (state.status) { case SortStatus.none: icon = Icons.not_started_outlined; color = Colors.green; action = state.sort; + tip = '运行'; break; case SortStatus.sorting: icon = Icons.stop_circle_outlined; color = Colors.grey; + tip = '寻路中'; action = null; break; case SortStatus.sorted: icon = Icons.refresh; color = Colors.black; action = state.reset; + tip = '重置'; break; } - - return GestureDetector( + return TolyAction( onTap: action, - child: Icon( - icon, - color: color, - ), + tooltip: tip, + child: Icon(icon, color: color, size: 20), ); } } diff --git a/modules/knowledge_system/algorithm/lib/src/views/algo_page.dart b/modules/knowledge_system/algorithm/lib/src/views/algo_page.dart new file mode 100644 index 000000000..92680610d --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/views/algo_page.dart @@ -0,0 +1,67 @@ +import 'package:algorithm/src/finding/data_scope/finding_state.dart'; +import 'package:algorithm/src/navigation/router/router.dart'; +import 'package:flutter/material.dart'; +import 'package:fx_go_router_ext/fx_go_router_ext.dart'; + +import '../../algorithm.dart'; + +class AlgoScope extends StatelessWidget { + final Widget child; + + const AlgoScope({super.key, required this.child}); + + @override + Widget build(BuildContext context) { + print("=====build======="); + return SortStateScope( + notifier: SortState(), + child: FindingStateScope( + notifier: FindingState(), + child: child, + ), + ); + } +} + +class AlgoRouterPage extends StatefulWidget { + const AlgoRouterPage({super.key}); + + @override + State createState() => _AlgoRouterPageState(); +} + +class _AlgoRouterPageState extends State { + final GoRouter _router = GoRouter( + initialLocation: '/finding/BFS', + routes: [algoRoutes], + onException: (BuildContext ctx, GoRouterState state, GoRouter router) { + router.go('/404', extra: state.uri.toString()); + }, + ); + + // late final DisplayLogic logic; + + @override + void initState() { + // logic = DisplayLogic(DisplayState( + // router: '/base/size', + // activeIndex: 0, + // total: kDisplayMap['/base/size']!.length, + // )); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return AlgoScope( + child: Column( + children: [ + const Divider(), + Expanded( + child: Router.withConfig(config: _router), + ), + ], + ), + ); + } +} diff --git a/modules/knowledge_system/algorithm/lib/src/views/desktop/desk_algo_panel.dart b/modules/knowledge_system/algorithm/lib/src/views/desktop/desk_algo_panel.dart new file mode 100644 index 000000000..61a8f6ae2 --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/views/desktop/desk_algo_panel.dart @@ -0,0 +1,62 @@ +// Copyright 2014 The 张风捷特烈 . All rights reserved. + +// Author: 张风捷特烈 +// CreateTime: 2024-07-07 +// Contact Me: 1981462002@qq.com + +import 'package:flutter/material.dart'; + +// class LayoutRouterPage extends StatefulWidget { +// LayoutRouterPage({super.key}); +// +// @override +// State createState() => _LayoutRouterPageState(); +// } +// +// class _LayoutRouterPageState extends State { +// final GoRouter _router = GoRouter( +// initialLocation: '/base/size', +// routes: [layoutRoutes], +// onException: (BuildContext ctx, GoRouterState state, GoRouter router) { +// router.go('/404', extra: state.uri.toString()); +// }, +// ); +// +// late final DisplayLogic logic; +// +// @override +// void initState() { +// logic = DisplayLogic(DisplayState( +// router: '/base/size', +// activeIndex: 0, +// total: kDisplayMap['/base/size']!.length, +// )); +// super.initState(); +// } +// +// @override +// Widget build(BuildContext context) { +// return DisplayScope( +// notifier: logic, +// child: Column( +// children: [ +// const Divider(), +// Expanded( +// child: Router.withConfig(config: _router), +// ), +// ], +// ), +// ); +// } +// } + +class DeskAlgoPanel extends StatelessWidget { + const DeskAlgoPanel({super.key}); + + @override + Widget build(BuildContext context) { + return Row( + + ); + } +} diff --git a/modules/knowledge_system/algorithm/pubspec.yaml b/modules/knowledge_system/algorithm/pubspec.yaml new file mode 100644 index 000000000..51cf4b611 --- /dev/null +++ b/modules/knowledge_system/algorithm/pubspec.yaml @@ -0,0 +1,54 @@ +name: algorithm +description: algorithm +version: 0.0.1 +homepage: +publish_to: none + +environment: + sdk: ">=3.5.0 <4.0.0" + flutter: ">=1.17.0" +resolution: workspace +dependencies: + flutter: + sdk: flutter + + + + + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # To add assets to your package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # To add custom fonts to your package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/artifact/test/utils_test.dart b/modules/knowledge_system/algorithm/test/utils_test.dart similarity index 100% rename from packages/artifact/test/utils_test.dart rename to modules/knowledge_system/algorithm/test/utils_test.dart diff --git a/packages/utils/.gitignore b/modules/knowledge_system/artifact/.gitignore similarity index 100% rename from packages/utils/.gitignore rename to modules/knowledge_system/artifact/.gitignore diff --git a/packages/utils/.metadata b/modules/knowledge_system/artifact/.metadata similarity index 100% rename from packages/utils/.metadata rename to modules/knowledge_system/artifact/.metadata diff --git a/packages/widget_repository/CHANGELOG.md b/modules/knowledge_system/artifact/CHANGELOG.md similarity index 100% rename from packages/widget_repository/CHANGELOG.md rename to modules/knowledge_system/artifact/CHANGELOG.md diff --git a/packages/widget_repository/LICENSE b/modules/knowledge_system/artifact/LICENSE similarity index 100% rename from packages/widget_repository/LICENSE rename to modules/knowledge_system/artifact/LICENSE diff --git a/packages/widget_repository/README.md b/modules/knowledge_system/artifact/README.md similarity index 100% rename from packages/widget_repository/README.md rename to modules/knowledge_system/artifact/README.md diff --git a/packages/widget_repository/analysis_options.yaml b/modules/knowledge_system/artifact/analysis_options.yaml similarity index 100% rename from packages/widget_repository/analysis_options.yaml rename to modules/knowledge_system/artifact/analysis_options.yaml diff --git a/modules/knowledge_system/artifact/lib/artifact.dart b/modules/knowledge_system/artifact/lib/artifact.dart new file mode 100644 index 000000000..42e84e723 --- /dev/null +++ b/modules/knowledge_system/artifact/lib/artifact.dart @@ -0,0 +1,7 @@ +library artifact; + +export 'src/articles/view/artifact_page.dart'; + +export 'src/points/exp.dart'; +export 'src/articles/data/exp.dart'; +export 'src/articles/view/desk_artifact_page.dart'; \ No newline at end of file diff --git a/modules/knowledge_system/artifact/lib/src/articles/bloc/article/bloc.dart b/modules/knowledge_system/artifact/lib/src/articles/bloc/article/bloc.dart new file mode 100644 index 000000000..5e73e1f6c --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/articles/bloc/article/bloc.dart @@ -0,0 +1,81 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../data/exp.dart'; + +import '../../data/repository/article_repository.dart'; + +class ArticleBloc extends Cubit { + ArticleBloc( + this.repository, { + this.pageSize = 20, + this.groupId, + }) : super(ArticleState.empty()); + + final int pageSize; + final int? groupId; + final ArticleRepository repository; + + /// 初始化时,加载 [pageSize] 条记录 + /// + void init() { + _loadDataFromDb(1, pageSize, filter: groupId?.toString(), requestNet: true); + } + + Future loadNextPageMore() async { + int curPage = state.data.length ~/ pageSize; + int nextPage = curPage + 1; + ArticleFilter filter = ArticleFilter( + page: nextPage, + pageSize: pageSize, + groupId: groupId, + ); + List
result = await repository.queryByDb(filter); + int count = await repository.total(filter); + emit(ArticleWithData( + data: state.data + result, + total: count, + )); + } + + Future _loadDataFromDb( + int page, + int pageSize, { + bool requestNet = false, + String? filter, + }) async { + ArticleFilter filter = ArticleFilter( + page: page, + pageSize: pageSize, + groupId: groupId, + ); + List
data = await repository.queryByDb(filter); + + // 没有内存缓存 并且数据库有数据 + if (data.isNotEmpty) { + emit(ArticleWithData(data: data, total: data.length)); + } + } + +} + +sealed class ArticleState { + final List
data; + + const ArticleState({this.data = const []}); + + factory ArticleState.empty() => const ArticleWithData(); +} + +class ArticleLoading extends ArticleState { + const ArticleLoading({super.data}); +} + +class ArticleWithData extends ArticleState { + final int total; + const ArticleWithData({super.data, this.total = 0}); +} + +class ArticleFailed extends ArticleState { + final String error; + const ArticleFailed(this.error, {super.data}); +} diff --git a/modules/knowledge_system/artifact/lib/src/articles/bloc/columnize/bloc.dart b/modules/knowledge_system/artifact/lib/src/articles/bloc/columnize/bloc.dart new file mode 100644 index 000000000..9fda14c6c --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/articles/bloc/columnize/bloc.dart @@ -0,0 +1,39 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import '../../data/exp.dart'; +import '../../data/repository/columnize_repository.dart'; + +class ColumnizeBloc extends Cubit { + final ColumnizeRepository repository; + + ColumnizeBloc(this.repository) : super(ColumnizeState.initial()); + + void init() { + _loadDataFromDb(requestNet: true); + } + + Future _loadDataFromDb({bool requestNet = false}) async { + /// + List data = await repository.queryByDb(); + + // + if (data.isNotEmpty) { + emit(ColumnizeState(data)); + } + } +} + +class ColumnizeState { + List data; + + ColumnizeState(this.data); + + factory ColumnizeState.initial() => ColumnizeState([ + Columnize(title: '-', url: '-'), + Columnize(title: '-', url: '-'), + Columnize(title: '-', url: '-'), + Columnize(title: '-', url: '-'), + Columnize(title: '-', url: '-'), + Columnize(title: '-', url: '-'), + Columnize(title: '-', url: '-'), + ]); +} diff --git a/packages/artifact/lib/src/blocs/exp.dart b/modules/knowledge_system/artifact/lib/src/articles/bloc/exp.dart similarity index 100% rename from packages/artifact/lib/src/blocs/exp.dart rename to modules/knowledge_system/artifact/lib/src/articles/bloc/exp.dart diff --git a/modules/knowledge_system/artifact/lib/src/articles/data/dao/article_dao.dart b/modules/knowledge_system/artifact/lib/src/articles/data/dao/article_dao.dart new file mode 100644 index 000000000..51faed9f2 --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/articles/data/dao/article_dao.dart @@ -0,0 +1,89 @@ +import 'package:fx_dao/fx_dao.dart'; +import 'package:sqflite/sqflite.dart'; + +import '../model/article.dart'; + +class ArticleDao extends Dao { + @override + String get createSql => ''; + + @override + String get name => 'article'; + + Future insert(Article po) => database.insert( + name, + po.toJson(), + conflictAlgorithm: ConflictAlgorithm.replace, + ); + + Future insertOrUpdate(Article po) async { + bool canUpdate = await shouldUpdate(po.id, po.update); + return database.insert( + name, + po.toJson(), + conflictAlgorithm: + canUpdate ? ConflictAlgorithm.replace : ConflictAlgorithm.ignore, + ); + } + + /// 当前数据是否需要更新 + Future shouldUpdate(int id, int updateAt) async { + List> data = await database + .rawQuery("SELECT `update` FROM $name WHERE id = ?", [id]); + // 没有数据,可以更新 + if (data.isEmpty) { + return true; + } + // 服务器中数据更新时间,大于本地数据库内容,可以更新 + return updateAt > data.first['update']; + } + + Future> query(ArticleFilter filter) async { + String queryArgs = ''; + List args = []; + if (filter.filter != null) { + queryArgs += "AND filter = ? "; + args.add(filter.filter); + } + if (filter.groupId != null) { + if (queryArgs.isEmpty) { + queryArgs += 'WHERE groupId = ? '; + } else { + queryArgs += "AND groupId = ? "; + } + args.add(filter.groupId); + } + queryArgs += 'LIMIT ? OFFSET ?'; + args.addAll([filter.pageSize, filter.offset]); + + List> data = await database.rawQuery( + "SELECT * FROM $name $queryArgs", + args, + ); + + List
result = data.map(Article.fromDb).toList(); + return result; + } + + Future total(ArticleFilter filter) async { + bool hasGroupId = filter.groupId != null; + String familySql = hasGroupId ? 'WHERE groupId = ?' : ''; + List familyArg = hasGroupId ? [filter.groupId!] : []; + + String sql = "SELECT count(id) as `count` FROM article $familySql"; + + List> result = await database.rawQuery(sql, familyArg); + if (result.isNotEmpty) { + return result.first['count'] as int ?? 0; + } + return 0; + } + + @override + Convertor
get convertor => Article.fromDb; + + @override + Future update(String id, Article frame) async { + return 0; + } +} diff --git a/modules/knowledge_system/artifact/lib/src/articles/data/dao/columnize_dao.dart b/modules/knowledge_system/artifact/lib/src/articles/data/dao/columnize_dao.dart new file mode 100644 index 000000000..131f4a3b6 --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/articles/data/dao/columnize_dao.dart @@ -0,0 +1,62 @@ +import 'package:fx_dao/fx_dao.dart'; + +import '../model/columnize.dart'; + +class ColumnizeDao extends Dao { + @override + String get createSql => ''; + + @override + String get name => 'columnize'; + + Future insert(Columnize po) => database.insert( + name, + po.toJson(), + conflictAlgorithm: ConflictAlgorithm.replace, + ); + + Future insertOrUpdate(Columnize po) async { + bool canUpdate = await shouldUpdate(po.id, po.update); + return database.insert( + name, + po.toJson(), + conflictAlgorithm: + canUpdate ? ConflictAlgorithm.replace : ConflictAlgorithm.ignore, + ); + } + + /// 当前数据是否需要更新 + Future shouldUpdate(int id, int updateAt) async { + List> data = await database + .rawQuery("SELECT `update` FROM $name WHERE id = ?", [id]); + // 没有数据,可以更新 + if (data.isEmpty) { + return true; + } + // 服务器中数据更新时间,大于本地数据库内容,可以更新 + return updateAt > data.first['update']; + } + + Future> query({ + int page = 1, + int pageSize = 20, + String? filter, + }) async { + String queryArgs = ''; + List args = []; + if (filter != null) { + queryArgs += "AND filter = ? "; + args.add(filter); + } + queryArgs += 'LIMIT ? OFFSET ?'; + args.addAll([pageSize, (page - 1) * pageSize]); + + List> data = await database.rawQuery( + "SELECT * FROM $name $queryArgs", + args, + ); + + List result = data.map(Columnize.fromDb).toList(); + return result; + } +} diff --git a/modules/knowledge_system/artifact/lib/src/articles/data/exp.dart b/modules/knowledge_system/artifact/lib/src/articles/data/exp.dart new file mode 100644 index 000000000..4207924f2 --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/articles/data/exp.dart @@ -0,0 +1,6 @@ +export 'repository/article_repository.dart'; +export 'repository/columnize_repository.dart'; +export 'model/article.dart'; +export 'model/columnize.dart'; +export 'dao/article_dao.dart'; +export 'dao/columnize_dao.dart'; \ No newline at end of file diff --git a/modules/knowledge_system/artifact/lib/src/articles/data/model/article.dart b/modules/knowledge_system/artifact/lib/src/articles/data/model/article.dart new file mode 100644 index 000000000..b1ef595b8 --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/articles/data/model/article.dart @@ -0,0 +1,104 @@ +import 'package:fx_dao/fx_dao.dart'; + +class Article implements Po{ + final String? username; + final String title; + final String? subtitle; + final String url; + final String? cover; + final int create; + final int update; + final int id; + final int userId; + final int groupId; + + Article({ + this.username = '', + required this.title, + this.subtitle = '', + required this.url, + this.cover = '', + this.update = 0, + this.create = 0, + required this.userId, + this.id = -1, + required this.groupId, + + }); + + Map toJson() => + { + "id": id, + "userId": userId, + "groupId": groupId, + "username": username, + "title": title, + "createAt": create, + "subtitle": subtitle, + "url": url, + "cover": cover, + "updateAt": update, + }; + + factory Article.fromMap(dynamic map)=> + Article( + id: map['articleId'] ?? '', + username: map['userName'] ?? '', + userId: map['userId'] ?? '', + title: map['title'] ?? '', + create: DateTime.parse(map['createAt']).millisecondsSinceEpoch, + update: DateTime.parse(map['updateAt']).millisecondsSinceEpoch, + subtitle: map['subtitle'] ?? '', + url: map['url'] ?? '', + groupId: map['groupId'] ?? 1, + cover: map['caver'] ?? '', + ); + + factory Article.fromDb(dynamic map)=> + Article( + id: map['id'] ?? '', + username: map['username'] ?? '', + userId: map['userId'] ?? '', + title: map['title'] ?? '', + create: map['createAt'] ?? 0 , + update: map['updateAt'] ?? 0, + subtitle: map['subtitle'] ?? '', + url: map['url'] ?? '', + groupId: map['groupId'] ?? 1, + cover: map['cover'] ?? '', + ); +} + +class ArticleFilter{ + final String? filter; + final int? groupId; + final int page; + final int pageSize; + + const ArticleFilter({ + this.filter, + this.groupId, + this.page = 1, + this.pageSize = 20, + }); + + int get offset =>pageSize*(page-1); + + ArticleFilter copyWith({ + String? filter, + int? groupId, + int? page, + }) { + return ArticleFilter( + filter: filter ?? this.filter, + groupId: groupId ?? this.groupId, + page: page ?? this.page, + pageSize: pageSize + ); + } + + @override + String toString() { + return 'ArticleFilter{filter: $filter, groupId: $groupId, page: $page, pageSize: $pageSize}'; + } +} \ No newline at end of file diff --git a/packages/artifact/lib/src/repositories/model/columnize.dart b/modules/knowledge_system/artifact/lib/src/articles/data/model/columnize.dart similarity index 98% rename from packages/artifact/lib/src/repositories/model/columnize.dart rename to modules/knowledge_system/artifact/lib/src/articles/data/model/columnize.dart index 564593ca6..63c4debb7 100644 --- a/packages/artifact/lib/src/repositories/model/columnize.dart +++ b/modules/knowledge_system/artifact/lib/src/articles/data/model/columnize.dart @@ -1,4 +1,3 @@ -import 'dart:convert'; class Columnize { final String? username; diff --git a/modules/knowledge_system/artifact/lib/src/articles/data/repository/article_repository.dart b/modules/knowledge_system/artifact/lib/src/articles/data/repository/article_repository.dart new file mode 100644 index 000000000..8f27f1f85 --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/articles/data/repository/article_repository.dart @@ -0,0 +1,19 @@ +import 'package:storage/storage.dart'; + +import '../dao/article_dao.dart'; +import '../exp.dart'; + +// 仓储: 提供数据 +class ArticleRepository { + const ArticleRepository(); + + ArticleDao get dao => AppStorage().article(); + + // 从数据库加载资源 + Future> queryByDb(ArticleFilter filter) async { + List
caches = await dao.query(filter); + return caches; + } + + Future total(ArticleFilter filter) => dao.total(filter); +} diff --git a/modules/knowledge_system/artifact/lib/src/articles/data/repository/columnize_repository.dart b/modules/knowledge_system/artifact/lib/src/articles/data/repository/columnize_repository.dart new file mode 100644 index 000000000..7b2b53c5d --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/articles/data/repository/columnize_repository.dart @@ -0,0 +1,22 @@ +import 'package:storage/storage.dart'; + +import '../exp.dart'; + +// 仓储: 提供数据 +class ColumnizeRepository { + const ColumnizeRepository(); + + ColumnizeDao get dao => AppStorage().article(); + + // 从数据库加载资源 + Future> queryByDb({ + int page = 1, + int pageSize = 20, + }) async { + List caches = await dao.query( + page: page, + pageSize: pageSize, + ); + return caches; + } +} diff --git a/packages/artifact/lib/src/views/article/article_detail_page.dart b/modules/knowledge_system/artifact/lib/src/articles/view/article/article_detail_page.dart similarity index 97% rename from packages/artifact/lib/src/views/article/article_detail_page.dart rename to modules/knowledge_system/artifact/lib/src/articles/view/article/article_detail_page.dart index 9e701d3ed..367184573 100644 --- a/packages/artifact/lib/src/views/article/article_detail_page.dart +++ b/modules/knowledge_system/artifact/lib/src/articles/view/article/article_detail_page.dart @@ -5,7 +5,7 @@ import 'package:url_launcher/url_launcher.dart'; import 'package:webview_flutter/webview_flutter.dart'; -import '../../repositories/model/article.dart'; +import '../../data/exp.dart'; class ArticleDetailPage extends StatefulWidget { final Article article; diff --git a/packages/artifact/lib/src/views/article/column_detail_page.dart b/modules/knowledge_system/artifact/lib/src/articles/view/article/column_detail_page.dart similarity index 94% rename from packages/artifact/lib/src/views/article/column_detail_page.dart rename to modules/knowledge_system/artifact/lib/src/articles/view/article/column_detail_page.dart index 74f6510c8..cbbc67f9c 100644 --- a/packages/artifact/lib/src/views/article/column_detail_page.dart +++ b/modules/knowledge_system/artifact/lib/src/articles/view/article/column_detail_page.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; -import '../../repositories/exp.dart'; + +import '../../data/exp.dart'; import 'sliver_article.dart'; class ColumnDetailPage extends StatelessWidget { diff --git a/packages/artifact/lib/src/views/article/columnize_page_view.dart b/modules/knowledge_system/artifact/lib/src/articles/view/article/columnize_page_view.dart similarity index 80% rename from packages/artifact/lib/src/views/article/columnize_page_view.dart rename to modules/knowledge_system/artifact/lib/src/articles/view/article/columnize_page_view.dart index e68479ea6..d3f1f4ff5 100644 --- a/packages/artifact/lib/src/views/article/columnize_page_view.dart +++ b/modules/knowledge_system/artifact/lib/src/articles/view/article/columnize_page_view.dart @@ -1,17 +1,14 @@ -import 'dart:convert'; import 'dart:math'; import 'package:app/app.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; - import 'package:intl/intl.dart'; - -import '../../blocs/article/bloc.dart'; -import '../../blocs/exp.dart'; -import '../../repositories/model/columnize.dart'; -import '../../repositories/repository/article_repository.dart'; +import '../../bloc/article/bloc.dart'; +import '../../bloc/exp.dart'; +import '../../data/exp.dart'; +import '../../data/repository/article_repository.dart'; import 'column_detail_page.dart'; @@ -50,7 +47,7 @@ class _ColumnizePageViewState extends State { double value = ((_position - _firstOffset + 1) % 5) / 5; factor.value = value == 0 ? 1 : value; _ctrl = PageController( - viewportFraction: 0.9, + viewportFraction: kAppEnv.isDesktopUI ? 0.5 : 0.9, initialPage: _position, )..addListener(() { if (_ctrl.page != null) { @@ -81,8 +78,7 @@ class _ColumnizePageViewState extends State { @override Widget build(BuildContext context) { List data = context.watch().state.data; - - return PageView.builder( + Widget child = PageView.builder( controller: _ctrl, // itemCount: 7, itemBuilder: (_, index) { return AnimatedBuilder( @@ -99,6 +95,42 @@ class _ColumnizePageViewState extends State { _position = index; }, ); + if (!kIsDesk) { + return child; + } + + return MouseRegion( + onEnter: _onEnter, + onExit: _onExit, + child: Stack( + alignment: Alignment.center, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 48.0), + child: child), + Positioned( + right: 0, + child: IconButton( + onPressed: () { + _position += 1; + _ctrl.animateToPage(_position, + duration: Duration(milliseconds: 500), + curve: Curves.easeIn); + }, + icon: Icon(Icons.navigate_next_outlined))), + Positioned( + left: 0, + child: IconButton( + onPressed: () { + _position -= 1; + _ctrl.animateToPage(_position, + duration: Duration(milliseconds: 500), + curve: Curves.easeIn); + }, + icon: Icon(Icons.navigate_before))), + ], + ), + ); } Widget? _buildByIndex(BuildContext context, int index, List data) { @@ -134,6 +166,19 @@ class _ColumnizePageViewState extends State { int result = offset % length; return result < 0 ? length + result : result; } + + bool _hover = false; + void _onEnter(PointerEnterEvent event) { + setState(() { + _hover = true; + }); + } + + void _onExit(PointerExitEvent event) { + setState(() { + _hover = false; + }); + } } class ColumnizeItem extends StatelessWidget { @@ -151,7 +196,8 @@ class ColumnizeItem extends StatelessWidget { child: MultiBlocProvider(providers: [ BlocProvider( create: (_) => - ArticleBloc(repository, groupId: columnize.id,pageSize: 100)..init(), + ArticleBloc(repository, groupId: columnize.id, pageSize: 100) + ..init(), ), ], child: ColumnDetailPage(columnize: columnize)))); }, diff --git a/modules/knowledge_system/artifact/lib/src/articles/view/article/sliver_article.dart b/modules/knowledge_system/artifact/lib/src/articles/view/article/sliver_article.dart new file mode 100644 index 000000000..2ec98f1c6 --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/articles/view/article/sliver_article.dart @@ -0,0 +1,186 @@ +import 'dart:io'; + +import 'package:app/app.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:url_launcher/url_launcher.dart'; + +import '../../bloc/exp.dart'; + +import '../../data/exp.dart'; +import 'article_detail_page.dart'; +import 'columnize_page_view.dart'; + +class SliverArticlePanel extends StatelessWidget { + const SliverArticlePanel({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + ArticleState state = context.watch().state; + return switch (state) { + ArticleLoading() => const SliverToBoxAdapter( + child: CupertinoActivityIndicator(), + ), + ArticleWithData() => SliverArticle( + data: state.data, + ), + ArticleFailed() => const SliverToBoxAdapter( + child: Text('error'), + ), + }; + } +} + +class SliverArticle extends StatelessWidget { + final List
data; + + const SliverArticle({Key? key, required this.data}) : super(key: key); + + @override + Widget build(BuildContext context) { + const SliverGridDelegate deskGridDelegate = + SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: 420, + mainAxisSpacing: 8, + mainAxisExtent: 188, + crossAxisSpacing: 8, + ); + + Widget child = kIsDesk + ? SliverGrid( + delegate: SliverChildBuilderDelegate( + _buildItem, + childCount: data.length, + ), + gridDelegate: deskGridDelegate) + : SliverList( + delegate: SliverChildBuilderDelegate( + _buildItem, + childCount: data.length, + )); + + return SliverPadding( + padding: const EdgeInsets.only(bottom: 0), sliver: child); + } + + Widget? _buildItem(BuildContext context, int index) { + return ArticlePanel(article: data[index]); + } +} + +class ArticlePanel extends StatelessWidget { + final Article article; + + const ArticlePanel({Key? key, required this.article}) : super(key: key); + + void toArticleDetail(BuildContext context) { + if (Platform.isAndroid || Platform.isIOS) { + Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => ArticleDetailPage(article: article), + ), + ); + } else { + _launchURL('https://juejin.cn${article.url}'); + } + } + + void _launchURL(String url) async { + print(url); + Uri uri = Uri.parse(url); + if (await canLaunchUrl(Uri.parse(url))) { + await launchUrl(uri, mode: LaunchMode.externalApplication); + } else { + debugPrint('Could not launch $url'); + } + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () => toArticleDetail(context), + child: Container( + color: Theme.of(context).listTileTheme.tileColor, + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + margin: const EdgeInsets.only(bottom: 6), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const CircleAvatar( + backgroundImage: AssetImage( + 'assets/images/icon_head.webp', + ), + backgroundColor: Colors.transparent, + radius: 10, + ), + const SizedBox( + width: 6, + ), + Expanded( + child: Text('${article.username}', + style: const TextStyle( + color: Color(0xff6A6D76), fontSize: 12))), + const Text( + '掘金', + style: TextStyle(fontSize: 12, color: Color(0xff6A6D76)), + ), + ], + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 4.0), + child: Text( + article.title, + maxLines: 2, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + ), + Row( + children: [ + Expanded( + child: Text( + '${article.subtitle}', + maxLines: 4, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + fontSize: 12, + color: Color( + 0xffA3A3A3, + )), + )), + if (article.cover != null && article.cover!.isNotEmpty) + Padding( + padding: const EdgeInsets.only(left: 8.0), + child: ClipRRect( + borderRadius: BorderRadius.circular(8), + child: Image.network( + article.cover!, + width: 110, + )), + ) + ], + ), + const SizedBox( + height: 4, + ), + Row( + children: [ + const Spacer(), + Text( + '更新时间: ${formatLong.format(DateTime.fromMillisecondsSinceEpoch(article.update, isUtc: true))}', + style: const TextStyle( + color: Color( + 0xff6A6D76, + ), + fontSize: 12), + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/modules/knowledge_system/artifact/lib/src/articles/view/article/sliver_columnize.dart b/modules/knowledge_system/artifact/lib/src/articles/view/article/sliver_columnize.dart new file mode 100644 index 000000000..8a4fc3700 --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/articles/view/article/sliver_columnize.dart @@ -0,0 +1,78 @@ +import 'package:flutter/material.dart'; +import 'package:url_launcher/url_launcher.dart'; + +import 'columnize_page_view.dart'; +import 'package:l10n/l10n.dart'; +class ColumnizeViewPage extends StatefulWidget { + const ColumnizeViewPage({Key? key}) : super(key: key); + + @override + State createState() => _ColumnizeViewPageState(); +} + +class _ColumnizeViewPageState extends State { + + late PageController _ctrl; + + @override + void initState() { + super.initState(); + _ctrl = PageController(viewportFraction: 0.9); + } + + @override + Widget build(BuildContext context) { + bool isDark = Theme.of(context).brightness == Brightness.dark; + + return Container( + height: 220, + color: Theme.of(context).listTileTheme.tileColor, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsets.only(left: 16.0,right: 16,top: 12,bottom: 4), + child: Row( + children: [ + CircleAvatar( + backgroundImage: AssetImage('assets/images/icon_head.webp',), + backgroundColor: Colors.transparent, + radius: 10, + ), + SizedBox(width: 6,), + Text("捷特文章专栏",style: TextStyle(fontSize: 16,fontWeight: FontWeight.bold),), + Spacer(), + GestureDetector( + onTap: () async{ + Uri uri = Uri.parse('https://juejin.im/user/5b42c0656fb9a04fe727eb37'); + if (await canLaunchUrl(uri)) { + await launchUrl(uri,mode:LaunchMode.externalNonBrowserApplication ); + } else { + debugPrint('Could not launch ${uri.path}'); + } + }, + child: Wrap( + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + Text( + context.l10n.knowledgeToJuejin + + ,style: TextStyle(fontSize: 12,color: Colors.blue),), + Icon(Icons.navigate_next,size: 12,color: Colors.blue,) + ], + ), + ), + + ], + ), + ), + Expanded( + child: ColumnizePageView(), + ), + SizedBox(height: 10,) + ], + ), + ); + } + +} diff --git a/packages/artifact/lib/src/views/article/toly_article_scroll_page.dart b/modules/knowledge_system/artifact/lib/src/articles/view/article/toly_article_scroll_page.dart similarity index 88% rename from packages/artifact/lib/src/views/article/toly_article_scroll_page.dart rename to modules/knowledge_system/artifact/lib/src/articles/view/article/toly_article_scroll_page.dart index b39cf4c57..81815e97a 100644 --- a/packages/artifact/lib/src/views/article/toly_article_scroll_page.dart +++ b/modules/knowledge_system/artifact/lib/src/articles/view/article/toly_article_scroll_page.dart @@ -1,13 +1,13 @@ - +import 'package:artifact/artifact.dart'; +import 'package:components/components.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:refresh/refresh.dart'; +import 'package:tolyui_refresh/tolyui_refresh.dart'; + +import '../../bloc/exp.dart'; -import '../../../artifact.dart'; -import '../../blocs/exp.dart'; -import '../toly_refresh_indicator.dart'; import 'sliver_article.dart'; import 'sliver_columnize.dart'; @@ -19,7 +19,6 @@ class TolyArticleScrollPage extends StatefulWidget { } class _TolyArticleScrollPageState extends State { - final RefreshController _refreshController = RefreshController(initialRefresh: false); @@ -34,7 +33,7 @@ class _TolyArticleScrollPageState extends State { @override Widget build(BuildContext context) { return RefreshConfigWrapper( - child: SmartRefresher( + child: TolyRefresh( enablePullUp: true, onRefresh: _onRefresh, onLoading: _loadMore, @@ -45,7 +44,7 @@ class _TolyArticleScrollPageState extends State { // handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), // ), SliverPadding( - padding: EdgeInsets.only(top: 10,bottom: 10), + padding: EdgeInsets.only(top: 10, bottom: 10), sliver: const SliverToBoxAdapter( child: ColumnizeViewPage(), ), @@ -75,7 +74,7 @@ class _TolyArticleScrollPageState extends State { await context.read().loadNextPageMore(); // int length = data.length; ArticleState state = bloc.state; - if(state is ArticleWithData){ + if (state is ArticleWithData) { if (state.data.length >= state.total) { _refreshController.loadNoData(); await Future.delayed(Duration(milliseconds: 2000)); @@ -85,8 +84,5 @@ class _TolyArticleScrollPageState extends State { _refreshController.loadComplete(); } - } - - } diff --git a/packages/artifact/lib/src/views/artifact_page.dart b/modules/knowledge_system/artifact/lib/src/articles/view/artifact_page.dart similarity index 88% rename from packages/artifact/lib/src/views/artifact_page.dart rename to modules/knowledge_system/artifact/lib/src/articles/view/artifact_page.dart index ff27fc677..1e7c65204 100644 --- a/packages/artifact/lib/src/views/artifact_page.dart +++ b/modules/knowledge_system/artifact/lib/src/articles/view/artifact_page.dart @@ -4,11 +4,12 @@ import 'package:algorithm/algorithm.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:l10n/l10n.dart'; import 'package:url_launcher/url_launcher.dart'; -import '../blocs/columnize/bloc.dart'; -import '../blocs/exp.dart'; -import '../repositories/exp.dart'; -import '../repositories/repository/article_repository.dart'; +import '../bloc/columnize/bloc.dart'; +import '../bloc/exp.dart'; +import '../data/exp.dart'; +import '../data/repository/article_repository.dart'; import 'article/toly_article_scroll_page.dart'; import 'dart:ui' as ui; @@ -46,7 +47,7 @@ class _ArtifactPageState extends State @override void initState() { super.initState(); - controller = TabController(length: 4, vsync: this); + controller = TabController(length: 2, vsync: this); controller.addListener(_listen); data = List.generate(5, (index) => 'Init $index'); } @@ -93,7 +94,8 @@ class _ArtifactPageState extends State children: [ GestureDetector( onTap: () { - _launchURL('https://github.com/toly1994328/FlutterUnit/blob/master/packages/algorithm/lib/src/algorithm/sort/functions/${name}.dart'); + _launchURL( + 'https://github.com/toly1994328/FlutterUnit/blob/master/packages/algorithm/lib/src/algorithm/sort/functions/${name}.dart'); }, child: Text( '查看排序源码', @@ -110,8 +112,8 @@ class _ArtifactPageState extends State Expanded(child: SortPaper()), ], ), - BuildingPanel(), - BuildingPanel(), + // BuildingPanel(), + // BuildingPanel(), ], ), ), @@ -122,7 +124,7 @@ class _ArtifactPageState extends State _launchURL(String url) async { Uri uri = Uri.parse(url); if (await canLaunchUrl(Uri.parse(url))) { - await launchUrl(uri,mode: LaunchMode.externalApplication); + await launchUrl(uri, mode: LaunchMode.externalApplication); } else { debugPrint('Could not launch $url'); } @@ -205,20 +207,20 @@ class _ArtifactPageState extends State tabs: [ Tab( // icon: Icon(Icons.account_balance_wallet_outlined), - text: '捷特文库', + text: context.l10n.knowledgeTabToly, ), Tab( // icon: Icon(Icons.account_balance_wallet_outlined), - text: '可视排序', - ), - Tab( - // icon: Icon(Icons.account_balance_wallet_outlined), - text: '布局宝库', - ), - Tab( - // icon: Icon(Icons.account_balance_wallet_outlined), - text: '要点宝库', + text: context.l10n.knowledgeTabAlgo, ), + // Tab( + // // icon: Icon(Icons.account_balance_wallet_outlined), + // text:context.l10n.knowledgeTabLayout, + // ), + // Tab( + // // icon: Icon(Icons.account_balance_wallet_outlined), + // text:context.l10n.knowledgeTabPoint, + // ), ], ), ), // ) diff --git a/modules/knowledge_system/artifact/lib/src/articles/view/building/building_panel.dart b/modules/knowledge_system/artifact/lib/src/articles/view/building/building_panel.dart new file mode 100644 index 000000000..e4696d54a --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/articles/view/building/building_panel.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; +import 'package:l10n/l10n.dart'; +class BuildingPanel extends StatelessWidget { + const BuildingPanel({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.add_chart,size: 36,color: Colors.grey,), + const SizedBox(height: 8,), + + Text( + context.l10n.knowledgeConstruction + ,style: TextStyle(color: Colors.grey),), + ], + ), + ); + } +} diff --git a/modules/knowledge_system/artifact/lib/src/articles/view/desk_artifact_page.dart b/modules/knowledge_system/artifact/lib/src/articles/view/desk_artifact_page.dart new file mode 100644 index 000000000..288c21e3c --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/articles/view/desk_artifact_page.dart @@ -0,0 +1,172 @@ +import 'package:algorithm/algorithm.dart'; +import 'package:artifact/artifact.dart'; +import 'package:components/components.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:layout/layout.dart'; +import 'package:url_launcher/url_launcher.dart'; + +import '../bloc/exp.dart'; +import '../data/exp.dart'; +import 'article/sliver_article.dart'; +import 'article/sliver_columnize.dart'; +import 'building/building_panel.dart'; +import 'package:l10n/l10n.dart'; + +class DeskKnowledgePage extends StatefulWidget { + const DeskKnowledgePage({super.key}); + + @override + State createState() => _DeskKnowledgePageState(); +} + +class _DeskKnowledgePageState extends State + with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin { + late TabController controller; + List data = []; + int _curIndex = 0; + + @override + void initState() { + super.initState(); + controller = TabController(length: 3, vsync: this); + controller.addListener(_listen); + data = List.generate(5, (index) => 'Init $index'); + } + + ArticleRepository aRepository = const ArticleRepository(); + ColumnizeRepository cRepository = const ColumnizeRepository(); + + @override + Widget build(BuildContext context) { + super.build(context); + AppLocalizations l10n = context.l10n; + + return MultiBlocProvider( + providers: [ + BlocProvider( + create: (_) => ColumnizeBloc(cRepository)..init()), + BlocProvider( + create: (_) => ArticleBloc(aRepository, pageSize: 1000)..init()), + ], + child: Scaffold( + endDrawer: SortSettings(), + body: Column( + children: [ + DeskKnowledgeTabTopBar( + onTabPressed: (int value) { + controller.index = value; + }, + tabs: [ + l10n.knowledgeTabLayout, + l10n.knowledgeTabAlgo, + l10n.knowledgeTabToly, + ], + ), + Expanded( + child: TabBarView( + controller: controller, + children: [ + LayoutRouterPage(), + AlgoRouterPage(), + TolyArticlesPage(), + // DeskPointPage(), + ], + )) + ], + ), + ), + ); + } + + void _listen() { + print('${controller.index}'); + if (_curIndex != controller.index) { + setState(() { + _curIndex = controller.index; + }); + } + } + + @override + bool get wantKeepAlive => true; +} + +class TolyArticlesPage extends StatelessWidget { + const TolyArticlesPage({super.key}); + + @override + Widget build(BuildContext context) { + return CustomScrollView( + slivers: [ + SliverPadding( + padding: EdgeInsets.only(top: 10, bottom: 10, right: 36, left: 36), + sliver: const SliverToBoxAdapter( + child: ColumnizeViewPage(), + ), + ), + SliverPadding( + padding: EdgeInsets.only(right: 36, left: 36), + sliver: SliverArticlePanel(), + ), + ], + ); + } +} + +class SoreAlgoPage extends StatelessWidget { + const SoreAlgoPage({super.key}); + + @override + Widget build(BuildContext context) { + String name = SortStateScope.of(context).config.name; + + return Column( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + children: [ + GestureDetector( + onTap: () { + _launchURL( + 'https://github.com/toly1994328/FlutterUnit/blob/master/packages/algorithm/lib/src/algorithm/sort/functions/${name}.dart'); + }, + child: Text( + '查看排序源码', + style: TextStyle( + fontSize: 12, + color: Theme.of(context).primaryColor, + ), + )), + Spacer(), + SortButton(), + const SizedBox( + width: 12, + ), + SortSelector(), + const SizedBox( + width: 12, + ), + GestureDetector( + onTap: () { + Scaffold.of(context).openEndDrawer(); + }, + child: Icon(Icons.settings)) + ], + ), + ), + Expanded(child: SortPaper()), + ], + ); + } + + void _launchURL(String url) async { + Uri uri = Uri.parse(url); + if (await canLaunchUrl(Uri.parse(url))) { + await launchUrl(uri, mode: LaunchMode.externalApplication); + } else { + debugPrint('Could not launch $url'); + } + } +} diff --git a/modules/knowledge_system/artifact/lib/src/points/bloc/bloc.dart b/modules/knowledge_system/artifact/lib/src/points/bloc/bloc.dart new file mode 100644 index 000000000..89c849669 --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/points/bloc/bloc.dart @@ -0,0 +1,4 @@ +export 'point_comment_bloc.dart'; +export 'point_bloc.dart'; + + diff --git a/modules/knowledge_system/artifact/lib/src/points/bloc/point_bloc.dart b/modules/knowledge_system/artifact/lib/src/points/bloc/point_bloc.dart new file mode 100644 index 000000000..91f6d32de --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/points/bloc/point_bloc.dart @@ -0,0 +1,53 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../repository/api/point_api.dart'; +import 'package:equatable/equatable.dart'; +import '../data/model/issue.dart'; + +/// create by 张风捷特烈 on 2020-09-03 +/// contact me by email 1981462002@qq.com +/// 说明: + +class PointBloc extends Cubit { + final PointApi api; + + PointBloc(this.api) : super(const PointLoading()); + + void loadPoint() async { + emit(const PointLoading()); + ApiRet> ret = await api.getIssues(); + if(ret.failed){ + emit(PointLoadFailure(ret.msg)); + } + emit(PointLoaded(ret.data)); + } +} + +sealed class PointState extends Equatable { + const PointState(); +} + +class PointLoading extends PointState { + const PointLoading(); + + @override + List get props => []; +} + +class PointLoaded extends PointState { + final List issues; + + const PointLoaded(this.issues); + + @override + List get props => [issues]; +} + +class PointLoadFailure extends PointState { + final String error; + + const PointLoadFailure(this.error); + + @override + List get props => [error]; +} diff --git a/modules/knowledge_system/artifact/lib/src/points/bloc/point_comment_bloc.dart b/modules/knowledge_system/artifact/lib/src/points/bloc/point_comment_bloc.dart new file mode 100644 index 000000000..0a71207aa --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/points/bloc/point_comment_bloc.dart @@ -0,0 +1,75 @@ +import 'package:artifact/src/points/repository/api/point_api.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../data/model/github_model.dart'; +import 'package:equatable/equatable.dart'; + +/// create by 张风捷特烈 on 2020-09-03 +/// contact me by email 1981462002@qq.com +/// 说明: + +class PointCommentBloc extends Cubit { + final PointApi api = PointApiImpl(); + + PointCommentBloc() : super(const PointCommentInitial()); + + void loadPointComment(Issue point) async { + emit(PointCommentLoading(point)); + if (point.number == null) { + emit(const PointCommentLoadFailure('point_bloc id 为空')); + return; + } + ApiRet> ret = await api.getIssuesComment(point.number!); + if (ret.failed) { + emit(PointCommentLoadFailure(ret.msg)); + return; + } + final List comments = ret.data; + comments.sort((a, b) => a.createdAt!.compareTo(b.createdAt!)); + emit(PointCommentLoaded(point, comments)); + } +} + +sealed class PointCommentState extends Equatable { + const PointCommentState(); +} + +class PointCommentInitial extends PointCommentState { + const PointCommentInitial(); + + @override + List get props => []; +} + +class PointCommentLoading extends PointCommentState { + final Issue issue; + + const PointCommentLoading(this.issue); + + @override + List get props => [issue]; +} + +class PointCommentLoaded extends PointCommentState { + final Issue issue; + final List comments; + + const PointCommentLoaded(this.issue, this.comments); + + @override + List get props => [issue, comments]; + + @override + String toString() { + return 'PointCommentLoaded{issue: $issue, comments: $comments}'; + } +} + +class PointCommentLoadFailure extends PointCommentState { + final String error; + + const PointCommentLoadFailure(this.error); + + @override + List get props => [error]; +} diff --git a/lib/point_system/api/app_info.dart b/modules/knowledge_system/artifact/lib/src/points/data/api/app_info.dart similarity index 100% rename from lib/point_system/api/app_info.dart rename to modules/knowledge_system/artifact/lib/src/points/data/api/app_info.dart diff --git a/lib/point_system/api/category_api.dart b/modules/knowledge_system/artifact/lib/src/points/data/api/category_api.dart similarity index 100% rename from lib/point_system/api/category_api.dart rename to modules/knowledge_system/artifact/lib/src/points/data/api/category_api.dart diff --git a/modules/knowledge_system/artifact/lib/src/points/data/api/issues_api.dart b/modules/knowledge_system/artifact/lib/src/points/data/api/issues_api.dart new file mode 100644 index 000000000..9856b9a43 --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/points/data/api/issues_api.dart @@ -0,0 +1,13 @@ +import 'dart:convert'; + +import 'package:dio/dio.dart'; + +import '../model/github_model.dart'; +import '../model/issue.dart'; +import '../model/repository.dart'; + + +/// create by 张风捷特烈 on 2020/6/17 +/// contact me by email 1981462002@qq.com +/// 说明: + diff --git a/lib/point_system/github_model/github_model.dart b/modules/knowledge_system/artifact/lib/src/points/data/model/github_model.dart similarity index 100% rename from lib/point_system/github_model/github_model.dart rename to modules/knowledge_system/artifact/lib/src/points/data/model/github_model.dart diff --git a/lib/point_system/github_model/github_user.dart b/modules/knowledge_system/artifact/lib/src/points/data/model/github_user.dart similarity index 100% rename from lib/point_system/github_model/github_user.dart rename to modules/knowledge_system/artifact/lib/src/points/data/model/github_user.dart diff --git a/lib/point_system/github_model/issue.dart b/modules/knowledge_system/artifact/lib/src/points/data/model/issue.dart similarity index 100% rename from lib/point_system/github_model/issue.dart rename to modules/knowledge_system/artifact/lib/src/points/data/model/issue.dart diff --git a/lib/point_system/github_model/issue_comment.dart b/modules/knowledge_system/artifact/lib/src/points/data/model/issue_comment.dart similarity index 100% rename from lib/point_system/github_model/issue_comment.dart rename to modules/knowledge_system/artifact/lib/src/points/data/model/issue_comment.dart diff --git a/lib/point_system/github_model/license.dart b/modules/knowledge_system/artifact/lib/src/points/data/model/license.dart similarity index 100% rename from lib/point_system/github_model/license.dart rename to modules/knowledge_system/artifact/lib/src/points/data/model/license.dart diff --git a/lib/point_system/github_model/repository.dart b/modules/knowledge_system/artifact/lib/src/points/data/model/repository.dart similarity index 100% rename from lib/point_system/github_model/repository.dart rename to modules/knowledge_system/artifact/lib/src/points/data/model/repository.dart diff --git a/lib/point_system/github_model/repository_permissions.dart b/modules/knowledge_system/artifact/lib/src/points/data/model/repository_permissions.dart similarity index 100% rename from lib/point_system/github_model/repository_permissions.dart rename to modules/knowledge_system/artifact/lib/src/points/data/model/repository_permissions.dart diff --git a/modules/knowledge_system/artifact/lib/src/points/exp.dart b/modules/knowledge_system/artifact/lib/src/points/exp.dart new file mode 100644 index 000000000..dd2135941 --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/points/exp.dart @@ -0,0 +1,8 @@ +export 'data/api/category_api.dart'; + +export 'view/desk_ui/desk_point_page.dart'; +export 'view/desk_ui/github_repo_panel.dart'; +export 'view/issues_point/issues_point_page.dart'; +export 'view/issues_point/issues_detail.dart'; + +export 'bloc/bloc.dart'; \ No newline at end of file diff --git a/modules/knowledge_system/artifact/lib/src/points/repository/api/point_api.dart b/modules/knowledge_system/artifact/lib/src/points/repository/api/point_api.dart new file mode 100644 index 000000000..d9a4da21b --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/points/repository/api/point_api.dart @@ -0,0 +1,55 @@ +import 'dart:convert'; + +import '../../data/model/issue.dart'; +import '../../data/model/issue_comment.dart'; +import '../../data/model/repository.dart'; +import 'package:fx_dio/fx_dio.dart'; +import 'package:app/app.dart'; + +export 'package:fx_dio/fx_dio.dart' show ApiRet; + +abstract interface class PointApi { + /// 获取 github 中 FlutterUnit 仓库信息 + Future> getFlutterUnitRepo(); + + /// 获取 github 中 FlutterUnit 仓库 issues 列表 + Future>> getIssues(); + + Future>> getIssuesComment(int pointId); +} + +class PointApiImpl implements PointApi { + Host get unit => FxDio()(); + + @override + Future> getFlutterUnitRepo() async { + return unit.get(UnitApi.repository.path, convertor: (data) { + dynamic repoStr = data['data']['repositoryData']; + return Repository.fromJson(json.decode(repoStr)); + }); + } + + @override + Future>> getIssues( + {int page = 1, int pageSize = 100}) async { + return unit.get>( + UnitApi.point.path, + queryParameters: { + "page": page, + "pageSize": pageSize, + }, + convertor: (data) => data['data'] + .map((e) => Issue.fromJson(json.decode(e['pointData']))) + .toList(), + ); + } + + @override + Future>> getIssuesComment(int pointId) async { + return unit.get>("${UnitApi.pointComment.path}$pointId", + convertor: (data) => data['data'] + .map((e) => + IssueComment.fromJson(json.decode(e['pointCommentData']))) + .toList()); + } +} diff --git a/modules/knowledge_system/artifact/lib/src/points/view/desk_ui/desk_point_page.dart b/modules/knowledge_system/artifact/lib/src/points/view/desk_ui/desk_point_page.dart new file mode 100644 index 000000000..fc6563e57 --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/points/view/desk_ui/desk_point_page.dart @@ -0,0 +1,154 @@ +import 'package:app/app.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:url_launcher/url_launcher.dart'; + +import '../../bloc/point_bloc.dart'; +import '../../bloc/bloc.dart'; +import '../../data/model/repository.dart'; +import '../../repository/api/point_api.dart'; +import '../issues_point/issues_point_page.dart'; +import 'github_repo_panel.dart'; +import 'package:fx_dio/fx_dio.dart'; + +class DeskPointPage extends StatefulWidget { + const DeskPointPage({Key? key}) : super(key: key); + + @override + State createState() => _DeskPointPageState(); +} + +class _DeskPointPageState extends State { + Repository _repository = Repository.fromJson({ + 'full_name': 'toly1994328/FlutterUnit', + 'license': {"spdx_id": 'GPL-3.0'}, + 'description': + '【Flutter 集录指南 App】The unity of flutter, The unity of coder.', + 'stargazers_count': 7958, + 'forks_count': 1039, + 'subscribers_count': 126, + 'open_issues_count': 40, + }); + + final PointApi _api = PointApiImpl(); + + @override + void initState() { + super.initState(); + _loadRepo(); + } + + void _loadRepo() async { + final ApiRet ret = await _api.getFlutterUnitRepo(); + if (ret.success) { + setState(() { + _repository = ret.data; + }); + } + } + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (_) => PointBloc(_api)..loadPoint(), + child: Scaffold( + body: Column( + children: [ + Expanded( + child: Row( + children: [ + Column( + children: [ + GithubRepoPanel( + repository: _repository, + ), + Expanded(child: SizedBox(width: 250, child: IssuesTip())) + ], + ), + VerticalDivider( + width: 1, + ), + Expanded(flex: 2, child: IssuesPointContent()), + ], + )) + ], + ), + ), + ); + } +} + +class IssuesTip extends StatelessWidget { + const IssuesTip({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: Text.rich( + TextSpan(children: [ + TextSpan( + text: '* 注: ', + style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold)), + TextSpan( + text: + '要点集录中的 QA 数据收录在 FlutterUnit 以 point 为标签的 issues 中。如果需要提供数据,在 issues 中问答即可。'), + TextSpan( + text: '点击这里跳转', + mouseCursor: SystemMouseCursors.click, + recognizer: TapGestureRecognizer()..onTap = _toUrl, + style: TextStyle( + color: Colors.blue, + decoration: TextDecoration.underline, + fontWeight: FontWeight.bold)), + ]), + style: TextStyle(fontSize: 14), + ), + ); + } + + void _toUrl() async { + String url = + 'https://github.com/toly1994328/FlutterUnit/issues?q=label%3Apoint+'; + if (!await launchUrl(Uri.parse(url))) { + throw Exception('Could not launch $url'); + } + } +} + +class SimpleDeskTopBar extends StatelessWidget { + final Widget? leading; + final Widget? tail; + final double height; + + const SimpleDeskTopBar( + {super.key, this.leading, this.tail, this.height = 64}); + + @override + Widget build(BuildContext context) { + bool isDark = Theme.of(context).brightness == Brightness.dark; + + return DragToMoveWrapper( + child: Container( + height: height, + color: isDark ? Color(0xff2C3036) : Colors.white, + child: Row( + children: [ + if (leading != null) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: leading!, + ), + const Spacer(), + const SizedBox( + width: 20, + ), + if (tail != null) tail!, + const WindowButtons(), + ], + ), + ), + ); + } +} diff --git a/lib/point_system/views/desk_ui/github_repo_panel.dart b/modules/knowledge_system/artifact/lib/src/points/view/desk_ui/github_repo_panel.dart similarity index 98% rename from lib/point_system/views/desk_ui/github_repo_panel.dart rename to modules/knowledge_system/artifact/lib/src/points/view/desk_ui/github_repo_panel.dart index c6597c9f6..910586e11 100644 --- a/lib/point_system/views/desk_ui/github_repo_panel.dart +++ b/modules/knowledge_system/artifact/lib/src/points/view/desk_ui/github_repo_panel.dart @@ -1,8 +1,9 @@ import 'package:app/app.dart'; -import 'package:components/components.dart'; +import 'package:toly_ui/toly_ui.dart'; import 'package:flutter/material.dart'; -import '../../github_model/repository.dart'; +import '../../data/model/repository.dart'; + class GithubRepoPanel extends StatefulWidget { final Repository repository; diff --git a/modules/knowledge_system/artifact/lib/src/points/view/issues_point/issue_item.dart b/modules/knowledge_system/artifact/lib/src/points/view/issues_point/issue_item.dart new file mode 100644 index 000000000..de2aadedd --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/points/view/issues_point/issue_item.dart @@ -0,0 +1,103 @@ +import 'dart:ui'; + +import 'package:app/app.dart'; +import 'package:toly_ui/toly_ui.dart'; +import 'package:flutter/material.dart'; +import 'package:utils/utils.dart'; + +import '../../data/model/issue.dart'; + +/// create by 张风捷特烈 on 2020/9/3 +/// contact me by email 1981462002@qq.com +/// 说明: + +class IssueItem extends StatelessWidget { + final Issue issue; + final ValueChanged onTap; + + const IssueItem({ + super.key, + required this.issue, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + bool isDark = Theme.of(context).brightness == Brightness.dark; + + return GestureDetector( + onTap: () => onTap(issue), + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: Theme.of(context).dividerTheme.color ?? Colors.transparent, + width: 1 / window.devicePixelRatio)), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildTop(), + Padding( + padding: const EdgeInsets.only(top: 5.0, bottom: 5.0, left: 10), + child: Text( + '${issue.title}', + style: TextStyle(fontSize: 15, color: Colors.grey, shadows: [ + Shadow(color: isDark ? Colors.black : Colors.white, offset: Offset(1, .5)) + ]), + ), + ), + Row( + children: [ + const Spacer(), + WrapColor( + color: Colors.greenAccent, + child: Text( + issue.commentNum.toString(), + style: const TextStyle(color: Colors.white), + )), + const SizedBox( + width: 5, + ), + const Icon( + TolyIcon.icon_common, + size: 20, + ), + ], + ) + ], + ), + ), + ); + } + + Widget _buildTop() { + return Row( + children: [ + CircleImage( + image: NetworkImage(issue.user?.avatarUrl ?? ''), + size: 40, + borderSize: 2, + ), + const SizedBox( + width: 10, + ), + WrapColor( + child: Text( + "#${issue.number}", + style: const TextStyle(color: Colors.white), + )), + const SizedBox( + width: 10, + ), + Text( + '${issue.user?.login}', + style: const TextStyle(fontWeight: FontWeight.bold), + ), + const Spacer(), + Text(ConvertMan.time2string(issue.createdAt!)), + ], + ); + } +} diff --git a/lib/point_system/views/issues_point/issues_detail.dart b/modules/knowledge_system/artifact/lib/src/points/view/issues_point/issues_detail.dart similarity index 95% rename from lib/point_system/views/issues_point/issues_detail.dart rename to modules/knowledge_system/artifact/lib/src/points/view/issues_point/issues_detail.dart index dd5d24c42..0338d57e2 100644 --- a/lib/point_system/views/issues_point/issues_detail.dart +++ b/modules/knowledge_system/artifact/lib/src/points/view/issues_point/issues_detail.dart @@ -1,10 +1,12 @@ import 'package:app/app.dart'; -import 'package:components/toly_ui/toly_ui.dart'; +import 'package:toly_ui/toly_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/app/utils/convert_man.dart'; -import 'package:flutter_unit/point_system/blocs/point_system_bloc.dart'; -import 'package:flutter_unit/point_system/github_model/github_model.dart'; +import 'package:utils/utils.dart'; + +import '../../bloc/bloc.dart'; +import '../../data/model/github_model.dart'; +import '../../data/model/issue.dart'; /// create by 张风捷特烈 on 2020/9/3 /// contact me by email 1981462002@qq.com @@ -18,7 +20,7 @@ class IssuesDetailPage extends StatelessWidget { return Scaffold( appBar: PreferredSize( preferredSize: Size(0,kToolbarHeight), - child: DragToMoveAreaNoDouble( + child: DragToMoveWrapper( child: AppBar( centerTitle: false, title: const Text('Flutter 要点集录'), diff --git a/modules/knowledge_system/artifact/lib/src/points/view/issues_point/issues_point_page.dart b/modules/knowledge_system/artifact/lib/src/points/view/issues_point/issues_point_page.dart new file mode 100644 index 000000000..99f19db1d --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/points/view/issues_point/issues_point_page.dart @@ -0,0 +1,170 @@ +import 'package:app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; + +import '../../bloc/bloc.dart'; +import '../../data/model/issue.dart'; +import '../../data/model/repository.dart'; +import '../../repository/api/point_api.dart'; +import 'issue_item.dart'; +import 'issues_detail.dart'; +import 'repo_widget.dart'; + +/// create by 张风捷特烈 on 2020/6/17 +/// contact me by email 1981462002@qq.com +/// 说明: + +class IssuesPointScope extends StatelessWidget { + const IssuesPointScope({super.key}); + + @override + Widget build(BuildContext context) { + return MultiBlocProvider( + providers: [ + BlocProvider( + create: (_) => PointBloc(PointApiImpl())..loadPoint()), + ], + child: const IssuesPointPage(), + ); + } +} + +class IssuesPointPage extends StatelessWidget { + const IssuesPointPage({super.key}); + + @override + Widget build(BuildContext context) { + return const Scaffold(body: IssuesPointContent()); + } +} + +class IssuesPointContent extends StatefulWidget { + const IssuesPointContent({super.key}); + + @override + State createState() => _IssuesPointContentState(); +} + +class _IssuesPointContentState extends State { + Repository? _repository; + + @override + void initState() { + super.initState(); + _loadRepo(); + } + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (_, state) => RefreshIndicator( + onRefresh: _loadIssues, + child: CustomScrollView(slivers: [ + // _buildSliverAppBar(), + buildContentByState(state) + ]), + )); + } + + Widget buildContentByState(PointState state) { + if (state is PointLoading) { + return const SliverPadding( + padding: EdgeInsets.only(top: 150), + sliver: SliverToBoxAdapter( + child: Center( + child: SpinKitCircle( + color: Colors.blue, + ), + )), + ); + } + + if (state is PointLoaded) { + List issues = state.issues; + return SliverList( + delegate: SliverChildBuilderDelegate( + (ctx, int index) => + IssueItem(onTap: toDetailPage, issue: issues[index]), + childCount: issues.length, + ), + ); + } + + if (state is PointLoadFailure) { + return SliverPadding( + padding: const EdgeInsets.only(top: 40), + sliver: SliverToBoxAdapter( + child: Center( + child: Text(state.error), + )), + ); + } + + return const SliverPadding( + padding: EdgeInsets.zero, + ); + } + + void toDetailPage(Issue issue) { + Navigator.of(context).push( + SlidePageRoute( + child: BlocProvider( + create: (_) => PointCommentBloc()..loadPointComment(issue), + child: const IssuesDetailPage(), + ), + ), + ); + } + + Widget _buildSliverAppBar() { + return SliverAppBar( + expandedHeight: 210.0, +// leading: _buildLeading(), + title: const Text('Flutter要点集录'), +// actions: _buildActions(), + elevation: 5, + pinned: true, + actions: [ + IconButton( + icon: const Icon( + Icons.help_outline, + color: Colors.white, + ), + onPressed: () { + // Navigator.of(context).pushNamed(UnitRouter.bug); + }) + ], + backgroundColor: Colors.blue, + flexibleSpace: FlexibleSpaceBar( + //伸展处布局 + titlePadding: const EdgeInsets.only(left: 55, bottom: 15), //标题边距 + collapseMode: CollapseMode.parallax, //视差效果 + background: _repository == null + ? const Center( + child: SpinKitFadingCube( + color: Colors.white, + ), + ) + : RepoWidget( + repository: _repository!, + ), + ), + ); + } + + Future _loadIssues() async { + BlocProvider.of(context).loadPoint(); + await Future.delayed(const Duration(milliseconds: 200)); + } + + void _loadRepo() async { + PointApi api = context.read().api; + final ApiRet ret = await api.getFlutterUnitRepo(); + if (ret.success) { + setState(() { + _repository = ret.data; + }); + } + } +} diff --git a/lib/point_system/views/issues_point/repo_widget.dart b/modules/knowledge_system/artifact/lib/src/points/view/issues_point/repo_widget.dart similarity index 93% rename from lib/point_system/views/issues_point/repo_widget.dart rename to modules/knowledge_system/artifact/lib/src/points/view/issues_point/repo_widget.dart index 57a458204..36849e1b3 100644 --- a/lib/point_system/views/issues_point/repo_widget.dart +++ b/modules/knowledge_system/artifact/lib/src/points/view/issues_point/repo_widget.dart @@ -1,9 +1,9 @@ import 'package:app/app.dart'; -import 'package:components/toly_ui/toly_ui.dart'; +import 'package:toly_ui/toly_ui.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_unit/app/utils/convert_man.dart'; -import 'package:flutter_unit/point_system/github_model/github_model.dart'; +import 'package:utils/utils.dart'; +import '../../data/model/repository.dart'; /// create by 张风捷特烈 on 2020/9/3 /// contact me by email 1981462002@qq.com @@ -52,8 +52,7 @@ class RepoWidget extends StatelessWidget { ), const Spacer(), Text( - "创建:" + - ConvertMan.time2string(repository.createdAt!, just: true), + "创建:${ConvertMan.time2string(repository.createdAt!, just: true)}", style: const TextStyle(color: Colors.grey), ), ], diff --git a/modules/knowledge_system/artifact/pubspec.yaml b/modules/knowledge_system/artifact/pubspec.yaml new file mode 100644 index 000000000..96c3113b1 --- /dev/null +++ b/modules/knowledge_system/artifact/pubspec.yaml @@ -0,0 +1,50 @@ +name: artifact +description: artifact +version: 0.0.1 +homepage: + +environment: + sdk: ">=3.5.0 <4.0.0" + flutter: ">=2.17.0" +resolution: workspace +dependencies: + flutter: + sdk: flutter + + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # To add assets to your package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # To add custom fonts to your package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/utils/test/utils_test.dart b/modules/knowledge_system/artifact/test/utils_test.dart similarity index 100% rename from packages/utils/test/utils_test.dart rename to modules/knowledge_system/artifact/test/utils_test.dart diff --git a/modules/knowledge_system/awesome/.gitignore b/modules/knowledge_system/awesome/.gitignore new file mode 100644 index 000000000..ac5aa9893 --- /dev/null +++ b/modules/knowledge_system/awesome/.gitignore @@ -0,0 +1,29 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +build/ diff --git a/modules/knowledge_system/awesome/.metadata b/modules/knowledge_system/awesome/.metadata new file mode 100644 index 000000000..fe59252be --- /dev/null +++ b/modules/knowledge_system/awesome/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9" + channel: "stable" + +project_type: package diff --git a/packages/widgets/CHANGELOG.md b/modules/knowledge_system/awesome/CHANGELOG.md similarity index 100% rename from packages/widgets/CHANGELOG.md rename to modules/knowledge_system/awesome/CHANGELOG.md diff --git a/packages/widgets/LICENSE b/modules/knowledge_system/awesome/LICENSE similarity index 100% rename from packages/widgets/LICENSE rename to modules/knowledge_system/awesome/LICENSE diff --git a/packages/widgets/README.md b/modules/knowledge_system/awesome/README.md similarity index 100% rename from packages/widgets/README.md rename to modules/knowledge_system/awesome/README.md diff --git a/packages/widgets/analysis_options.yaml b/modules/knowledge_system/awesome/analysis_options.yaml similarity index 100% rename from packages/widgets/analysis_options.yaml rename to modules/knowledge_system/awesome/analysis_options.yaml diff --git a/modules/knowledge_system/awesome/lib/awesome.dart b/modules/knowledge_system/awesome/lib/awesome.dart new file mode 100644 index 000000000..d19ac0978 --- /dev/null +++ b/modules/knowledge_system/awesome/lib/awesome.dart @@ -0,0 +1,7 @@ +library awesome; + +/// A Calculator. +class Calculator { + /// Returns [value] plus 1. + int addOne(int value) => value + 1; +} diff --git a/lib/awesome/listenable/change_notifier_01/main.dart b/modules/knowledge_system/awesome/lib/listenable/change_notifier_01/main.dart similarity index 100% rename from lib/awesome/listenable/change_notifier_01/main.dart rename to modules/knowledge_system/awesome/lib/listenable/change_notifier_01/main.dart diff --git a/lib/awesome/listenable/change_notifier_01/notifier/progress_value_notifier.dart b/modules/knowledge_system/awesome/lib/listenable/change_notifier_01/notifier/progress_value_notifier.dart similarity index 100% rename from lib/awesome/listenable/change_notifier_01/notifier/progress_value_notifier.dart rename to modules/knowledge_system/awesome/lib/listenable/change_notifier_01/notifier/progress_value_notifier.dart diff --git a/lib/awesome/listenable/change_notifier_01/page/detail/detail_progress_view.dart b/modules/knowledge_system/awesome/lib/listenable/change_notifier_01/page/detail/detail_progress_view.dart similarity index 100% rename from lib/awesome/listenable/change_notifier_01/page/detail/detail_progress_view.dart rename to modules/knowledge_system/awesome/lib/listenable/change_notifier_01/page/detail/detail_progress_view.dart diff --git a/lib/awesome/listenable/change_notifier_01/page/detail/download_detail.dart b/modules/knowledge_system/awesome/lib/listenable/change_notifier_01/page/detail/download_detail.dart similarity index 100% rename from lib/awesome/listenable/change_notifier_01/page/detail/download_detail.dart rename to modules/knowledge_system/awesome/lib/listenable/change_notifier_01/page/detail/download_detail.dart diff --git a/lib/awesome/listenable/change_notifier_01/page/home/home_page.dart b/modules/knowledge_system/awesome/lib/listenable/change_notifier_01/page/home/home_page.dart similarity index 100% rename from lib/awesome/listenable/change_notifier_01/page/home/home_page.dart rename to modules/knowledge_system/awesome/lib/listenable/change_notifier_01/page/home/home_page.dart diff --git a/lib/awesome/listenable/change_notifier_01/page/home/home_progress_view.dart b/modules/knowledge_system/awesome/lib/listenable/change_notifier_01/page/home/home_progress_view.dart similarity index 100% rename from lib/awesome/listenable/change_notifier_01/page/home/home_progress_view.dart rename to modules/knowledge_system/awesome/lib/listenable/change_notifier_01/page/home/home_progress_view.dart diff --git a/modules/knowledge_system/awesome/lib/listenable/change_notifier_02/main.dart b/modules/knowledge_system/awesome/lib/listenable/change_notifier_02/main.dart new file mode 100644 index 000000000..5b807aa88 --- /dev/null +++ b/modules/knowledge_system/awesome/lib/listenable/change_notifier_02/main.dart @@ -0,0 +1,32 @@ + +import 'package:flutter/material.dart'; + +import 'notifier/download_data_scope.dart'; +import 'notifier/progress_value_notifier.dart'; +import 'page/home/home_page.dart'; + +class MyApp extends StatelessWidget{ + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + return DownloadDataScope( + notifier: ProgressValueNotifier(), + child: MaterialApp( + debugShowCheckedModeBanner: false, + theme: ThemeData( + colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), + useMaterial3: true, + ), + home: const ChangeNotifierHome02(), + ), + ); + } +} + + + + + + + diff --git a/lib/awesome/listenable/change_notifier_02/notifier/download_data_scope.dart b/modules/knowledge_system/awesome/lib/listenable/change_notifier_02/notifier/download_data_scope.dart similarity index 100% rename from lib/awesome/listenable/change_notifier_02/notifier/download_data_scope.dart rename to modules/knowledge_system/awesome/lib/listenable/change_notifier_02/notifier/download_data_scope.dart diff --git a/lib/awesome/listenable/change_notifier_02/notifier/progress_value_notifier.dart b/modules/knowledge_system/awesome/lib/listenable/change_notifier_02/notifier/progress_value_notifier.dart similarity index 100% rename from lib/awesome/listenable/change_notifier_02/notifier/progress_value_notifier.dart rename to modules/knowledge_system/awesome/lib/listenable/change_notifier_02/notifier/progress_value_notifier.dart diff --git a/lib/awesome/listenable/change_notifier_02/page/detail/detail_progress_view.dart b/modules/knowledge_system/awesome/lib/listenable/change_notifier_02/page/detail/detail_progress_view.dart similarity index 100% rename from lib/awesome/listenable/change_notifier_02/page/detail/detail_progress_view.dart rename to modules/knowledge_system/awesome/lib/listenable/change_notifier_02/page/detail/detail_progress_view.dart diff --git a/lib/awesome/listenable/change_notifier_02/page/detail/download_detail.dart b/modules/knowledge_system/awesome/lib/listenable/change_notifier_02/page/detail/download_detail.dart similarity index 100% rename from lib/awesome/listenable/change_notifier_02/page/detail/download_detail.dart rename to modules/knowledge_system/awesome/lib/listenable/change_notifier_02/page/detail/download_detail.dart diff --git a/lib/awesome/listenable/change_notifier_02/page/home/home_page.dart b/modules/knowledge_system/awesome/lib/listenable/change_notifier_02/page/home/home_page.dart similarity index 100% rename from lib/awesome/listenable/change_notifier_02/page/home/home_page.dart rename to modules/knowledge_system/awesome/lib/listenable/change_notifier_02/page/home/home_page.dart diff --git a/lib/awesome/listenable/change_notifier_02/page/home/home_progress_view.dart b/modules/knowledge_system/awesome/lib/listenable/change_notifier_02/page/home/home_progress_view.dart similarity index 86% rename from lib/awesome/listenable/change_notifier_02/page/home/home_progress_view.dart rename to modules/knowledge_system/awesome/lib/listenable/change_notifier_02/page/home/home_progress_view.dart index b5a3d1d0d..96d73da54 100644 --- a/lib/awesome/listenable/change_notifier_02/page/home/home_progress_view.dart +++ b/modules/knowledge_system/awesome/lib/listenable/change_notifier_02/page/home/home_progress_view.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:flutter_unit/awesome/listenable/change_notifier_02/notifier/download_data_scope.dart'; +import '../../notifier/download_data_scope.dart'; import '../../notifier/progress_value_notifier.dart'; class HomeProgressView extends StatelessWidget{ diff --git a/modules/knowledge_system/awesome/pubspec.yaml b/modules/knowledge_system/awesome/pubspec.yaml new file mode 100644 index 000000000..2c13654f9 --- /dev/null +++ b/modules/knowledge_system/awesome/pubspec.yaml @@ -0,0 +1,50 @@ +name: awesome +description: "A new Flutter package project." +version: 0.0.1 +homepage: + +environment: + sdk: ">=3.5.0 <4.0.0" + flutter: ">=1.17.0" +resolution: workspace +dependencies: + flutter: + sdk: flutter + + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # To add assets to your package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # To add custom fonts to your package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/modules/knowledge_system/awesome/test/awesome_test.dart b/modules/knowledge_system/awesome/test/awesome_test.dart new file mode 100644 index 000000000..40896d375 --- /dev/null +++ b/modules/knowledge_system/awesome/test/awesome_test.dart @@ -0,0 +1,12 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:awesome/awesome.dart'; + +void main() { + test('adds one to input values', () { + final calculator = Calculator(); + expect(calculator.addOne(2), 3); + expect(calculator.addOne(-7), -6); + expect(calculator.addOne(0), 1); + }); +} diff --git a/modules/knowledge_system/layout/.gitignore b/modules/knowledge_system/layout/.gitignore new file mode 100644 index 000000000..ac5aa9893 --- /dev/null +++ b/modules/knowledge_system/layout/.gitignore @@ -0,0 +1,29 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +build/ diff --git a/modules/knowledge_system/layout/.metadata b/modules/knowledge_system/layout/.metadata new file mode 100644 index 000000000..2b377030c --- /dev/null +++ b/modules/knowledge_system/layout/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "7482962148e8d758338d8a28f589f317e1e42ba4" + channel: "stable" + +project_type: package diff --git a/modules/knowledge_system/layout/CHANGELOG.md b/modules/knowledge_system/layout/CHANGELOG.md new file mode 100644 index 000000000..41cc7d819 --- /dev/null +++ b/modules/knowledge_system/layout/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/modules/knowledge_system/layout/LICENSE b/modules/knowledge_system/layout/LICENSE new file mode 100644 index 000000000..ba75c69f7 --- /dev/null +++ b/modules/knowledge_system/layout/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/modules/knowledge_system/layout/README.md b/modules/knowledge_system/layout/README.md new file mode 100644 index 000000000..02fe8ecab --- /dev/null +++ b/modules/knowledge_system/layout/README.md @@ -0,0 +1,39 @@ + + +TODO: Put a short description of the package here that helps potential users +know whether this package might be useful for them. + +## Features + +TODO: List what your package can do. Maybe include images, gifs, or videos. + +## Getting started + +TODO: List prerequisites and provide or point to information on how to +start using the package. + +## Usage + +TODO: Include short and useful examples for package users. Add longer examples +to `/example` folder. + +```dart +const like = 'sample'; +``` + +## Additional information + +TODO: Tell users more about the package: where to find more information, how to +contribute to the package, how to file issues, what response they can expect +from the package authors, and more. diff --git a/modules/knowledge_system/layout/analysis_options.yaml b/modules/knowledge_system/layout/analysis_options.yaml new file mode 100644 index 000000000..a5744c1cf --- /dev/null +++ b/modules/knowledge_system/layout/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/modules/knowledge_system/layout/lib/layout.dart b/modules/knowledge_system/layout/lib/layout.dart new file mode 100644 index 000000000..bc7cbf6d9 --- /dev/null +++ b/modules/knowledge_system/layout/lib/layout.dart @@ -0,0 +1,3 @@ +library layout; + +export 'src/views/layout_page.dart'; \ No newline at end of file diff --git a/modules/knowledge_system/layout/lib/src/bloc/display_logic.dart b/modules/knowledge_system/layout/lib/src/bloc/display_logic.dart new file mode 100644 index 000000000..110c1da88 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/bloc/display_logic.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; + +import '../data/display_map/display_map.dart'; +import '../data/model/display_frame.dart'; +import 'display_state.dart'; + +class DisplayLogic with ChangeNotifier { + DisplayLogic(this._state); + + DisplayState _state; + + DisplayState get state => _state; + + bool get enableNext => _state.activeIndex < _state.total - 1; + + bool get enablePrev => _state.activeIndex > 0; + + void nextPage() { + if (enableNext) { + _state = _state.copyWith(activeIndex: _state.activeIndex + 1); + notifyListeners(); + } + } + + void active(String path) { + if (path != _state.router) { + int length = kDisplayMap[path]!.length; + _state = DisplayState( + router: path, + activeIndex: 0, + total: length, + ); + notifyListeners(); + } + } + + void prevPage() { + if (enablePrev) { + _state = _state.copyWith(activeIndex: _state.activeIndex - 1); + notifyListeners(); + } + } +} + +class DisplayScope extends InheritedNotifier { + const DisplayScope({ + required super.notifier, + required super.child, + super.key, + }); + + static DisplayLogic of(BuildContext context) => + context.dependOnInheritedWidgetOfExactType()!.notifier!; +} diff --git a/modules/knowledge_system/layout/lib/src/bloc/display_state.dart b/modules/knowledge_system/layout/lib/src/bloc/display_state.dart new file mode 100644 index 000000000..dddb24d12 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/bloc/display_state.dart @@ -0,0 +1,30 @@ +import 'package:layout/src/data/model/display_frame.dart'; + +import '../data/display_map/display_map.dart'; + +class DisplayState { + final String router; + final int activeIndex; + final int total; + + DisplayState({ + required this.router, + required this.activeIndex, + required this.total, + }); + + DisplayFrame get frame { + return kDisplayMap[router]![activeIndex]; + } + + DisplayState copyWith({ + int? total, + int? activeIndex, + String? router, + }) => + DisplayState( + router: router ?? this.router, + activeIndex: activeIndex ?? this.activeIndex, + total: total ?? this.total, + ); +} diff --git a/modules/knowledge_system/layout/lib/src/data/display_map/base.dart b/modules/knowledge_system/layout/lib/src/data/display_map/base.dart new file mode 100644 index 000000000..aed627644 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/data/display_map/base.dart @@ -0,0 +1,85 @@ +import 'package:flutter/material.dart'; +import 'package:layout/src/views/base/positioned/positioned_show.dart'; + +import '../../views/base/align/align_show.dart'; +import '../../views/base/align/align_show2.dart'; +import '../../views/base/padding/inner_padding.dart'; +import '../../views/base/padding/outer_padding.dart'; +import '../../views/base/padding/sizedbox_padding.dart'; +import '../../views/base/size/size_loss_by_align.dart'; +import '../../views/base/size/size_tight_constraint.dart'; +import '../../views/base/size/size_unconstraint.dart'; +import '../model/display_frame.dart'; + +List get baseSize => [ + DisplayFrame( + title: '父级中的紧约束', + desc: "当前虽然指定 SizeBox 宽高为 150*100。但由于父级的紧约束被强制固定尺寸。" + "下一步,将父级的紧约束改为宽松约束。", + src: '', + display: (BuildContext context) => const SizeTightConstraint( + info: "受到紧约束\n尺寸无法生效", + ), + ), + DisplayFrame( + title: '用布局组件放宽父级约束', + desc: + "通过嵌套 Align、Row、Column、Flex、Scaffold 等组件, 提供一个宽松的父级约束,让 SizeBox 指定的尺寸可以生效" + "就可以生效。", + src: '', + display: (BuildContext context) => const LossDisplay(), + ), + DisplayFrame( + title: '用 UnconstrainedBox 组件解除约束', + desc: "通过嵌套 UnconstrainedBox 组件,可以解除之前父级对当前区域的约束,从而使指定尺寸生效。", + src: '', + display: (BuildContext context) => const SizeUnconstrain(), + ), + ]; + +List get basePadding => [ + DisplayFrame( + title: 'Padding 实现内边距', + desc: "将色块区域视为边界,文字距离边界有一定的内边距。", + src: '', + display: (BuildContext context) => const InnerPadding(), + ), + DisplayFrame( + title: 'Padding 实现外边距', + desc: + "两个色块区域之间,右侧可以通过 Padding 嵌套,距离外部有边距。Container 的 margin 属性就是这个原理。", + src: '', + display: (BuildContext context) => const OuterPadding(), + ), + DisplayFrame( + title: 'SizedBox 实现边距', + desc: + "有时在行列布局中,可以通过空白的 SizedBox 组件进行站位,来简单地实现边距效果。", + src: '', + display: (BuildContext context) => const SizedBoxPadding(), + ), +]; + +List get baseAlign => [ + DisplayFrame( + title: 'Align 组件实现对齐', + desc: "Align 组件可以在自身区域内(示例中灰色),对子组件(蓝色)进行对齐定位。", + src: '', + display: (BuildContext context) => const AlignShow(), + ), + DisplayFrame( + title: 'Align 实现 sin 排布', + desc: "由于Alignment对象可指定在父容器中宽高的分率位置,可以使用Align实现一些复杂的排布需求,比如按指定的数学方程变化位置。", + src: '', + display: (BuildContext context) => const AlignShow2(), + ), +]; + +List get basePostioned => [ + DisplayFrame( + title: 'Positioned 组件实现定位', + desc: "Positioned 组件可以在 Stack 组件内,对子组件指定位置(左上右下)进行定位布局。", + src: '', + display: (BuildContext context) => const PositionedShow(), + ), +]; \ No newline at end of file diff --git a/modules/knowledge_system/layout/lib/src/data/display_map/display_map.dart b/modules/knowledge_system/layout/lib/src/data/display_map/display_map.dart new file mode 100644 index 000000000..7ed673381 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/data/display_map/display_map.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; + +import '../../views/base/size/size_loss_by_align.dart'; +import '../../views/base/size/size_tight_constraint.dart'; +import '../../views/base/size/size_unconstraint.dart'; +import '../../views/popable/autocomplete_demo.dart'; +import '../../views/popable/dropdown_button_demo.dart'; +import '../../views/popable/dropdown_menu_demo.dart'; +import '../model/display_frame.dart'; +import 'base.dart'; +import 'funny.dart'; +import 'multi.dart'; + +Map> get kDisplayMap => { + '/base/size': baseSize, + '/base/padding': basePadding, + '/base/align': baseAlign, + '/base/positioned': basePostioned, + + '/multi/flex': multiFlex, + '/multi/wrap': multiWrap, + '/multi/stack': multiStack, + + '/scroll/list': listView, + '/scroll/grid': gridView, + '/scroll/page': pageView, + + '/funny/elevator':funnyElevator, + + // '/popable/DropdownButton': [ + // DisplayFrame( + // title: '下拉按钮 DropdownButton', + // desc: + // "Material 风格的下拉选择按钮。基于 Navigator 导航实现,推入 _DropdownRoute 路由,所以点击外部区域时,弹框消失,且外部无法响应该次点击事件。视图构建逻辑非常固定,可定制性低。条目一次性完全加载,不适合海量条目。", + // src: '', + // display: (BuildContext context) => const CustomDropDownButton(), + // ), + // ], + // '/popable/DropdownMenu': [ + // DisplayFrame( + // title: '下拉按钮 DropdownMenu', + // desc: + // "Material 风格的下拉选择按钮。基于 Navigator 导航实现,推入 _DropdownRoute 路由,所以点击外部区域时,弹框消失,且外部无法响应该次点击事件。视图构建逻辑非常固定,可定制性低。条目一次性完全加载,不适合海量条目。", + // src: '', + // display: (BuildContext context) => const DropdownMenuNode1(), + // ), + // ], + // '/popable/Autocomplete': [ + // DisplayFrame( + // title: '自动填充 Autocomplete', + // desc: + // "Material 风格的下拉选择按钮。基于 Navigator 导航实现,推入 _DropdownRoute 路由,所以点击外部区域时,弹框消失,且外部无法响应该次点击事件。视图构建逻辑非常固定,可定制性低。条目一次性完全加载,不适合海量条目。", + // src: '', + // display: (BuildContext context) => const AutocompleteDemo(), + // ), + // ], +}; \ No newline at end of file diff --git a/modules/knowledge_system/layout/lib/src/data/display_map/funny.dart b/modules/knowledge_system/layout/lib/src/data/display_map/funny.dart new file mode 100644 index 000000000..5df2be118 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/data/display_map/funny.dart @@ -0,0 +1,21 @@ +// Copyright 2014 The 星星 . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Author: 星星 +// CreateTime: 2024-07-02 +// Contact Me: 1981462002@qq.com + +import 'package:flutter/cupertino.dart'; + +import '../../views/interest/elevator/elevator.dart'; +import '../model/display_frame.dart'; + +List get funnyElevator => [ + DisplayFrame( + title: '电梯布局', + desc: "模拟完成电梯的运行。", + src: '', + display: (BuildContext context) => ElevatorRoom(), + ), +]; diff --git a/modules/knowledge_system/layout/lib/src/data/display_map/multi.dart b/modules/knowledge_system/layout/lib/src/data/display_map/multi.dart new file mode 100644 index 000000000..e16a20229 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/data/display_map/multi.dart @@ -0,0 +1,86 @@ +import 'package:flutter/material.dart'; +import 'package:layout/src/views/base/positioned/positioned_show.dart'; +import 'package:layout/src/views/playground/view/stack/stack_playground.dart'; + +import '../../views/base/align/align_show.dart'; +import '../../views/base/align/align_show2.dart'; +import '../../views/base/padding/inner_padding.dart'; +import '../../views/base/padding/outer_padding.dart'; +import '../../views/base/padding/sizedbox_padding.dart'; +import '../../views/base/size/size_loss_by_align.dart'; +import '../../views/base/size/size_tight_constraint.dart'; +import '../../views/base/size/size_unconstraint.dart'; +import '../../views/multi/flex/column_show.dart'; +import '../../views/playground/view/flex/flex_playground.dart'; +import '../../views/multi/flex/row_show.dart'; +import '../../views/playground/view/wrap/wrap_playground.dart'; +import '../../views/scroll/grid_view/grid_view_demo01.dart'; +import '../../views/scroll/list_view/list_view_demo01.dart'; +import '../../views/scroll/page_view/page_view_demo01.dart'; +import '../model/display_frame.dart'; + +List get multiFlex => [ + DisplayFrame( + title: 'Flex PlayGround', + desc: "在 Flex PlayGround 中,你可以通过交互来直观体验 Flex 布局特性。灰色是 Flex 布局区域。", + src: '', + display: (BuildContext context) => const FlexPlayground(), + ), + DisplayFrame( + title: 'Row 组件横向排列', + desc: "可以将若干个组件横向排列,区域宽度无上限约束,子组件总宽超过时会越界异常。详细布局特性见第三页: Flex PlayGround", + src: '', + display: (BuildContext context) => const RowShow(), + ), + DisplayFrame( + title: 'Column 组件横向排列', + desc: "可以将若干个组件竖向排列,区域高度无上限约束,子组件总高超过时会越界异常。详细布局特性见第三页: Flex PlayGround", + src: '', + display: (BuildContext context) => const ColumnShow(), + ), + ]; + +List get multiWrap => [ + DisplayFrame( + title: 'Wrap PlayGround', + desc: "在 Wrap PlayGround 中,你可以通过交互来直观体验 Wrap 布局特性。", + src: '', + display: (BuildContext context) => WrapPlayground(), + ), + ]; + +List get multiStack => [ + DisplayFrame( + title: 'Stack PlayGround', + desc: "在 Stack PlayGround 中,你可以通过交互来直观体验 Stack 布局特性。", + src: '', + display: (BuildContext context) => StackPlayground(), + ), +]; + +List get listView => [ + DisplayFrame( + title: 'ListView 滑动列表', + desc: "通过 ListView.builder 构造,可以实现按需加载的滑动视图。", + src: '', + display: (BuildContext context) => ListViewDemo01(), + ), +]; + +List get gridView => [ + DisplayFrame( + title: 'GridView 滑动网格', + desc: "通过 GridView.builder 构造,可以实现按需加载的网格滑动视图。", + src: '', + display: (BuildContext context) => GridViewDemo01(), + ), +]; + +List get pageView => [ + DisplayFrame( + title: 'PageView 滑动界面', + desc: "通过 GridView.builder 构造,可以实现按需加载的网格滑动视图。", + src: '', + display: (BuildContext context) => PageViewDemo01(), + ), +]; diff --git a/modules/knowledge_system/layout/lib/src/data/model/display_frame.dart b/modules/knowledge_system/layout/lib/src/data/model/display_frame.dart new file mode 100644 index 000000000..b6741c8a7 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/data/model/display_frame.dart @@ -0,0 +1,24 @@ +import 'package:flutter/cupertino.dart'; + +import '../../views/base/size/size_tight_constraint.dart'; +import '../../views/base/size/size_loss_by_align.dart'; +import '../../views/base/size/size_unconstraint.dart'; +import '../../views/popable/autocomplete_demo.dart'; +import '../../views/popable/dropdown_button_demo.dart'; +import '../../views/popable/dropdown_menu_demo.dart'; + +class DisplayFrame { + final String title; + final String desc; + final String src; + final WidgetBuilder display; + + DisplayFrame({ + required this.title, + required this.desc, + required this.src, + required this.display, + }); +} + + diff --git a/modules/knowledge_system/layout/lib/src/ext/go_router/listener.dart b/modules/knowledge_system/layout/lib/src/ext/go_router/listener.dart new file mode 100644 index 000000000..d0db8c4cb --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/ext/go_router/listener.dart @@ -0,0 +1,36 @@ +// Copyright 2014 The 张风捷特烈 . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Author: 张风捷特烈 +// CreateTime: 2024-05-25 +// Contact Me: 1981462002@qq.com + +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +mixin RouterChangeListenerMixin on State { + late GoRouterDelegate _delegate; + + String get path => '/${_delegate.currentConfiguration.matches.last.matchedLocation}'; + + @override + void initState() { + super.initState(); + _delegate = GoRouter.of(context).routerDelegate; + _delegate.addListener(_onChange); + } + + @override + void dispose() { + _delegate.removeListener(_onChange); + super.dispose(); + } + + void _onChange() { + RouteMatchBase match = _delegate.currentConfiguration.matches.last; + onChangeRoute("/${match.matchedLocation}"); + } + + void onChangeRoute(String path); +} diff --git a/modules/knowledge_system/layout/lib/src/ext/go_router/path.dart b/modules/knowledge_system/layout/lib/src/ext/go_router/path.dart new file mode 100644 index 000000000..89442b5f5 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/ext/go_router/path.dart @@ -0,0 +1,14 @@ +// Copyright 2014 The 张风捷特烈 . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Author: 张风捷特烈 +// CreateTime: 2024-05-25 +// Contact Me: 1981462002@qq.com + +import 'package:go_router/go_router.dart'; + +extension GoRouterPath on GoRouter{ + String get path => '/${routerDelegate.currentConfiguration.matches.last.matchedLocation}'; +} + diff --git a/modules/knowledge_system/layout/lib/src/navigation/menu/base_layout.dart b/modules/knowledge_system/layout/lib/src/navigation/menu/base_layout.dart new file mode 100644 index 000000000..df3f5da78 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/navigation/menu/base_layout.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +// import 'package:iroute/app/res/fx_icon.dart'; + +Map get baseMenus => { + 'path': '/base', + 'icon': Icons.layers_rounded, + 'label': '基本布局', + 'children': [ + { + 'path': '/size', + 'label': '布局尺寸', + }, + { + 'path': '/padding', + 'label': '布局边距', + }, + { + 'path': '/align', + 'label': '布局对齐', + }, + { + 'path': '/positioned', + 'label': '布局定位', + }, + ] + }; diff --git a/modules/knowledge_system/layout/lib/src/navigation/menu/funny.dart b/modules/knowledge_system/layout/lib/src/navigation/menu/funny.dart new file mode 100644 index 000000000..028a6cb74 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/navigation/menu/funny.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; + +// import 'package:iroute/app/res/fx_icon.dart'; +// { +// 'path': '/expanded', +// 'label': '延展布局', +// // 'icon': Icons.text_fields, +// }, +// { +// 'path': '/holy', +// 'label': '圣杯布局', +// // 'icon': Icons.text_fields, +// }, +Map get funnyMenus => { + 'path': '/funny', + 'icon': Icons.multitrack_audio, + 'label': '趣味布局', + 'children': [ + { + 'path': '/elevator', + 'label': '电梯布局', + }, + + ] + }; diff --git a/modules/knowledge_system/layout/lib/src/navigation/menu/layout.dart b/modules/knowledge_system/layout/lib/src/navigation/menu/layout.dart new file mode 100644 index 000000000..501a56e63 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/navigation/menu/layout.dart @@ -0,0 +1,23 @@ + +// import '../../../app/res/fx_icon.dart'; + +import 'package:flutter/material.dart'; + +Map home = { + 'path': '/home', + 'label': '布局总览', + 'icon': Icons.dashboard + + // 'children': [ + // { + // 'icon': Icons.home, + // 'path': '/home', + // 'label': '首页', + // }, + // { + // 'path': '/collect', + // 'label': '我的收藏', + // 'icon': Icons.collections_bookmark_outlined, + // }, + // ], +}; \ No newline at end of file diff --git a/modules/knowledge_system/layout/lib/src/navigation/menu/menu_repository_impl.dart b/modules/knowledge_system/layout/lib/src/navigation/menu/menu_repository_impl.dart new file mode 100644 index 000000000..3608b3052 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/navigation/menu/menu_repository_impl.dart @@ -0,0 +1,15 @@ +import 'base_layout.dart'; +import 'funny.dart'; +import 'scroll.dart'; +import 'layout.dart'; +import 'multi.dart'; + +Map get layoutMenus => { + 'children': [ + home, + baseMenus, + multiMenus, + calcMenus, + funnyMenus, + ] + }; diff --git a/modules/knowledge_system/layout/lib/src/navigation/menu/multi.dart b/modules/knowledge_system/layout/lib/src/navigation/menu/multi.dart new file mode 100644 index 000000000..bccec3a3d --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/navigation/menu/multi.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; + +// import 'package:iroute/app/res/fx_icon.dart'; +// { +// 'path': '/expanded', +// 'label': '延展布局', +// // 'icon': Icons.text_fields, +// }, +// { +// 'path': '/holy', +// 'label': '圣杯布局', +// // 'icon': Icons.text_fields, +// }, +Map get multiMenus => { + 'path': '/multi', + 'icon': Icons.multitrack_audio, + 'label': '多子布局', + 'children': [ + { + 'path': '/flex', + 'label': 'Flex 适应布局', + }, + { + 'path': '/wrap', + 'label': 'Wrap 包裹布局', + }, + { + 'path': '/stack', + 'label': 'Stack 堆叠布局', + }, + ] + }; diff --git a/modules/knowledge_system/layout/lib/src/navigation/menu/popable.dart b/modules/knowledge_system/layout/lib/src/navigation/menu/popable.dart new file mode 100644 index 000000000..e969f68cd --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/navigation/menu/popable.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; +// import 'package:iroute/app/res/fx_icon.dart'; + +Map get popableMenus => { + 'path': '/popable', + 'icon': Icons.layers_rounded, + 'label': '菜单浮层', + 'children': [ + { + 'path': '/DropdownButton', + 'label': '下拉按钮', + }, + { + 'path': '/DropdownMenu', + 'label': '下拉菜单', + // 'icon': Icons.calculate_outlined, + }, + { + 'path': '/Autocomplete', + 'label': '自动填充', + // 'icon': Icons.calculate_outlined, + }, + ] +}; diff --git a/modules/knowledge_system/layout/lib/src/navigation/menu/scroll.dart b/modules/knowledge_system/layout/lib/src/navigation/menu/scroll.dart new file mode 100644 index 000000000..cef50dead --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/navigation/menu/scroll.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; +// import 'package:iroute/app/res/fx_icon.dart'; + +Map get calcMenus => { + 'path': '/scroll', + 'icon': Icons.touch_app_outlined, + 'label': '滑动布局', + 'children': [ + { + 'path': '/list', + 'label': '列表布局', + // 'icon': Icons.list_alt, + }, + { + 'path': '/grid', + 'label': '网格布局', + // 'icon': Icons.grid_on_sharp, + }, + { + 'path': '/page', + 'label': '滑页布局', + // 'icon': Icons.grid_on_sharp, + }, + ] +}; diff --git a/modules/knowledge_system/layout/lib/src/navigation/router/app_router.dart b/modules/knowledge_system/layout/lib/src/navigation/router/app_router.dart new file mode 100644 index 000000000..ebdc49ad0 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/navigation/router/app_router.dart @@ -0,0 +1,29 @@ + +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + + +import '../../../layout.dart'; +import 'desk_router.dart'; + + + + RouteBase get layoutRoutes => GoRoute( + path: '/', + redirect: (_, __) => null, + routes: [ + + + // GoRoute( + // path: 'start_error', + // builder: (BuildContext context, GoRouterState state) { + // return AppStartFixListener( + // child: ErrorPage( + // error: state.extra.toString(), + // ), + // ); + // }, + // ), + deskNavRoute + ], +); diff --git a/modules/knowledge_system/layout/lib/src/navigation/router/desk_router.dart b/modules/knowledge_system/layout/lib/src/navigation/router/desk_router.dart new file mode 100644 index 000000000..a92d62023 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/navigation/router/desk_router.dart @@ -0,0 +1,113 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +import '../../../layout.dart'; +import '../../views/base/size/size_tight_constraint.dart'; +import '../../views/base/size/size_display.dart'; +import '../../views/test_show.dart'; +import '../menu/menu_repository_impl.dart'; +import '../view/app_desk_navigation.dart'; + +RouteBase get deskNavRoute => ShellRoute( + builder: (BuildContext context, GoRouterState state, Widget child) { + return AppDeskNavigation(content: child); + }, + routes: [ + GoRoute( + path: 'home', + builder: (BuildContext context, GoRouterState state) { + return const LayoutPage(); + }, + ), + GoRoute( + path: 'base/:name', + builder: (BuildContext context, GoRouterState state) { + return const FrameDisplayPanel(); + }, + ), + GoRoute( + path: 'scroll/:name', + builder: (BuildContext context, GoRouterState state) { + return const FrameDisplayPanel(); + }, + ), + GoRoute( + path: 'popable/:name', + builder: (BuildContext context, GoRouterState state) { + return const FrameDisplayPanel(); + }, + ), + GoRoute( + path: 'multi/:name', + builder: (BuildContext context, GoRouterState state) { + return const FrameDisplayPanel(); + }, + ), + GoRoute( + path: 'funny/:name', + builder: (BuildContext context, GoRouterState state) { + return const FrameDisplayPanel(); + }, + // builder: (_,__)=>Text("暂未实现"), + // routes: [ + // GoRoute( + // path: 'row', + // builder: (BuildContext context, GoRouterState state) { + // return TextShow( + // info: 'row', + // ); + // }, + // ), + // GoRoute( + // path: 'column', + // builder: (BuildContext context, GoRouterState state) { + // return TextShow( + // info: 'column', + // ); + // }, + // ), GoRoute( + // path: 'expanded', + // builder: (BuildContext context, GoRouterState state) { + // return TextShow( + // info: 'expanded', + // ); + // }, + // ), + // GoRoute( + // path: 'holy', + // builder: (BuildContext context, GoRouterState state) { + // return TextShow( + // info: 'holy', + // ); + // }, + // ), + // ] + ), + // GoRoute( + // path: 'text/gen/secret', + // builder: (BuildContext context, GoRouterState state) { + // return SecretGenPage(); + // }, + // ), + // GoRoute( + // path: 'text/:name', + // pageBuilder: (BuildContext context, GoRouterState state) { + // return CustomTransitionPage( + // key: ValueKey(state.uri.path), + // // transitionDuration: const Duration(milliseconds: 500), + // // reverseTransitionDuration: const Duration(milliseconds: 500), + // child: ToolListPanel( + // key: ValueKey(state.fullPath??''), + // name: state.pathParameters['name'] ?? ''), + // transitionsBuilder: (BuildContext context, + // Animation animation, + // Animation secondaryAnimation, + // Widget child) { + // return FadeTransition( + // opacity: animation.drive(CurveTween(curve: Curves.easeIn)), + // child: child, + // ); + // }); + // }, + // ), + ]); diff --git a/modules/knowledge_system/layout/lib/src/navigation/router/transition/fade_page_transitions_builder.dart b/modules/knowledge_system/layout/lib/src/navigation/router/transition/fade_page_transitions_builder.dart new file mode 100644 index 000000000..d0e9e3f04 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/navigation/router/transition/fade_page_transitions_builder.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; + +class FadePageTransitionsBuilder extends PageTransitionsBuilder { + const FadePageTransitionsBuilder(); + + @override + Widget buildTransitions( + PageRoute? route, + BuildContext? context, + Animation animation, + Animation secondaryAnimation, + Widget child, + ) { + return FadeTransition( + opacity: animation.drive(CurveTween(curve: Curves.easeIn)), + child: child, + ); + } +} diff --git a/packages/app/lib/app/router/fade_page_route.dart b/modules/knowledge_system/layout/lib/src/navigation/router/transition/page_route/fade_page_route.dart similarity index 94% rename from packages/app/lib/app/router/fade_page_route.dart rename to modules/knowledge_system/layout/lib/src/navigation/router/transition/page_route/fade_page_route.dart index 515618ad5..01b5015f6 100644 --- a/packages/app/lib/app/router/fade_page_route.dart +++ b/modules/knowledge_system/layout/lib/src/navigation/router/transition/page_route/fade_page_route.dart @@ -3,7 +3,8 @@ import 'dart:io'; import 'package:flutter/material.dart'; -import 'cupertino_back_gesture_detector.dart'; +import '../slide_transition/cupertino_back_gesture_detector.dart'; + class FadePageRoute extends MaterialPageRoute { final Widget child; diff --git a/packages/app/lib/app/router/slide_page_route.dart b/modules/knowledge_system/layout/lib/src/navigation/router/transition/page_route/slide_page_route.dart similarity index 94% rename from packages/app/lib/app/router/slide_page_route.dart rename to modules/knowledge_system/layout/lib/src/navigation/router/transition/page_route/slide_page_route.dart index 45386e7ce..2cb92871a 100644 --- a/packages/app/lib/app/router/slide_page_route.dart +++ b/modules/knowledge_system/layout/lib/src/navigation/router/transition/page_route/slide_page_route.dart @@ -4,7 +4,8 @@ import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'cupertino_back_gesture_detector.dart'; +import '../slide_transition/cupertino_back_gesture_detector.dart'; + class SlidePageRoute extends MaterialPageRoute { diff --git a/packages/app/lib/app/router/zero_page_route.dart b/modules/knowledge_system/layout/lib/src/navigation/router/transition/page_route/zero_page_route.dart similarity index 90% rename from packages/app/lib/app/router/zero_page_route.dart rename to modules/knowledge_system/layout/lib/src/navigation/router/transition/page_route/zero_page_route.dart index 5636c95a9..a6c0ca8bf 100644 --- a/packages/app/lib/app/router/zero_page_route.dart +++ b/modules/knowledge_system/layout/lib/src/navigation/router/transition/page_route/zero_page_route.dart @@ -1,9 +1,10 @@ -//渐变透明路由动画 + import 'dart:io'; import 'package:flutter/material.dart'; -import 'cupertino_back_gesture_detector.dart'; +import '../slide_transition/cupertino_back_gesture_detector.dart'; + class ZeroPageRoute extends MaterialPageRoute { final Widget child; @@ -27,7 +28,6 @@ class ZeroPageRoute extends MaterialPageRoute { child: child, ); } - return child; } diff --git a/modules/knowledge_system/layout/lib/src/navigation/router/transition/size_clip_transition.dart b/modules/knowledge_system/layout/lib/src/navigation/router/transition/size_clip_transition.dart new file mode 100644 index 000000000..f3fa0f5ca --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/navigation/router/transition/size_clip_transition.dart @@ -0,0 +1,105 @@ +import 'dart:math'; + +import 'package:flutter/cupertino.dart'; + +class SizeClipTransition extends StatelessWidget { + final Animation animation; + final Animation secondaryAnimation; + final Widget child; + + const SizeClipTransition({ + super.key, + required this.animation, + required this.secondaryAnimation, + required this.child, + }); + + @override + Widget build(BuildContext context) { + return ClipPath( + clipper: CirclePathClipper(Curves.easeIn.transform(animation.value)), + child: child, + ); + } +} + +class SizePathClipper extends CustomClipper { + final double progress; + + SizePathClipper(this.progress); + + @override + Path getClip(Size size) { + Rect box = Rect.fromLTWH(0, 0, size.width, size.height); + Rect center = Rect.fromCenter( + center: Offset(size.width / 2, size.height / 2), + width: size.width * (1 - progress), + height: size.height, + ); + + return Path() + ..addRect(box) + ..addRect(center) + ..fillType = PathFillType.evenOdd; + } + + @override + bool shouldReclip(covariant SizePathClipper oldClipper) { + return oldClipper.progress != progress; + } +} + +class ScalePathClipper extends CustomClipper { + final double progress; + + ScalePathClipper(this.progress); + + @override + Path getClip(Size size) { + Rect box = Rect.fromLTWH(0, 0, size.width, size.height); + Rect center = Rect.fromCenter( + center: Offset(size.width / 2, size.height / 2), + width: size.width * (1 - progress), + height: size.height* (1 - progress), + ); + + return Path() + ..addRect(box) + ..addRect(center) + ..fillType = PathFillType.evenOdd; + } + + @override + bool shouldReclip(covariant ScalePathClipper oldClipper) { + return oldClipper.progress != progress; + } +} + +class CirclePathClipper extends CustomClipper { + final double progress; + + CirclePathClipper(this.progress); + + @override + Path getClip(Size size) { + print('progress:$progress'); + if(progress==0){ + return Path(); + } + Rect box = Rect.fromLTWH(0, 0, size.width, size.height); + Rect center = Rect.fromCircle( + center: Offset(size.width , 0), + radius: sqrt(size.width*size.width+size.height*size.height) * (progress), + ); + + Path zone = Path()..addRect(box); + Path cliper = Path()..addOval(center); + + return Path.combine(PathOperation.intersect, zone, cliper ); + } + + @override + bool shouldReclip(covariant CirclePathClipper oldClipper) { + return oldClipper.progress != progress; + } +} \ No newline at end of file diff --git a/packages/app/lib/app/router/cupertino_back_gesture_detector.dart b/modules/knowledge_system/layout/lib/src/navigation/router/transition/slide_transition/cupertino_back_gesture_detector.dart similarity index 100% rename from packages/app/lib/app/router/cupertino_back_gesture_detector.dart rename to modules/knowledge_system/layout/lib/src/navigation/router/transition/slide_transition/cupertino_back_gesture_detector.dart diff --git a/modules/knowledge_system/layout/lib/src/navigation/router/transition/slide_transition/slide_page_transition_builder.dart b/modules/knowledge_system/layout/lib/src/navigation/router/transition/slide_transition/slide_page_transition_builder.dart new file mode 100644 index 000000000..fd027cd44 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/navigation/router/transition/slide_transition/slide_page_transition_builder.dart @@ -0,0 +1,33 @@ +import 'dart:io'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +import 'cupertino_back_gesture_detector.dart'; + +class SlidePageTransitionsBuilder extends PageTransitionsBuilder { + const SlidePageTransitionsBuilder(); + + @override + Widget buildTransitions( + PageRoute? route, + BuildContext? context, + Animation animation, + Animation secondaryAnimation, + Widget child, + ) { + if (Platform.isIOS) { + child = CupertinoBackGestureDetector( + enabledCallback: () => isPopGestureEnabled(route!), + onStartPopGesture: () => startPopGesture(route!), + child: child); + } + final bool linearTransition = isPopGestureInProgress(route!); + return CupertinoPageTransition( + primaryRouteAnimation: animation, + secondaryRouteAnimation: secondaryAnimation, + linearTransition: linearTransition, + child: child, + ); + } +} \ No newline at end of file diff --git a/modules/knowledge_system/layout/lib/src/navigation/view/app_desk_navigation.dart b/modules/knowledge_system/layout/lib/src/navigation/view/app_desk_navigation.dart new file mode 100644 index 000000000..7ea382dd0 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/navigation/view/app_desk_navigation.dart @@ -0,0 +1,27 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:tolyui/tolyui.dart'; + +import '../../views/display/layout_playground.dart'; +import 'app_menu_tree.dart'; + +class AppDeskNavigation extends StatelessWidget { + final Widget content; + + const AppDeskNavigation({super.key, required this.content}); + + @override + Widget build(BuildContext context) { + Color backgroundColor = context.isDark ? Color(0xff001529) : Colors.white; + + return Scaffold( + backgroundColor: backgroundColor, + body: Row( + children: [ + const AppMenuTree(), + Expanded(child: LayoutPlayGround(content: content)), + ], + ), + ); + } +} diff --git a/modules/knowledge_system/layout/lib/src/navigation/view/app_menu_tree.dart b/modules/knowledge_system/layout/lib/src/navigation/view/app_menu_tree.dart new file mode 100644 index 000000000..3242d3d75 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/navigation/view/app_menu_tree.dart @@ -0,0 +1,85 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:tolyui/tolyui.dart'; + +import '../../bloc/display_logic.dart'; +import '../../ext/go_router/listener.dart'; +import '../menu/menu_repository_impl.dart'; + +class AppMenuTree extends StatefulWidget { + const AppMenuTree({super.key}); + + @override + State createState() => _AppMenuTreeState(); +} + +class _AppMenuTreeState extends State with RouterChangeListenerMixin { + late MenuTreeMeta _menuMeta; + + @override + void initState() { + super.initState(); + _initTreeMeta(); + } + + void _initTreeMeta() { + MenuNode root = MenuNode.fromMap(layoutMenus); + List parts = Uri.parse(path).pathSegments; + String parentPath = parts.sublist(0, parts.length - 1).join('/'); + _menuMeta = MenuTreeMeta( + expandMenus: ['/$parentPath'], + activeMenu: root.find(path), + root: root, + ); + } + + @override + Widget build(BuildContext context) { + Color expandBackgroundColor = context.isDark ? Colors.black : Colors.transparent; + Color backgroundColor = context.isDark ? Color(0xff001529) : Colors.white; + + return TolyRailMenuTree( + leading: const SizedBox( + height: 18, + ), + enableWidthChange: true, + maxWidth: 360, + width: 190, + meta: _menuMeta, + backgroundColor: backgroundColor, + expandBackgroundColor: expandBackgroundColor, + onSelect: _onSelect, + ); + } + + void _onMenuRouterChange(BuildContext context, String? path) { + if (path != null) { + context.go(path); + DisplayScope.of(context).active(path); + } + } + + void _onSelect(MenuNode menu) { + if (menu.isLeaf) { + context.go(menu.id); + print(path); + } else { + _menuMeta = _menuMeta.select(menu, singleExpand: true); + setState(() {}); + } + } + + @override + void reassemble() { + MenuNode root = MenuNode.fromMap(layoutMenus); + _menuMeta = _menuMeta.copyWith(root: root); + super.reassemble(); + } + + @override + void onChangeRoute(String path) { + _menuMeta = _menuMeta.selectPath(path, singleExpand: true); + DisplayScope.of(context).active(path); + setState(() {}); + } +} diff --git a/modules/knowledge_system/layout/lib/src/views/base/align/align_show.dart b/modules/knowledge_system/layout/lib/src/views/base/align/align_show.dart new file mode 100644 index 000000000..af3eae267 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/base/align/align_show.dart @@ -0,0 +1,62 @@ +import 'package:flutter/material.dart'; + +class AlignShow extends StatelessWidget { + const AlignShow({super.key}); + + final List alignments = const [ + Alignment.topLeft, + Alignment.topCenter, + Alignment.topRight, + Alignment.centerLeft, + Alignment.center, + Alignment.centerRight, + Alignment.bottomLeft, + Alignment.bottomCenter, + Alignment.bottomRight, + ]; + + final List alignmentsInfo = const [ + "topLeft", + "topCenter", + "topRight", + "centerLeft", + "center", + "centerRight", + "bottomLeft", + "bottomCenter", + "bottomRight", + ]; + + @override + Widget build(BuildContext context) { + TextStyle style = TextStyle( + fontWeight: FontWeight.bold, + color: Colors.grey, + fontSize: 12 + ); + return Center( + child: Wrap( + children: alignments + .toList() + .map((mode) => Column(children: [ + Container( + margin: const EdgeInsets.all(5), + width: 150, + height: 100, + color: Colors.grey.withAlpha(88), + child: Align( + child: Container( + width: 30, + height: 30, + color: Colors.cyanAccent, + ), + alignment: mode)), + Text( + alignmentsInfo[alignments.indexOf(mode)], + style: style, + ) + ])) + .toList()), + ); + } +} diff --git a/modules/knowledge_system/layout/lib/src/views/base/align/align_show2.dart b/modules/knowledge_system/layout/lib/src/views/base/align/align_show2.dart new file mode 100644 index 000000000..508c768c3 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/base/align/align_show2.dart @@ -0,0 +1,75 @@ +import 'dart:math'; +import 'package:flutter/material.dart'; +class Ball extends StatelessWidget { + const Ball({ + super.key, + this.radius = 15, + this.color = Colors.blue, + }); + final double radius; //半径 + final Color color; //颜色 + + @override + Widget build(BuildContext context) { + return Container( + width: radius * 2, + height: radius * 2, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: color, + ), + ); + } +} + +class AlignShow2 extends StatefulWidget { + const AlignShow2({ + super.key, + }); + + @override + _AlignShow2State createState() => _AlignShow2State(); +} + +class _AlignShow2State extends State { + double _x = 0.0; //Alignment坐标系上的x坐标 + + @override + Widget build(BuildContext context) { + var item = Container( + width: 300, + height: 120, + color: Colors.black.withAlpha(10), + child: Align( + child: const Ball( + color: Colors.orangeAccent, + ), + alignment: Alignment(_x, f(_x * pi)), + ), + ); + + var slider = SizedBox( + width: 320, + child: Slider( + + max: 180, + min: -180, + divisions: 360, + label: "${_x.toStringAsFixed(2)}π", + value: _x * 180, + onChanged: (v) => setState(() => _x = v / 180)), + ); + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [slider, item], + ), + ); + } + + double f(x) { + //映射函数 -- 可随意指定 + double y = sin(x); + return y; + } +} diff --git a/modules/knowledge_system/layout/lib/src/views/base/padding/inner_padding.dart b/modules/knowledge_system/layout/lib/src/views/base/padding/inner_padding.dart new file mode 100644 index 000000000..789511ba9 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/base/padding/inner_padding.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; + +class InnerPadding extends StatelessWidget { + const InnerPadding({super.key}); + + @override + Widget build(BuildContext context) { + Color? color = Theme.of(context).primaryColor; + TextStyle? style = const TextStyle(color: Colors.white); + return Center( + child: ColoredBox( + color: color, + child: SizedBox( + width: 200, + height: 80, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 12.0,vertical: 8), + child: Text("张风捷特烈 " * 10, style: style), + ), + ), + ), + ); + } +} \ No newline at end of file diff --git a/modules/knowledge_system/layout/lib/src/views/base/padding/outer_padding.dart b/modules/knowledge_system/layout/lib/src/views/base/padding/outer_padding.dart new file mode 100644 index 000000000..9bf10ed6a --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/base/padding/outer_padding.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; + +import 'inner_padding.dart'; + +class OuterPadding extends StatelessWidget { + const OuterPadding({super.key}); + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + InnerPadding(), + Padding( + padding: const EdgeInsets.only(left: 24.0), + child: InnerPadding(), + ), + ], + ); + } +} diff --git a/modules/knowledge_system/layout/lib/src/views/base/padding/sizedbox_padding.dart b/modules/knowledge_system/layout/lib/src/views/base/padding/sizedbox_padding.dart new file mode 100644 index 000000000..4e3fd5cda --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/base/padding/sizedbox_padding.dart @@ -0,0 +1,20 @@ + +import 'package:flutter/material.dart'; + +import 'inner_padding.dart'; + +class SizedBoxPadding extends StatelessWidget { + const SizedBoxPadding({super.key}); + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + InnerPadding(), + SizedBox(width: 24), + InnerPadding(), + ], + ); + } +} diff --git a/modules/knowledge_system/layout/lib/src/views/base/positioned/positioned_show.dart b/modules/knowledge_system/layout/lib/src/views/base/positioned/positioned_show.dart new file mode 100644 index 000000000..05b7674f5 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/base/positioned/positioned_show.dart @@ -0,0 +1,79 @@ +import 'package:flutter/material.dart'; + +class PositionedShow extends StatefulWidget { + const PositionedShow({super.key}); + + @override + State createState() => _PositionedShowState(); +} + +class _PositionedShowState extends State { + @override + Widget build(BuildContext context) { + Widget yellowBox = Container( + color: Colors.yellow, + height: 100, + width: 100, + ); + + Widget redBox = Container( + color: Colors.red, + height: 90, + width: 90, + ); + + Widget greenBox = DraggedBox( + onPanUpdate: _onPanUpdate, + ); + + Widget cyanBox = Container( + color: Colors.cyanAccent, + height: 70, + width: 70, + ); + + return Container( + color: Colors.grey.withAlpha(33), + child: Stack( + children: [ + yellowBox, + redBox, + Positioned(top: _top, left: _left, child: greenBox), + Positioned(bottom: 20, right: 20, child: cyanBox) + ], + )); + } + + double _top = 20; + double _left = 20; + void _onPanUpdate(DragUpdateDetails details) { + setState(() { + _top += details.delta.dy; + _left += details.delta.dx; + }); + } +} + +class DraggedBox extends StatelessWidget { + final GestureDragUpdateCallback? onPanUpdate; + + const DraggedBox({super.key, this.onPanUpdate}); + + @override + Widget build(BuildContext context) { + return MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + onPanUpdate: onPanUpdate, + child: Container( + color: Colors.green, + height: 80, + width: 80, + ), + ), + ); + } + + +} + diff --git a/modules/knowledge_system/layout/lib/src/views/base/size/size_display.dart b/modules/knowledge_system/layout/lib/src/views/base/size/size_display.dart new file mode 100644 index 000000000..e6a389777 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/base/size/size_display.dart @@ -0,0 +1,20 @@ +import 'package:flutter/material.dart'; + +import '../../../bloc/display_logic.dart'; +import '../../../data/model/display_frame.dart'; + + +class FrameDisplayPanel extends StatelessWidget { + const FrameDisplayPanel({super.key}); + + @override + Widget build(BuildContext context) { + DisplayFrame frame = DisplayScope.of(context).state.frame; + + return Material( + color: Colors.transparent, + elevation: 0, + child: frame.display(context), + ); + } +} diff --git a/modules/knowledge_system/layout/lib/src/views/base/size/size_loss_by_align.dart b/modules/knowledge_system/layout/lib/src/views/base/size/size_loss_by_align.dart new file mode 100644 index 000000000..3d018ca79 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/base/size/size_loss_by_align.dart @@ -0,0 +1,76 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:layout/src/views/components/grid_xy_layout.dart'; + +import 'size_tight_constraint.dart'; + +class LossDisplay extends StatelessWidget { + const LossDisplay({super.key}); + + @override + Widget build(BuildContext context) { + return GridXYLayout( + capacity: (2, 2), + xyBuilder: ((int, int) pos) => switch (pos) { + (0, 0) => const Align(child: SizeTightConstraint(info: 'Align 放宽约束')), + (0, 1) => const Row(children: [SizeTightConstraint(info: 'Row 放宽约束')]), + (1, 0) => const Scaffold( + backgroundColor: Colors.transparent, + body: SizeTightConstraint(info: 'Scaffold 放宽约束'), + ), + (1, 1) => const Column( + children: [SizeTightConstraint(info: 'Column 放宽约束')], + ), + _ => const SizedBox() + }, + ); + } +} + +class SizeLossByAlign extends StatelessWidget { + const SizeLossByAlign({super.key}); + + @override + Widget build(BuildContext context) { + return const Align( + alignment: Alignment.topCenter, + child: ColoredBox( + color: Colors.redAccent, + child: SizedBox( + width: 120, + height: 80, + child: Center( + child: Text( + 'Align 放宽约束', + style: TextStyle(color: Colors.white), + )), + ), + ), + ); + } +} + +class SizeLossByRow extends StatelessWidget { + const SizeLossByRow({super.key}); + + @override + Widget build(BuildContext context) { + return const Row( + children: [ + ColoredBox( + color: Colors.redAccent, + child: SizedBox( + width: 150, + height: 100, + child: Center( + child: Text( + 'Row 放宽约束', + style: TextStyle(color: Colors.white), + )), + ), + ), + ], + ); + } +} diff --git a/modules/knowledge_system/layout/lib/src/views/base/size/size_tight_constraint.dart b/modules/knowledge_system/layout/lib/src/views/base/size/size_tight_constraint.dart new file mode 100644 index 000000000..e129a0d20 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/base/size/size_tight_constraint.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; + +class SizeTightConstraint extends StatelessWidget { + final String info; + + const SizeTightConstraint({super.key, required this.info}); + + @override + Widget build(BuildContext context) { + Color color = Theme.of(context).primaryColor; + return LayoutBuilder( + builder: (ctx,cts)=>ColoredBox( + color: color, + child: SizedBox( + width: 150, + height: 100, + child: Center( + child: Text( + '$info\n当前约束:\n${cts.toString().replaceAll('BoxConstraints', '')}', + style: const TextStyle(color: Colors.white), + textAlign: TextAlign.center, + )), + ), + ), + ); + } +} diff --git a/modules/knowledge_system/layout/lib/src/views/base/size/size_unconstraint.dart b/modules/knowledge_system/layout/lib/src/views/base/size/size_unconstraint.dart new file mode 100644 index 000000000..893b102ca --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/base/size/size_unconstraint.dart @@ -0,0 +1,14 @@ +import 'package:flutter/material.dart'; +import 'package:layout/src/views/base/size/size_tight_constraint.dart'; + +class SizeUnconstrain extends StatelessWidget { + const SizeUnconstrain({super.key}); + + @override + Widget build(BuildContext context) { + return UnconstrainedBox( + child: SizeTightConstraint(info: "通过 UnconstrainedBox 解除约束",), + ); + } +} + diff --git a/modules/knowledge_system/layout/lib/src/views/components/grid_xy_layout.dart b/modules/knowledge_system/layout/lib/src/views/components/grid_xy_layout.dart new file mode 100644 index 000000000..b1a444352 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/components/grid_xy_layout.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; + +typedef XYBuilder = Function((int x, int y)); + +class GridXYLayout extends StatelessWidget { + final (int, int) capacity; + final XYBuilder xyBuilder; + final bool hasLine; + + const GridXYLayout({ + super.key, + this.capacity = (2, 2), + required this.xyBuilder, + this.hasLine = true, + }); + + @override + Widget build(BuildContext context) { + List children = []; + for (int i = 0; i < capacity.$1; i++) { + List columnChildren = []; + for (int j = 0; j < capacity.$2; j++) { + columnChildren.add(Expanded(child: xyBuilder((i, j)))); + if (hasLine && j != capacity.$2 - 1) { + columnChildren.add(const Divider()); + } + } + children.add(Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: columnChildren, + ))); + if (hasLine && i != capacity.$1 - 1) { + children.add(const VerticalDivider()); + } + } + return Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: children, + ); + } +} diff --git a/modules/knowledge_system/layout/lib/src/views/display/layout_playground.dart b/modules/knowledge_system/layout/lib/src/views/display/layout_playground.dart new file mode 100644 index 000000000..72e430789 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/display/layout_playground.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; + +import 'playground_bottom_bar.dart'; +import 'playground_top_bar.dart'; + +class LayoutPlayGround extends StatelessWidget { + final Widget content; + + const LayoutPlayGround({ + super.key, + required this.content, + }); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const PlaygroundTopBar(), + const Divider(), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 24), + child: DecoratedBox( + decoration: BoxDecoration( + border: Border.all(color: Colors.grey.withOpacity(0.4)), + ), + child: content, + ), + )), + const Divider(), + const PlaygroundBottomBar() + ], + ), + ); + } +} diff --git a/modules/knowledge_system/layout/lib/src/views/display/playground_bottom_bar.dart b/modules/knowledge_system/layout/lib/src/views/display/playground_bottom_bar.dart new file mode 100644 index 000000000..0b96227c7 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/display/playground_bottom_bar.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; +import 'package:layout/src/bloc/display_logic.dart'; + +import '../../data/model/display_frame.dart'; + +class PlaygroundBottomBar extends StatelessWidget { + const PlaygroundBottomBar({super.key}); + + @override + Widget build(BuildContext context) { + DisplayFrame frame = DisplayScope.of(context).state.frame; + return Container( + width: double.maxFinite, + color: Color(0xfff2f2f2), + padding: EdgeInsets.symmetric(horizontal: 12, vertical: 4), + // height: 24, + child: Text( + frame.desc, + style: TextStyle(fontSize: 12, fontFamily: '宋体'), + ), + // child: NavigationToolbar( + // centerMiddle: true, + // middle: Text("${menu??'布局测试'}"), + // trailing: IconButton(onPressed: (){ + // + // }, icon: Icon(Icons.code)), + // ), + ); + } +} diff --git a/modules/knowledge_system/layout/lib/src/views/display/playground_top_bar.dart b/modules/knowledge_system/layout/lib/src/views/display/playground_top_bar.dart new file mode 100644 index 000000000..5a893447b --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/display/playground_top_bar.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; +import 'package:tolyui/basic/basic.dart'; + +import '../../bloc/display_logic.dart'; +import '../../bloc/display_state.dart'; +import '../../data/model/display_frame.dart'; + +class PlaygroundTopBar extends StatelessWidget { + const PlaygroundTopBar({super.key}); + + @override + Widget build(BuildContext context) { + DisplayState state = DisplayScope.of(context).state; + DisplayFrame frame = state.frame; + const ActionStyle style = ActionStyle.light(backgroundColor: Color(0xffd5d5d5)); + return Container( + color: const Color(0xfff2f2f2), + padding: const EdgeInsets.symmetric(horizontal: 12), + height: 46, + child: NavigationToolbar( + centerMiddle: true, + leading: UnconstrainedBox( + child: Align( + alignment: Alignment.centerLeft, + child: Text( + '当前: ${state.activeIndex + 1}/${state.total}', + style: const TextStyle(color: Colors.grey, fontWeight: FontWeight.bold), + )), + ), + middle: Text( + frame.title, + style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16), + ), + trailing: Wrap( + children: [ + TolyAction( + style: style, + child: const Icon(Icons.skip_previous, size: 20), + onTap: () { + DisplayScope.of(context).prevPage(); + }), + TolyAction( + style: style, + child: const Icon(Icons.skip_next, size: 20), + onTap: () { + DisplayScope.of(context).nextPage(); + }), + TolyAction(style: style, child: const Icon(Icons.code, size: 20), onTap: () {}), + ], + ), + ), + ); + } +} diff --git a/modules/knowledge_system/layout/lib/src/views/interest/elevator/elevator.dart b/modules/knowledge_system/layout/lib/src/views/interest/elevator/elevator.dart new file mode 100644 index 000000000..1b3818cf2 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/interest/elevator/elevator.dart @@ -0,0 +1,85 @@ +// Copyright 2014 The 星星. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Author: 星星 +// CreateTime: 2024-07-02 +// Contact Me: 1981462002@qq.com + +import 'package:flutter/material.dart'; +import 'package:tolyui/basic/basic.dart'; + +class ElevatorRoom extends StatefulWidget { + const ElevatorRoom({super.key}); + + @override + State createState() => _ElevatorRoomState(); +} + +class _ElevatorRoomState extends State { + int lv = 1 ; + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + double roomHeight = constraints.maxHeight/20; + + return Row( + children: [ + SizedBox( + width: 10, + ), + Stack( + children: [ + Column( + children: List.generate( + 20, + (index) => Container( + decoration: + BoxDecoration(border: Border.all(width: 1, color: Colors.grey)), + width: constraints.minWidth / 10, + height: roomHeight, + )), + ), + Positioned( + bottom: roomHeight * ( lv - 1 ), + child: elevator( width: constraints.minWidth / 10, + height: constraints.maxHeight / 20,), + ), + ], + ), + Column( + children: [ + TolyAction(child: Icon(Icons.arrow_upward_outlined), onTap:upRoom), + TolyAction(child: Icon(Icons.arrow_downward_outlined), onTap:downRoom), + + ], + ), + ], + ); + }, + ); + } + + void upRoom() { + lv+=1; + setState(() { + + }); + } + + void downRoom() { + + lv-=1; + setState(() { + + }); + } + Widget elevator({required double height, required double width}) { + return Container( + height: height, + width: width, + color: Colors.blue, + ); + } +} diff --git a/modules/knowledge_system/layout/lib/src/views/layout_page.dart b/modules/knowledge_system/layout/lib/src/views/layout_page.dart new file mode 100644 index 000000000..1b953d3fb --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/layout_page.dart @@ -0,0 +1,62 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:go_router/go_router.dart'; +import 'package:layout/src/navigation/router/app_router.dart'; + +import '../bloc/display_logic.dart'; +import '../bloc/display_state.dart'; +import '../data/display_map/display_map.dart'; + +class LayoutRouterPage extends StatefulWidget { + LayoutRouterPage({super.key}); + + @override + State createState() => _LayoutRouterPageState(); +} + +class _LayoutRouterPageState extends State { + final GoRouter _router = GoRouter( + initialLocation: '/base/size', + routes: [layoutRoutes], + onException: (BuildContext ctx, GoRouterState state, GoRouter router) { + router.go('/404', extra: state.uri.toString()); + }, + ); + + late final DisplayLogic logic; + + @override + void initState() { + logic = DisplayLogic(DisplayState( + router: '/base/size', + activeIndex: 0, + total: kDisplayMap['/base/size']!.length, + )); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return DisplayScope( + notifier: logic, + child: Column( + children: [ + const Divider(), + Expanded( + child: Router.withConfig(config: _router), + ), + ], + ), + ); + } +} + +class LayoutPage extends StatelessWidget { + const LayoutPage({super.key}); + + @override + Widget build(BuildContext context) { + return Material(color: Colors.white, child: const Center(child: Text("TODO"))); + } +} diff --git a/modules/knowledge_system/layout/lib/src/views/multi/flex/column_show.dart b/modules/knowledge_system/layout/lib/src/views/multi/flex/column_show.dart new file mode 100644 index 000000000..ee4659a66 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/multi/flex/column_show.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; + +class ColumnShow extends StatelessWidget { + const ColumnShow({super.key}); + + @override + Widget build(BuildContext context) { + List colors = [ + const Color(0xffe64032), + const Color(0xff307dee), + const Color(0xfff9c01f), + const Color(0xff309949), + ]; + return Column( + children: [ + Container(width: 20, height: 20, color: colors[0]), + Container(width: 10, height: 80, color: colors[1]), + Container(width: 40, height: 30, color: colors[2]), + Container(width: 60, height: 20, color: colors[3]), + ], + ); + } +} diff --git a/modules/knowledge_system/layout/lib/src/views/multi/flex/row_show.dart b/modules/knowledge_system/layout/lib/src/views/multi/flex/row_show.dart new file mode 100644 index 000000000..4a15ef26a --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/multi/flex/row_show.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; + +class RowShow extends StatelessWidget { + const RowShow({super.key}); + + @override + Widget build(BuildContext context) { + List colors = [ + Color(0xffe64032), + Color(0xff307dee), + Color(0xfff9c01f), + Color(0xff309949), + ]; + return Row( + children: [ + Container(width: 20, height: 20, color: colors[0]), + Container(width: 10, height: 80, color: colors[1]), + Container(width: 40, height: 30, color: colors[2]), + Container(width: 60, height: 20, color: colors[3]), + ], + ); + } +} diff --git a/modules/knowledge_system/layout/lib/src/views/playground/cons.dart b/modules/knowledge_system/layout/lib/src/views/playground/cons.dart new file mode 100644 index 000000000..f97ab2d18 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/playground/cons.dart @@ -0,0 +1,16 @@ +// Copyright 2014 The 张风捷特烈 . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Author: 张风捷特烈 +// CreateTime: 2024-06-29 +// Contact Me: 1981462002@qq.com + +import 'dart:ui'; + +const List kColors = [ + Color(0xffe64032), + Color(0xff307dee), + Color(0xfff9c01f), + Color(0xff309949), +]; diff --git a/modules/knowledge_system/layout/lib/src/views/playground/data/flex_attr.dart b/modules/knowledge_system/layout/lib/src/views/playground/data/flex_attr.dart new file mode 100644 index 000000000..9d608d068 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/playground/data/flex_attr.dart @@ -0,0 +1,48 @@ +// Copyright 2014 The 张风捷特烈 . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Author: 张风捷特烈 +// CreateTime: 2024-06-29 +// Contact Me: 1981462002@qq.com + +import 'package:flutter/material.dart'; + +class FlexAttr { + final Axis direction; + final MainAxisAlignment mainAxisAlignment; + final CrossAxisAlignment crossAxisAlignment; + final MainAxisSize mainAxisSize; + final TextDirection textDirection; + final VerticalDirection verticalDirection; + final TextBaseline textBaseline; + + FlexAttr({ + required this.direction, + this.mainAxisAlignment = MainAxisAlignment.start, + this.crossAxisAlignment = CrossAxisAlignment.center, + this.mainAxisSize = MainAxisSize.max, + this.textDirection = TextDirection.ltr, + this.textBaseline = TextBaseline.alphabetic, + this.verticalDirection = VerticalDirection.down, + }); + + FlexAttr copyWith({ + Axis? direction, + MainAxisAlignment? mainAxisAlignment, + CrossAxisAlignment? crossAxisAlignment, + MainAxisSize? mainAxisSize, + TextDirection? textDirection, + VerticalDirection? verticalDirection, + TextBaseline? textBaseline, + }) => + FlexAttr( + direction: direction ?? this.direction, + mainAxisAlignment: mainAxisAlignment ?? this.mainAxisAlignment, + crossAxisAlignment: crossAxisAlignment ?? this.crossAxisAlignment, + mainAxisSize: mainAxisSize ?? this.mainAxisSize, + textDirection: textDirection ?? this.textDirection, + verticalDirection: verticalDirection ?? this.verticalDirection, + textBaseline: textBaseline ?? this.textBaseline, + ); +} diff --git a/modules/knowledge_system/layout/lib/src/views/playground/data/stack_attr.dart b/modules/knowledge_system/layout/lib/src/views/playground/data/stack_attr.dart new file mode 100644 index 000000000..c307bb23f --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/playground/data/stack_attr.dart @@ -0,0 +1,50 @@ +// Copyright 2014 The 张风捷特烈 . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Author: 张风捷特烈 +// CreateTime: 2024-06-29 +// Contact Me: 1981462002@qq.com + +import 'package:flutter/material.dart'; + + + +class StackAttr { + final AttrAlignment alignment; + final StackFit fit; + final TextDirection textDirection; + + StackAttr({ + this.alignment = AttrAlignment.topStart, + this.fit = StackFit.loose, + this.textDirection = TextDirection.ltr, + + }); + + StackAttr copyWith({ + AttrAlignment? alignment, + StackFit? fit, + TextDirection? textDirection, + }) => + StackAttr( + alignment: alignment ?? this.alignment, + fit: fit ?? this.fit, + textDirection: textDirection ?? this.textDirection, + ); +} + +enum AttrAlignment{ + topStart(Alignment.topLeft), + topCenter(Alignment.topCenter), + topRight(Alignment.topRight), + bottomCenter(Alignment.bottomCenter), + bottomLeft(Alignment.bottomLeft), + bottomRight(Alignment.bottomRight), + center(Alignment.center), + centerLeft(Alignment.centerLeft), + centerRight(Alignment.centerRight), + ; + final Alignment value; + const AttrAlignment(this.value); +} diff --git a/modules/knowledge_system/layout/lib/src/views/playground/data/wrap_attr.dart b/modules/knowledge_system/layout/lib/src/views/playground/data/wrap_attr.dart new file mode 100644 index 000000000..e2acc5f2c --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/playground/data/wrap_attr.dart @@ -0,0 +1,55 @@ +// Copyright 2014 The 张风捷特烈 . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Author: 张风捷特烈 +// CreateTime: 2024-06-29 +// Contact Me: 1981462002@qq.com + +import 'package:flutter/material.dart'; + + +class WrapAttr { + final Axis direction; + final WrapAlignment mainAlignment; + final WrapCrossAlignment crossAxisAlignment; + final double spacing; + final TextDirection textDirection; + final VerticalDirection verticalDirection; + final WrapAlignment runAlignment; + final double runSpacing; + + const WrapAttr({ + this.direction = Axis.horizontal, + this.mainAlignment = WrapAlignment.start, + this.crossAxisAlignment = WrapCrossAlignment.center, + this.spacing = 0.0, + this.textDirection = TextDirection.ltr, + this.verticalDirection = VerticalDirection.down, + this.runAlignment = WrapAlignment.start, + this.runSpacing =0.0, + }); + + WrapAttr copyWith({ + Axis? direction, + WrapAlignment? mainAlignment, + WrapCrossAlignment? crossAxisAlignment, + double? spacing, + TextDirection?textDirection, + VerticalDirection? verticalDirection, + WrapAlignment?runAlignment, + Clip? clipBehavior, + double ?runSpacing, + + }) => + WrapAttr( + direction: direction ?? this.direction, + mainAlignment: mainAlignment ?? this.mainAlignment, + crossAxisAlignment: crossAxisAlignment ?? this.crossAxisAlignment, + spacing: spacing ?? this.spacing, + textDirection: textDirection ?? this.textDirection, + verticalDirection: verticalDirection ?? this.verticalDirection, + runAlignment: runAlignment ?? this.runAlignment, + runSpacing: runSpacing??this.runSpacing, + ); +} diff --git a/modules/knowledge_system/layout/lib/src/views/playground/view/display_item.dart b/modules/knowledge_system/layout/lib/src/views/playground/view/display_item.dart new file mode 100644 index 000000000..0438b2254 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/playground/view/display_item.dart @@ -0,0 +1,49 @@ +// Copyright 2014 The 张风捷特烈 . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Author: 张风捷特烈 +// CreateTime: 2024-06-29 +// Contact Me: 1981462002@qq.com + +import 'package:flutter/material.dart'; + +class DisplayItem { + final double width; + final double height; + final Color color; + + DisplayItem({ + required this.width, + required this.height, + required this.color, + }); +} + + +class DisplayPlayItem extends StatefulWidget { + final DisplayItem item; + final bool selected; + const DisplayPlayItem({ + super.key, + required this.item, + required this.selected, + }); + + @override + State createState() => _DisplayPlayItemState(); +} + +class _DisplayPlayItemState extends State { + @override + Widget build(BuildContext context) { + return Container( + width: widget.item.width, + height: widget.item.height, + decoration: BoxDecoration( + border: widget.selected ? Border.all() : null, + color: widget.item.color, + ), + ); + } +} diff --git a/modules/knowledge_system/layout/lib/src/views/playground/view/flex/flex_op_panel.dart b/modules/knowledge_system/layout/lib/src/views/playground/view/flex/flex_op_panel.dart new file mode 100644 index 000000000..592cce69a --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/playground/view/flex/flex_op_panel.dart @@ -0,0 +1,170 @@ +// Copyright 2014 The 张风捷特烈 . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Author: 张风捷特烈 +// CreateTime: 2024-06-29 +// Contact Me: 1981462002@qq.com + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:tolyui/tolyui.dart'; + +import '../../data/flex_attr.dart'; +import '../form_item/item_selector.dart'; +import '../form_item/item_size_input.dart'; + + +class FlexOpTool extends StatefulWidget { + final ValueChanged onAddBox; + final VoidCallback onDelete; + final VoidCallback onReset; + final FlexAttr attr; + final ValueChanged onAttrChange; + + const FlexOpTool({ + super.key, + required this.onAddBox, + required this.onDelete, + required this.onReset, + required this.attr, + required this.onAttrChange, + }); + + @override + State createState() => _FlexOpToolState(); +} + +class _FlexOpToolState extends State { + final TextEditingController _widthCtrl = TextEditingController(text: '24'); + final TextEditingController _heightCtrl = TextEditingController(text: '64'); + + @override + Widget build(BuildContext context) { + TextStyle labelStyle = const TextStyle(color: Color(0xff61666d), fontSize: 12); + return Column( + children: [ + const Padding( + padding: EdgeInsets.all(8.0), + child: Text( + 'Flex 操作面板', + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + const Divider(), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 6), + child: Row( + children: [ + Text('增删操作', style: labelStyle.copyWith(fontWeight: FontWeight.bold)), + const Spacer(), + TolyAction(onTap: _handleAdd, child: const Icon(CupertinoIcons.add, size: 18)), + TolyAction(onTap: widget.onDelete, child: const Icon(CupertinoIcons.delete, size: 16)), + ], + ), + ), + ItemSizeInput(widthCtrl: _widthCtrl, heightCtrl: _heightCtrl), + const SizedBox(height: 12), + const Divider(), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 6), + child: Row( + children: [ + Text('属性操作', style: labelStyle.copyWith(fontWeight: FontWeight.bold)), + const Spacer(), + TolyAction(onTap: widget.onReset, child: const Icon(CupertinoIcons.refresh, size: 16)), + + ], + ), + ), + ItemSelector( + label: '排列方向:', + subTitle: 'direction', + selectIndex: widget.attr.direction.index, + data: Axis.values, + calcFun: (Axis data) => data.name, + onSelect: (Axis value) { + widget.onAttrChange(widget.attr.copyWith(direction: value)); + }, + ), + ItemSelector( + label: '主轴对齐:', + subTitle: 'mainAxisAlignment', + selectIndex: widget.attr.mainAxisAlignment.index, + data: MainAxisAlignment.values, + calcFun: (MainAxisAlignment data) => data.name, + onSelect: (MainAxisAlignment value) { + widget.onAttrChange(widget.attr.copyWith(mainAxisAlignment: value)); + }, + ), + ItemSelector( + label: '主轴尺寸:', + subTitle: 'mainAxisSize', + selectIndex: widget.attr.mainAxisSize.index, + data: MainAxisSize.values, + calcFun: (MainAxisSize data) => data.name, + onSelect: (MainAxisSize value) { + widget.onAttrChange(widget.attr.copyWith(mainAxisSize: value)); + }, + ), + ItemSelector( + label: '叉轴对齐:', + subTitle: 'crossAxisAlignment', + selectIndex: widget.attr.crossAxisAlignment.index, + data: CrossAxisAlignment.values, + calcFun: (CrossAxisAlignment data) => data.name, + onSelect: (CrossAxisAlignment value) { + widget.onAttrChange(widget.attr.copyWith(crossAxisAlignment: value)); + }, + ), + ItemSelector( + label: '垂直方向:', + subTitle: 'verticalDirection', + selectIndex: widget.attr.verticalDirection.index, + data: VerticalDirection.values, + calcFun: (VerticalDirection data) => data.name, + onSelect: (VerticalDirection value) { + widget.onAttrChange(widget.attr.copyWith(verticalDirection: value)); + }, + ), + ItemSelector( + label: '文字方向:', + subTitle: 'textDirection', + selectIndex: widget.attr.textDirection.index, + data: TextDirection.values, + calcFun: (TextDirection data) => data.name, + onSelect: (TextDirection value) { + widget.onAttrChange(widget.attr.copyWith(textDirection: value)); + }, + ), + ItemSelector( + label: '文字基线:', + subTitle: 'textBaseline', + selectIndex: widget.attr.textBaseline.index, + data: TextBaseline.values, + calcFun: (TextBaseline data) => data.name, + onSelect: (TextBaseline value) { + widget.onAttrChange(widget.attr.copyWith(textBaseline: value)); + }, + ), + ], + ); + } + + void _handleAdd() { + final double? width = double.tryParse(_widthCtrl.text); + final double? height = double.tryParse(_heightCtrl.text); + if (width == null || height == null) { + $message.warning(message: '请输入合法宽高数字!'); + return; + } + widget.onAddBox(Size(width, height)); + } + + @override + void dispose() { + _widthCtrl.dispose(); + _heightCtrl.dispose(); + super.dispose(); + } +} diff --git a/modules/knowledge_system/layout/lib/src/views/playground/view/flex/flex_playground.dart b/modules/knowledge_system/layout/lib/src/views/playground/view/flex/flex_playground.dart new file mode 100644 index 000000000..07e2db436 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/playground/view/flex/flex_playground.dart @@ -0,0 +1,144 @@ +// Copyright 2014 The 张风捷特烈 . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Author: 张风捷特烈 +// CreateTime: 2024-06-23 +// Contact Me: 1981462002@qq.com + +import 'package:flutter/material.dart'; +import 'package:tolyui/tolyui.dart'; + +import '../../cons.dart'; +import '../../data/flex_attr.dart'; +import '../display_item.dart'; +import 'flex_op_panel.dart'; + +class FlexPlayground extends StatefulWidget { + const FlexPlayground({super.key}); + + @override + State createState() => _FlexPlaygroundState(); +} + +class _FlexPlaygroundState extends State { + List _data = []; + late FlexAttr _attr; + int _selectIndex = -1; + + @override + void initState() { + super.initState(); + _reset(init: true); + } + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Expanded( + child: Center( + child: ColoredBox( + color: Colors.grey.withOpacity(0.1), + child: FlexDisplay( + items: _data, + attr: _attr, + selectIndex: _selectIndex, + onSelectChanged: _onSelectChanged, + )), + )), + const VerticalDivider(), + SizedBox( + width: 210, + child: FlexOpTool( + attr: _attr, + onReset: _reset, + onAddBox: _onAddBox, + onDelete: _deleteSelectIndex, + onAttrChange: _onAttrChange, + )), + ], + ); + } + + void _reset({bool init=false}){ + _attr = FlexAttr(direction: Axis.horizontal); + _data = [ + DisplayItem(width: 20, height: 20, color: kColors[0]), + DisplayItem(width: 10, height: 80, color: kColors[1]), + DisplayItem(width: 40, height: 30, color: kColors[2]), + DisplayItem(width: 60, height: 20, color: kColors[3]), + ]; + if(init) return; + setState(() {}); + } + + void _onAttrChange(FlexAttr attr) { + setState(() { + _attr = attr; + }); + } + + void _onAddBox(Size size) { + int index = _data.length + 1; + Color color = kColors[index % kColors.length]; + _data.add(DisplayItem(width: size.width, height: size.height, color: color)); + setState(() {}); + } + + void _deleteSelectIndex() { + if (_selectIndex < 0 || _selectIndex >= _data.length) { + $message.warning(message: '请先选择删除的色块!'); + return; + } + _data.removeAt(_selectIndex); + _selectIndex = -1; + setState(() {}); + } + + void _onSelectChanged(int value) { + if (_selectIndex == value) { + _selectIndex = -1; + } else { + _selectIndex = value; + } + setState(() {}); + } + + +} + +class FlexDisplay extends StatelessWidget { + final List items; + final FlexAttr attr; + final int selectIndex; + final ValueChanged onSelectChanged; + + const FlexDisplay({ + super.key, + required this.items, + required this.attr, + required this.selectIndex, + required this.onSelectChanged, + }); + + @override + Widget build(BuildContext context) { + return Flex( + direction: attr.direction, + mainAxisAlignment: attr.mainAxisAlignment, + crossAxisAlignment: attr.crossAxisAlignment, + mainAxisSize: attr.mainAxisSize, + textDirection: attr.textDirection, + verticalDirection: attr.verticalDirection, + textBaseline: TextBaseline.alphabetic, + children: items.asMap().keys.map((int index) { + bool active = selectIndex == index; + return GestureDetector( + onTap: () => onSelectChanged(index), + child: DisplayPlayItem(item: items[index], selected: active), + ); + }).toList(), + ); + } +} diff --git a/modules/knowledge_system/layout/lib/src/views/playground/view/form_item/item_selector.dart b/modules/knowledge_system/layout/lib/src/views/playground/view/form_item/item_selector.dart new file mode 100644 index 000000000..57432213e --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/playground/view/form_item/item_selector.dart @@ -0,0 +1,74 @@ +// Copyright 2014 The 张风捷特烈 . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Author: 张风捷特烈 +// CreateTime: 2024-06-30 +// Contact Me: 1981462002@qq.com + +import 'package:flutter/material.dart'; +import 'package:tolyui/tolyui.dart'; + +typedef NameCalc = String Function(T data); + +class ItemSelector extends StatelessWidget { + final int selectIndex; + final List data; + final NameCalc calcFun; + final ValueChanged onSelect; + final String label; + final String subTitle; + + const ItemSelector({ + super.key, + required this.selectIndex, + required this.data, + required this.calcFun, + required this.onSelect, + required this.subTitle, + required this.label, + }); + + @override + Widget build(BuildContext context) { + TextStyle labelStyle = const TextStyle(color: Color(0xff61666d), fontSize: 12); + DropMenuCellStyle lightStyle = const DropMenuCellStyle( + padding: EdgeInsets.symmetric(horizontal: 4,vertical: 1), + borderRadius: BorderRadius.all(Radius.circular(6)), + foregroundColor: Color(0xff1f1f1f), + backgroundColor: Colors.transparent, + disableColor: Color(0xffbfbfbf), + hoverBackgroundColor: Color(0xfff5f5f5), + hoverForegroundColor: Color(0xff1f1f1f), + textStyle: TextStyle(fontSize: 11) + ); + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 6), + child: Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(label, style: labelStyle), + Text(subTitle, style: labelStyle.copyWith(fontSize: 8)), + ], + ), + const Spacer(), + TolySelect( + fontSize: 11, + cellStyle: lightStyle, + data: data.map((e) => calcFun(e)).toList(), + selectIndex: selectIndex, + iconSize: 16, + height: 25, + width: 110, + maxHeight: 200, + shrinkWrapWidthOverlay: false, + minWidth: 0, + onSelected: (int index) => onSelect(data[index]), + ) + ], + ), + ); + } +} diff --git a/modules/knowledge_system/layout/lib/src/views/playground/view/form_item/item_size_input.dart b/modules/knowledge_system/layout/lib/src/views/playground/view/form_item/item_size_input.dart new file mode 100644 index 000000000..1ccb27ae2 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/playground/view/form_item/item_size_input.dart @@ -0,0 +1,54 @@ +// Copyright 2014 The 张风捷特烈 . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Author: 张风捷特烈 +// CreateTime: 2024-06-29 +// Contact Me: 1981462002@qq.com + +import 'package:flutter/cupertino.dart'; + +class ItemSizeInput extends StatelessWidget { + final TextEditingController widthCtrl; + final TextEditingController heightCtrl; + + const ItemSizeInput({ + super.key, + required this.widthCtrl, + required this.heightCtrl, + }); + + @override + Widget build(BuildContext context) { + TextStyle labelStyle = const TextStyle(color: Color(0xff61666d), fontSize: 12); + + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: Row( + children: [ + Text('宽高: ', style: labelStyle), + const SizedBox(width: 20), + Expanded( + child: CupertinoTextField( + controller: widthCtrl, + style: const TextStyle(fontSize: 12), + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + )), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 4.0), + child: Text( + "x", + style: TextStyle(fontSize: 12), + ), + ), + Expanded( + child: CupertinoTextField( + controller: heightCtrl, + style: const TextStyle(fontSize: 12), + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + )) + ], + ), + ); + } +} diff --git a/modules/knowledge_system/layout/lib/src/views/playground/view/form_item/value_input.dart b/modules/knowledge_system/layout/lib/src/views/playground/view/form_item/value_input.dart new file mode 100644 index 000000000..67a300a95 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/playground/view/form_item/value_input.dart @@ -0,0 +1,52 @@ +// Copyright 2014 The 张风捷特烈 . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Author: 张风捷特烈 +// CreateTime: 2024-06-30 +// Contact Me: 1981462002@qq.com + +import 'package:flutter/cupertino.dart'; + +class ValueInput extends StatelessWidget { + final String label; + final String subtitle; + final ValueChanged onChange; + + const ValueInput({ + super.key, + required this.onChange, + required this.label, + required this.subtitle, + }); + + @override + Widget build(BuildContext context) { + TextStyle labelStyle = const TextStyle(color: Color(0xff61666d), fontSize: 12); + + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 6), + child: Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(label, style: labelStyle), + Text(subtitle, style: labelStyle.copyWith(fontSize: 8)), + ], + ), + const Spacer(), + SizedBox( + width: 110, + child: CupertinoTextField( + keyboardType: TextInputType.number, + onChanged: onChange, + style: const TextStyle(fontSize: 12), + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + ), + ) + ], + ), + ); + } +} diff --git a/modules/knowledge_system/layout/lib/src/views/playground/view/stack/stack_op_panel.dart b/modules/knowledge_system/layout/lib/src/views/playground/view/stack/stack_op_panel.dart new file mode 100644 index 000000000..18c1fae97 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/playground/view/stack/stack_op_panel.dart @@ -0,0 +1,130 @@ +// Copyright 2014 The 张风捷特烈 . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Author: 张风捷特烈 +// CreateTime: 2024-06-29 +// Contact Me: 1981462002@qq.com + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:tolyui/tolyui.dart'; + +import '../../data/stack_attr.dart'; +import '../form_item/item_selector.dart'; +import '../form_item/item_size_input.dart'; + + +class StackOpTool extends StatefulWidget { + final ValueChanged onAddBox; + final VoidCallback onDelete; + final VoidCallback onReset; + final StackAttr attr; + final ValueChanged onAttrChange; + + const StackOpTool({ + super.key, + required this.onAddBox, + required this.onDelete, + required this.onReset, + required this.attr, + required this.onAttrChange, + }); + + @override + State createState() => _StackOpToolState(); +} + +class _StackOpToolState extends State { + final TextEditingController _widthCtrl = TextEditingController(text: '24'); + final TextEditingController _heightCtrl = TextEditingController(text: '64'); + + @override + Widget build(BuildContext context) { + TextStyle labelStyle = const TextStyle(color: Color(0xff61666d), fontSize: 12); + return Column( + children: [ + const Padding( + padding: EdgeInsets.all(8.0), + child: Text( + 'Flex 操作面板', + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + const Divider(), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 6), + child: Row( + children: [ + Text('增删操作', style: labelStyle.copyWith(fontWeight: FontWeight.bold)), + const Spacer(), + TolyAction(child: const Icon(CupertinoIcons.add, size: 18), onTap: _handleAdd), + TolyAction(child: const Icon(CupertinoIcons.delete, size: 16), onTap: widget.onDelete), + ], + ), + ), + ItemSizeInput(widthCtrl: _widthCtrl, heightCtrl: _heightCtrl), + const SizedBox(height: 12), + const Divider(), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 6), + child: Row( + children: [ + Text('属性操作', style: labelStyle.copyWith(fontWeight: FontWeight.bold)), + Spacer(), + TolyAction(child: const Icon(CupertinoIcons.refresh, size: 16), onTap: widget.onReset), + + ], + ), + ), + ItemSelector( + label: '对齐方式:', + subTitle: 'direction', + selectIndex: widget.attr.alignment.index, + data: AttrAlignment.values, + calcFun: (AttrAlignment data) => data.name, + onSelect: (AttrAlignment value) { + widget.onAttrChange(widget.attr.copyWith(alignment: value)); + }, + ), + ItemSelector( + label: '适应模式:', + subTitle: 'fit', + selectIndex: widget.attr.fit.index, + data: StackFit.values, + calcFun: (StackFit data) => data.name, + onSelect: (StackFit value) { + widget.onAttrChange(widget.attr.copyWith(fit: value)); + }, + ), + ItemSelector( + label: '文字方向:', + subTitle: 'textDirection', + selectIndex: widget.attr.textDirection.index, + data: TextDirection.values, + calcFun: (TextDirection data) => data.name, + onSelect: (TextDirection value) { + widget.onAttrChange(widget.attr.copyWith(textDirection: value)); + }, + ), + ], + ); + } + + void _handleAdd() { + final double? width = double.tryParse(_widthCtrl.text); + final double? height = double.tryParse(_heightCtrl.text); + if (width == null || height == null) { + $message.warning(message: '请输入合法宽高数字!'); + return; + } + widget.onAddBox(Size(width, height)); + } + + @override + void dispose() { + _widthCtrl.dispose(); + _heightCtrl.dispose(); + super.dispose(); + } +} diff --git a/modules/knowledge_system/layout/lib/src/views/playground/view/stack/stack_playground.dart b/modules/knowledge_system/layout/lib/src/views/playground/view/stack/stack_playground.dart new file mode 100644 index 000000000..3a22f8f27 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/playground/view/stack/stack_playground.dart @@ -0,0 +1,137 @@ +// Copyright 2014 The 张风捷特烈 . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Author: 张风捷特烈 +// CreateTime: 2024-06-23 +// Contact Me: 1981462002@qq.com + +import 'package:flutter/material.dart'; +import 'package:tolyui/tolyui.dart'; + +import '../../cons.dart'; +import '../../data/flex_attr.dart'; +import '../../data/stack_attr.dart'; +import '../display_item.dart'; +import 'stack_op_panel.dart'; + +class StackPlayground extends StatefulWidget { + const StackPlayground({super.key}); + + @override + State createState() => _StackPlaygroundState(); +} + +class _StackPlaygroundState extends State { + List _data = []; + StackAttr _attr = StackAttr(); + int _selectIndex = -1; + + @override + void initState() { + super.initState(); + _data = [ + DisplayItem(width: 80, height: 80, color: kColors[3]), + DisplayItem(width: 60, height: 60, color: kColors[2]), + DisplayItem(width: 40, height: 40, color: kColors[1]), + DisplayItem(width: 20, height: 20, color: kColors[0]), + ]; + } + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Expanded( + child: Center( + child: SizedBox( + width: 200, + height: 200, + child: ColoredBox( + color: Colors.grey.withOpacity(0.1), + child: StackDisplay( + items: _data, + attr: _attr, + selectIndex: _selectIndex, + onSelectChanged: _onSelectChanged, + )), + ), + )), + const VerticalDivider(), + SizedBox( + width: 200, + child: StackOpTool( + attr: _attr, + onReset: ()=>_onAttrChange(StackAttr()), + onAddBox: _onAddBox, + onDelete: _deleteSelectIndex, + onAttrChange: _onAttrChange, + )), + ], + ); + } + + void _onAttrChange(StackAttr attr) { + setState(() { + _attr = attr; + }); + } + + void _onAddBox(Size size) { + int index = _data.length + 1; + Color color = kColors[index % kColors.length]; + _data.add(DisplayItem(width: size.width, height: size.height, color: color)); + print(size); + setState(() {}); + } + + void _onSelectChanged(int value) { + if (_selectIndex == value) { + _selectIndex = -1; + } else { + _selectIndex = value; + } + setState(() {}); + } + + void _deleteSelectIndex() { + if (_selectIndex < 0 || _selectIndex >= _data.length) { + $message.warning(message: '请先选择删除的色块!'); + return; + } + _data.removeAt(_selectIndex); + _selectIndex = -1; + setState(() {}); + } +} + +class StackDisplay extends StatelessWidget { + final List items; + final StackAttr attr; + final int selectIndex; + final ValueChanged onSelectChanged; + + const StackDisplay({ + super.key, + required this.items, + required this.attr, + required this.selectIndex, + required this.onSelectChanged, + }); + + @override + Widget build(BuildContext context) { + return Stack( + fit: attr.fit, + alignment: attr.alignment.value, + textDirection: attr.textDirection, + children: items.asMap().keys.map((int index) { + bool active = selectIndex == index; + return GestureDetector( + onTap: () => onSelectChanged(index), + child: DisplayPlayItem(item: items[index], selected: active), + ); + }).toList(), + ); + } +} diff --git a/modules/knowledge_system/layout/lib/src/views/playground/view/wrap/wrap_op_panel.dart b/modules/knowledge_system/layout/lib/src/views/playground/view/wrap/wrap_op_panel.dart new file mode 100644 index 000000000..61f65d562 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/playground/view/wrap/wrap_op_panel.dart @@ -0,0 +1,172 @@ +// Copyright 2014 The 张风捷特烈 . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Author: 张风捷特烈 +// CreateTime: 2024-06-29 +// Contact Me: 1981462002@qq.com + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:layout/src/views/playground/view/form_item/value_input.dart'; +import 'package:tolyui/tolyui.dart'; + +import '../../data/wrap_attr.dart'; +import '../form_item/item_selector.dart'; +import '../form_item/item_size_input.dart'; + +class WrapOpTool extends StatefulWidget { + final ValueChanged onAddBox; + final VoidCallback onDelete; + final WrapAttr attr; + final ValueChanged onAttrChange; + final VoidCallback onReset; + + const WrapOpTool({ + super.key, + required this.onAddBox, + required this.onDelete, + required this.onReset, + required this.attr, + required this.onAttrChange, + }); + + @override + State createState() => _WrapOpToolState(); +} + +class _WrapOpToolState extends State { + final TextEditingController widthCtrl = TextEditingController(text: '24'); + final TextEditingController heightCtrl = TextEditingController(text: '64'); + final TextEditingController spacingCtrl = TextEditingController(text: "0"); + final TextEditingController rubSpacingCtrl = TextEditingController(text: "0"); + + @override + Widget build(BuildContext context) { + TextStyle labelStyle = TextStyle(color: Color(0xff61666d), fontSize: 12); + return Column( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + 'Wrap 操作面板', + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + Divider(), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 6), + child: Row( + children: [ + Text('增删操作', style: labelStyle.copyWith(fontWeight: FontWeight.bold)), + Spacer(), + TolyAction(child: Icon(CupertinoIcons.add, size: 18), onTap: _handleAdd), + TolyAction(child: Icon(CupertinoIcons.delete, size: 16), onTap: widget.onDelete), + ], + ), + ), + ItemSizeInput( + widthCtrl: widthCtrl, + heightCtrl: heightCtrl, + ), + const SizedBox(height: 12), + Divider(), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 6), + child: Row( + children: [ + Text('属性操作', style: labelStyle.copyWith(fontWeight: FontWeight.bold)), + Spacer(), + TolyAction( + child: const Icon(CupertinoIcons.refresh, size: 16), onTap: widget.onReset), + ], + ), + ), + ItemSelector( + label: '排列方向:', + subTitle: 'direction', + selectIndex: widget.attr.direction.index, + data: Axis.values, + calcFun: (Axis data) => data.name, + onSelect: (Axis value) { + widget.onAttrChange(widget.attr.copyWith(direction: value)); + }, + ), + ItemSelector( + label: '主轴对齐:', + subTitle: 'mainAlignment', + selectIndex: widget.attr.mainAlignment.index, + data: WrapAlignment.values, + calcFun: (WrapAlignment data) => data.name, + onSelect: (WrapAlignment value) { + widget.onAttrChange(widget.attr.copyWith(mainAlignment: value)); + }, + ), + ItemSelector( + label: '交叉轴对齐:', + subTitle: 'crossAxisAlignment', + selectIndex: widget.attr.crossAxisAlignment.index, + data: WrapCrossAlignment.values, + calcFun: (WrapCrossAlignment data) => data.name, + onSelect: (WrapCrossAlignment value) { + widget.onAttrChange(widget.attr.copyWith(crossAxisAlignment: value)); + }, + ), + ItemSelector( + label: '叉轴对齐:', + subTitle: 'runAlignment', + selectIndex: widget.attr.runAlignment.index, + data: WrapAlignment.values, + calcFun: (WrapAlignment data) => data.name, + onSelect: (WrapAlignment value) { + widget.onAttrChange(widget.attr.copyWith(runAlignment: value)); + }, + ), + ItemSelector( + label: '垂直方向:', + subTitle: 'verticalDirection', + selectIndex: widget.attr.verticalDirection.index, + data: VerticalDirection.values, + calcFun: (VerticalDirection data) => data.name, + onSelect: (VerticalDirection value) { + widget.onAttrChange(widget.attr.copyWith(verticalDirection: value)); + }, + ), + ItemSelector( + label: '文字方向:', + subTitle: 'textDirection', + selectIndex: widget.attr.textDirection.index, + data: TextDirection.values, + calcFun: (TextDirection data) => data.name, + onSelect: (TextDirection value) { + widget.onAttrChange(widget.attr.copyWith(textDirection: value)); + }, + ), + ValueInput( + label: '主轴间距:', + subtitle: 'spacing', + onChange: (String v) { + widget.onAttrChange(widget.attr.copyWith(spacing: double.tryParse(v))); + }, + ), + ValueInput( + label: '叉轴间距:', + subtitle: 'runSpacing', + onChange: (String v) { + widget.onAttrChange(widget.attr.copyWith(runSpacing: double.parse(v))); + }, + ), + ], + ); + } + + void _handleAdd() { + final double? width = double.tryParse(widthCtrl.text); + final double? height = double.tryParse(heightCtrl.text); + if (width == null || height == null) { + $message.warning(message: '请输入合法宽高数字!'); + return; + } + widget.onAddBox(Size(width, height)); + } +} diff --git a/modules/knowledge_system/layout/lib/src/views/playground/view/wrap/wrap_playground.dart b/modules/knowledge_system/layout/lib/src/views/playground/view/wrap/wrap_playground.dart new file mode 100644 index 000000000..5c7d94e8d --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/playground/view/wrap/wrap_playground.dart @@ -0,0 +1,158 @@ +// Copyright 2014 The 星星 . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Author: 星星 +// CreateTime: 2024-06-25 +// Contact Me: 1981462002@qq.com + +// Copyright 2014 The 张风捷特烈 . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Author: 张风捷特烈 +// CreateTime: 2024-06-23 +// Contact Me: 1981462002@qq.com + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:tolyui/tolyui.dart'; + +import '../../data/wrap_attr.dart'; +import '../display_item.dart'; +import '../form_item/item_size_input.dart'; +import 'wrap_op_panel.dart'; + +const List kColors = [ + Color(0xffd23eb9), + Color(0xff2164c7), + Color(0xffd5a213), + Color(0xff16e848), +]; + +class WrapPlayground extends StatefulWidget { + const WrapPlayground({super.key}); + + @override + State createState() => _WrapPlaygroundState(); +} + +class _WrapPlaygroundState extends State { + List _data = []; + late WrapAttr _attr; + int _selectIndex = -1; + + @override + void initState() { + super.initState(); + _reset(); + } + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Expanded( + child: Center( + child: ColoredBox( + color: Colors.grey.withOpacity(0.1), + child: WrapDisplay( + items: _data, + attr: _attr, + selectIndex: _selectIndex, + onSelectChanged: _onSelectChanged, + )))), + const VerticalDivider(), + SizedBox( + width: 210, + child: WrapOpTool( + attr: _attr, + onAddBox: _onAddBox, + onDelete: _deleteSelectIndex, + onAttrChange: _onAttrChange, + onReset: _reset, + )), + ], + ); + } + + void _reset() { + _attr = const WrapAttr(); + _data = [ + DisplayItem(width: 20, height: 20, color: kColors[0]), + DisplayItem(width: 10, height: 80, color: kColors[1]), + DisplayItem(width: 40, height: 30, color: kColors[2]), + DisplayItem(width: 60, height: 20, color: kColors[3]), + ]; + setState(() {}); + } + + void _onAttrChange(WrapAttr attr) { + _attr = attr; + setState(() {}); + } + + void _onAddBox(Size size) { + int index = _data.length + 1; + Color color = kColors[index % kColors.length]; + _data.add(DisplayItem(width: size.width, height: size.height, color: color)); + print(size); + setState(() {}); + } + + void _onSelectChanged(int value) { + if (_selectIndex == value) { + _selectIndex = -1; + } else { + _selectIndex = value; + } + setState(() {}); + } + + void _deleteSelectIndex() { + if (_selectIndex < 0 || _selectIndex >= _data.length) { + $message.warning(message: '请先选择删除的色块!'); + return; + } + _data.removeAt(_selectIndex); + _selectIndex = -1; + setState(() {}); + } +} + +class WrapDisplay extends StatelessWidget { + final List items; + final WrapAttr attr; + final int selectIndex; + final ValueChanged onSelectChanged; + + const WrapDisplay({ + super.key, + required this.items, + required this.attr, + required this.selectIndex, + required this.onSelectChanged, + }); + + @override + Widget build(BuildContext context) { + return Wrap( + direction: attr.direction, + alignment: attr.mainAlignment, + crossAxisAlignment: attr.crossAxisAlignment, + spacing: attr.spacing, + textDirection: attr.textDirection, + verticalDirection: attr.verticalDirection, + runAlignment: attr.runAlignment, + runSpacing: attr.runSpacing, + children: items.asMap().keys.map((int index) { + bool active = selectIndex == index; + return GestureDetector( + onTap: () => onSelectChanged(index), + child: DisplayPlayItem(item: items[index], selected: active), + ); + }).toList(), + ); + } +} + diff --git a/modules/knowledge_system/layout/lib/src/views/popable/autocomplete_demo.dart b/modules/knowledge_system/layout/lib/src/views/popable/autocomplete_demo.dart new file mode 100644 index 000000000..ac64c76d7 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/popable/autocomplete_demo.dart @@ -0,0 +1,43 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; + +class AutocompleteDemo extends StatelessWidget { + const AutocompleteDemo({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 24.0), + child: Center( + child: Autocomplete( + optionsBuilder: buildOptions, + onSelected: onSelected, + ), + ), + ); + } + + void onSelected(String selection) { + debugPrint('当前选择了 $selection'); + } + + Future> buildOptions( + TextEditingValue textEditingValue, + ) async { + if (textEditingValue.text == '') { + return const Iterable.empty(); + } + return searchByArgs(textEditingValue.text); + } + + Future> searchByArgs(String args) async{ + // 模拟网络请求 + await Future.delayed(const Duration(milliseconds: 200)); + const List data = [ + 'toly', 'toly49', 'toly42', 'toly56', + 'card', 'ls', 'alex', 'fan sha', + ]; + return data.where((String name) => name.contains(args)); + } +} diff --git a/modules/knowledge_system/layout/lib/src/views/popable/dropdown_button_demo.dart b/modules/knowledge_system/layout/lib/src/views/popable/dropdown_button_demo.dart new file mode 100644 index 000000000..66755554a --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/popable/dropdown_button_demo.dart @@ -0,0 +1,68 @@ +import 'package:flutter/material.dart'; + +/// create by 张风捷特烈 on 2020-03-16 +/// contact me by email 1981462002@qq.com +/// 说明: + +// { +// "widgetId": 55, +// "name": 'DropdownButton基本用法', +// "priority": 1, +// "subtitle": +// "【value】 : 当前值 【T】\n" +// "【items】 : 下拉选框 【List>】\n" +// "【icon】 : 图标 【Widget】\n" +// "【elevation】 : 影深 【double】\n" +// "【onChanged】 : 选择条目事件 【Function(T)】\n" +// "【backgroundColor】 : 背景色 【Color】", +// } +class CustomDropDownButton extends StatefulWidget { + const CustomDropDownButton({Key? key}) : super(key: key); + + @override + _CustomDropDownButtonState createState() => _CustomDropDownButtonState(); +} + +class _CustomDropDownButtonState extends State { + Color _color = Colors.red; + final List _colors = const [ + Colors.red, + Colors.yellow, + Colors.blue, + Colors.green + ]; + final List _info = const ["红色", "黄色", "蓝色", "绿色"]; + + @override + Widget build(BuildContext context) { + return Wrap( + children: [ + Container( + margin: const EdgeInsets.symmetric(horizontal: 20), + width: 50, + height: 50, + color: _color, + ), + DropdownButton( + value: _color, + elevation: 1, + icon: Icon( + Icons.expand_more, + size: 20, + color: _color, + ), + items: _buildItems(), + onChanged: (v) => setState(() => _color = v??Colors.blue)), + ], + ); + } + + List> _buildItems() => _colors + .map((e) => DropdownMenuItem( + value: e, + child: Text( + _info[_colors.indexOf(e)], + style: TextStyle(color: e), + ))) + .toList(); +} \ No newline at end of file diff --git a/modules/knowledge_system/layout/lib/src/views/popable/dropdown_menu_demo.dart b/modules/knowledge_system/layout/lib/src/views/popable/dropdown_menu_demo.dart new file mode 100644 index 000000000..f1d839e99 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/popable/dropdown_menu_demo.dart @@ -0,0 +1,63 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +/// create by 张风捷特烈 on 2020/9/21 +/// contact me by email 1981462002@qq.com +/// 说明: 370 DropdownMenu 表单菜单 +/// 下拉选择组件,支持文本输入过滤,可自定义菜单项。底层主要依赖 MenuAnchor 和 TextFiled 实现。 +/// link: 55 +// { +// "widgetId": 370, +// "name": '下拉菜单的简单使用', +// "priority": 1, +// "subtitle": +// "【dropdownMenuEntries】 : 菜单条目列表 【List>】\n" +// "【initialSelection】 : 表单验证回调 【T?】\n" +// "【onSelected】 : 表单保存回调 【ValueChanged?】\n" +// "【menuHeight】 : 菜单高度 【double】\n" +// "【width】 : 输入框宽度 【double】", +// } +class DropdownMenuNode1 extends StatefulWidget { + const DropdownMenuNode1({super.key}); + + @override + State createState() => _DropdownMenuNode1State(); +} + +class _DropdownMenuNode1State extends State { + final List data = ['语文', '数学', '英语', '物理', '化学', '生物', '地理']; + late String _dropdownValue = data.first; + + @override + Widget build(BuildContext context) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField(), + // DropdownMenu( + // requestFocusOnTap: false, + // menuHeight: 200, + // initialSelection: data.first, + // onSelected: _onSelect, + // dropdownMenuEntries: _buildMenuList(data), + // ), + const SizedBox(height: 8,), + Text('你选择的学科是: $_dropdownValue') + ], + ), + ); + } + + void _onSelect(String? value) { + // setState(() { + // _dropdownValue = value!; + // }); + } + + List> _buildMenuList(List data) { + return data.map((String value) { + return DropdownMenuEntry(value: value, label: value); + }).toList(); + } +} diff --git a/modules/knowledge_system/layout/lib/src/views/scroll/grid_view/grid_view_demo01.dart b/modules/knowledge_system/layout/lib/src/views/scroll/grid_view/grid_view_demo01.dart new file mode 100644 index 000000000..f69c82489 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/scroll/grid_view/grid_view_demo01.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; + +class GridViewDemo01 extends StatelessWidget { + GridViewDemo01({super.key}); + + final List data = List.generate(128, (i) => Color(0xFFFF00FF - 2 * i)); + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 200, + child: GridView.count( + crossAxisCount: 4, + mainAxisSpacing: 2, + crossAxisSpacing: 2, + childAspectRatio: 1 / 0.618, + children: data.map((color) => _buildItem(color)).toList(), + ), + ); + } + + Container _buildItem(Color color) => Container( + alignment: Alignment.center, + width: 100, + height: 30, + color: color, + child: Text( + colorString(color), + style: const TextStyle( + color: Colors.white, + shadows: [ + Shadow(color: Colors.black, offset: Offset(.5, .5), blurRadius: 2) + ], + ), + ), + ); + + String colorString(Color color) => + "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; + +} diff --git a/modules/knowledge_system/layout/lib/src/views/scroll/list_view/list_view_demo01.dart b/modules/knowledge_system/layout/lib/src/views/scroll/list_view/list_view_demo01.dart new file mode 100644 index 000000000..d9dd06d29 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/scroll/list_view/list_view_demo01.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; + +class ListViewDemo01 extends StatelessWidget { + const ListViewDemo01({super.key}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: ListView.builder( + itemCount: 100, + itemBuilder: (_, index) { + return Card( + child: ListTile( + tileColor: Colors.transparent, + title: Text('Test index:$index'), + )); + }), + ); + } +} diff --git a/modules/knowledge_system/layout/lib/src/views/scroll/page_view/page_view_demo01.dart b/modules/knowledge_system/layout/lib/src/views/scroll/page_view/page_view_demo01.dart new file mode 100644 index 000000000..f1f7053fe --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/scroll/page_view/page_view_demo01.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; + +const List kColors3 = [Colors.blue,Colors.red, Colors.green, Colors.orange]; + +class PageViewDemo01 extends StatelessWidget { + PageViewDemo01({super.key}); + + final List data = List.generate(128, (i) => Color(0xFFFF00FF - 2 * i)); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: PageView.builder( + itemCount: 8, + itemBuilder: (_, index) { + Color color = kColors3[index % kColors3.length]; + return Container( + color: color, + alignment: Alignment.center, + child: Text( + 'Page ${index + 1}\n${colorString(color)}', + textAlign: TextAlign.center, + style: TextStyle(fontSize: 18, color: Colors.white), + ), + ); + }), + ); + } + + String colorString(Color color) => + "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; +} diff --git a/modules/knowledge_system/layout/lib/src/views/test_show.dart b/modules/knowledge_system/layout/lib/src/views/test_show.dart new file mode 100644 index 000000000..34b46ae96 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/views/test_show.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; + +class TextShow extends StatelessWidget { + final String info; + + const TextShow({super.key, required this.info}); + + @override + Widget build(BuildContext context) { + return Material( + color: Colors.white, + child: Center(child: Text(info)), + ); + } +} diff --git a/modules/knowledge_system/layout/pubspec.yaml b/modules/knowledge_system/layout/pubspec.yaml new file mode 100644 index 000000000..a1d73812f --- /dev/null +++ b/modules/knowledge_system/layout/pubspec.yaml @@ -0,0 +1,52 @@ +name: layout +description: "A new Flutter package project." +version: 0.0.1 +homepage: + +environment: + sdk: ">=3.5.0 <4.0.0" + flutter: ">=1.17.0" +resolution: workspace +dependencies: + flutter: + sdk: flutter + + + + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # To add assets to your package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # To add custom fonts to your package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/modules/knowledge_system/layout/test/layout_test.dart b/modules/knowledge_system/layout/test/layout_test.dart new file mode 100644 index 000000000..8a6cc84f3 --- /dev/null +++ b/modules/knowledge_system/layout/test/layout_test.dart @@ -0,0 +1,12 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:layout/layout.dart'; + +void main() { + // test('adds one to input values', () { + // final calculator = Calculator(); + // expect(calculator.addOne(2), 3); + // expect(calculator.addOne(-7), -6); + // expect(calculator.addOne(0), 1); + // }); +} diff --git a/modules/knowledge_system/note/.gitignore b/modules/knowledge_system/note/.gitignore new file mode 100644 index 000000000..eb6c05cd3 --- /dev/null +++ b/modules/knowledge_system/note/.gitignore @@ -0,0 +1,31 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +build/ diff --git a/modules/knowledge_system/note/.metadata b/modules/knowledge_system/note/.metadata new file mode 100644 index 000000000..ab15d7afa --- /dev/null +++ b/modules/knowledge_system/note/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "c519ee916eaeb88923e67befb89c0f1dabfa83e6" + channel: "stable" + +project_type: package diff --git a/modules/knowledge_system/note/CHANGELOG.md b/modules/knowledge_system/note/CHANGELOG.md new file mode 100644 index 000000000..41cc7d819 --- /dev/null +++ b/modules/knowledge_system/note/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/modules/knowledge_system/note/LICENSE b/modules/knowledge_system/note/LICENSE new file mode 100644 index 000000000..ba75c69f7 --- /dev/null +++ b/modules/knowledge_system/note/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/modules/knowledge_system/note/README.md b/modules/knowledge_system/note/README.md new file mode 100644 index 000000000..e316598f9 --- /dev/null +++ b/modules/knowledge_system/note/README.md @@ -0,0 +1,2 @@ +#### 匠心巧记 模块 +打造便捷使用的 [全端同步] 笔记 \ No newline at end of file diff --git a/modules/knowledge_system/note/analysis_options.yaml b/modules/knowledge_system/note/analysis_options.yaml new file mode 100644 index 000000000..a5744c1cf --- /dev/null +++ b/modules/knowledge_system/note/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/modules/knowledge_system/note/lib/note.dart b/modules/knowledge_system/note/lib/note.dart new file mode 100644 index 000000000..4e8b4746c --- /dev/null +++ b/modules/knowledge_system/note/lib/note.dart @@ -0,0 +1,7 @@ +export 'src/view/view.dart'; +export 'src/bloc/bloc.dart'; +export 'src/bloc/news_bloc.dart'; +export 'src/repository/repository.dart'; +export 'src/env/env.dart'; +export 'package:flutter_quill/flutter_quill.dart' + show FlutterQuillLocalizations; diff --git a/modules/knowledge_system/note/lib/src/bloc/bloc.dart b/modules/knowledge_system/note/lib/src/bloc/bloc.dart new file mode 100644 index 000000000..cb5880850 --- /dev/null +++ b/modules/knowledge_system/note/lib/src/bloc/bloc.dart @@ -0,0 +1,154 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:fx_dio/fx_dio.dart'; +import 'package:note/src/repository/article_repository.dart'; +import 'package:note/src/repository/model/article.dart'; + +import '../repository/model/model.dart'; + +class ArtSysBloc extends Cubit { + ArtSysBloc() : super(ArtSysState(articles: [])); + + final ArticleRepository _repository = HttpArticleRepository(); + + TextEditingController titleCtrl = TextEditingController(); + TextEditingController ctrl = TextEditingController(); + + Future loadFirstFrame() async { + if (state.articles.isEmpty) { + emit(state.copyWith(status: const LoadingStatus())); + } + ApiRet> ret = await _repository.list(SizeFilter()); + if (ret.success) { + ArtSysState newState = state.copyWith( + articles: ret.data, + status: SuccessStatus(ret.paginate?.total ?? 0), + ); + emit(newState); + _openCurrent(); + return; + } + print(ret.trace?.toString()); + ArtSysState newState = state.copyWith( + status: FailedStatus(ret.trace?.error, ret.trace?.stack), + ); + emit(newState); + } + + void _openCurrent() { + int? id = state.active?.id; + if (id != null) { + open(id); + } + } + + Future newArticle() async { + await _repository.create( + ArticleCreatePayload( + subtitle: '', + title: '新建文档', + url: '', + cover: '', + type: 1, + createAt: DateTime.timestamp().toIso8601String(), + ), + ); + await loadFirstFrame(); + } + + void select(ArticlePo article) { + if (article.type == 1) { + open(article.id); + } else {} + titleCtrl.text = article.title; + emit(state.copyWith(active: article)); + } + + void open(int id) async { + ApiRet ret = await _repository.open(id); + if (ret.success) { + ctrl.text = ret.data; + } + } + + void write(String content) async { + int? id = state.active?.id; + if (id != null) { + ApiRet ret = await _repository.write(id, content); + } + } + + void updateTitleV2() { + ArticlePo? article = state.active; + String title = titleCtrl.text; + if (article != null) { + updateTitle(article, title); + } + } + + void updateTitle(ArticlePo article, String title) async { + if (title == article.title) return; + ApiRet ret = await _repository.update( + article.id, ArticleUpdatePayload(title: title)); + if (ret.success) { + open(article.id); + loadFirstFrame(); + titleCtrl.text = ret.data.title; + } else { + print(ret.trace?.error); + } + } + + Future delete() async { + int? id = state.active?.id; + if (id != null) { + ApiRet ret = await _repository.delete(id); + await loadFirstFrame(); + } + } +} + +sealed class ListStatus { + const ListStatus(); +} + +class LoadingStatus extends ListStatus { + const LoadingStatus(); +} + +class SuccessStatus extends ListStatus { + final int total; + + const SuccessStatus(this.total); +} + +class FailedStatus extends ListStatus { + final Object? error; + final StackTrace? trace; + + const FailedStatus(this.error, [this.trace]); +} + +class ArtSysState { + final List articles; + final ArticlePo? active; + final ListStatus status; + + ArtSysState({ + required this.articles, + this.active, + this.status = const LoadingStatus(), + }); + + ArtSysState copyWith({ + List? articles, + ArticlePo? active, + ListStatus? status, + }) { + return ArtSysState( + articles: articles ?? this.articles, + active: active ?? this.active, + status: status ?? this.status, + ); + } +} diff --git a/modules/knowledge_system/note/lib/src/bloc/news_bloc.dart b/modules/knowledge_system/note/lib/src/bloc/news_bloc.dart new file mode 100644 index 000000000..74623de10 --- /dev/null +++ b/modules/knowledge_system/note/lib/src/bloc/news_bloc.dart @@ -0,0 +1,60 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:fx_dao/src/table/dao.dart'; +import 'package:note/note.dart'; +import 'package:fx_dio/fx_dio.dart'; +import 'package:app/app.dart'; + +class NewsBloc extends Cubit + with Cacheable>, TimeoutCache> { + NewsBloc() : super(NewsState(headerNews: [])); + + final ArticleRepository _repository = HttpArticleRepository(); + + void initByCache() async { + List? retCache = await find(shouldRemove: false); + if (retCache != null) { + emit(NewsState(headerNews: retCache)); + return; + } + } + + @override + String get cacheKey => 'flutter.unit.news'; + + void load() async { + List? retCache = await find(); + if (retCache != null) { + print("=====load in cache========="); + emit(NewsState(headerNews: retCache)); + return; + } + refreshFromNet(); + } + + Future refreshFromNet() async { + SizeFilter filter = const SizeFilter( + page: 1, + pageSize: 8, + ); + ApiRet> ret = + await _repository.getArticlesByTag(1, filter: filter); + print("=====load in net========="); + if (ret.success) { + save(ret.data); + emit(NewsState(headerNews: ret.data)); + } + } + + @override + ConvertorList> get convertor => (e) { + return e.map(ArticlePo.fromCache).toList(); + }; +} + +class NewsState { + final List headerNews; + + NewsState({ + required this.headerNews, + }); +} diff --git a/modules/knowledge_system/note/lib/src/env/env.dart b/modules/knowledge_system/note/lib/src/env/env.dart new file mode 100644 index 000000000..40fd925fc --- /dev/null +++ b/modules/knowledge_system/note/lib/src/env/env.dart @@ -0,0 +1,25 @@ +import 'package:fx_dio/fx_dio.dart'; + +class NoteEnv with NoteModuleBridge { + static NoteEnv? _instance; + + NoteEnv._(); + + factory NoteEnv() { + _instance ??= NoteEnv._(); + return _instance!; + } + + NoteModuleBridge? _bridge; + + void attachBridge(NoteModuleBridge bridge) { + _bridge = bridge; + } + + @override + Host get host => _bridge!.host; +} + +mixin NoteModuleBridge { + Host get host; +} diff --git a/modules/knowledge_system/note/lib/src/repository/article_repository.dart b/modules/knowledge_system/note/lib/src/repository/article_repository.dart new file mode 100644 index 000000000..df436f7fc --- /dev/null +++ b/modules/knowledge_system/note/lib/src/repository/article_repository.dart @@ -0,0 +1,125 @@ +import 'dart:convert'; + +import 'package:fx_dio/fx_dio.dart'; + +import '../env/env.dart'; +import 'model/model.dart'; + +typedef PaginateList = ({List list, int total}); + +abstract class ArticleRepository { + Future> create(ArticleCreatePayload payload); + + Future> open(int id); + + Future> write(int id, String content); + + Future>> list(SizeFilter filter); + + /// 根据标签查询文章列表 + /// [tagId] 标签 id + /// [filter] 分页信息 + Future>> getArticlesByTag( + int tagId, { + SizeFilter? filter, + }); + + Future> delete(int id); + + Future> update(int id, ArticleUpdatePayload payload); + + Future> loadArticleTree(); +} + +class HttpArticleRepository implements ArticleRepository { + Host get host => NoteEnv().host; + + @override + Future> create(ArticleCreatePayload payload) { + return host.post( + '/article', + data: payload.apiData, + convertor: (rep) => rep['status'] == true, + ); + } + + @override + Future> delete(int id) { + return host.delete( + '/article/$id', + convertor: (rep) => rep['status'] == true, + ); + } + + @override + Future>> list(SizeFilter filter) { + return host.get>( + '/article', + queryParameters: { + 'page': filter.page, + 'page_size': filter.pageSize, + }, + convertor: (data) { + return data.map(ArticlePo.fromApi).toList(); + }, + ); + } + + @override + Future> open(int id) { + return host.get( + '/article/open/$id', + convertor: (rep) => rep, + ); + } + + @override + Future> write(int id, String content) { + return host.post( + '/article/write', + data: { + 'article_id': id, + 'content': content, + }, + convertor: (rep) { + return rep; + }, + ); + } + + @override + Future> update(int id, ArticleUpdatePayload payload) { + return host.put( + '/article/$id', + data: payload.apiData, + convertor: (rep) { + return ArticlePo.fromApi(rep); + }, + ); + } + + @override + Future> loadArticleTree() { + return host.get( + '/category', + convertor: (rep) { + return Hierarchy.fromJson(rep['data']); + }, + ); + } + + @override + Future>> getArticlesByTag(int tagId, + {SizeFilter? filter}) { + SizeFilter size = filter ?? const SizeFilter(); + return host.get>( + '/article/tag', + queryParameters: { + 'tag_id': tagId, + 'page': size.page, + 'page_size': size.pageSize, + }, + convertor: (data) => data.map(ArticlePo.fromApi).toList(), + ); + } +} diff --git a/modules/knowledge_system/note/lib/src/repository/model/article.dart b/modules/knowledge_system/note/lib/src/repository/model/article.dart new file mode 100644 index 000000000..5b278bbb2 --- /dev/null +++ b/modules/knowledge_system/note/lib/src/repository/model/article.dart @@ -0,0 +1,143 @@ +import 'package:fx_dao/fx_dao.dart'; +import 'package:intl/intl.dart'; + +DateFormat _noteTimeShort = DateFormat('yyyy/M/d'); +DateFormat _noteTimeLong = DateFormat('yyyy/M/d HH:mm:ss'); +Duration offset = DateTime.now().timeZoneOffset; + +enum ArticleType { + net, + custom, +} + +class ArticlePo implements Po { + final String title; + final String? subtitle; + final String url; + final String? cover; + final int create; + final int update; + final int id; + final int status; + final int type; + + ArticlePo({ + required this.title, + this.subtitle = '', + required this.url, + this.cover = '', + this.update = 0, + this.create = 0, + this.status = 0, + this.type = 0, + this.id = -1, + }); + + String get updateDate { + return _noteTimeLong + .format(DateTime.fromMillisecondsSinceEpoch(update).add(offset)); + } + + String get createDate => _noteTimeShort + .format(DateTime.fromMillisecondsSinceEpoch(create).add(offset)); + + factory ArticlePo.fromApi(dynamic map) => ArticlePo( + id: map['article_id'] ?? 0, + title: map['title'] ?? '', + type: map['type'] ?? '', + status: map['status'] ?? '', + create: DateTime.parse(map['create_at']).millisecondsSinceEpoch, + update: DateTime.parse(map['update_at']).millisecondsSinceEpoch, + subtitle: map['subtitle'] ?? '', + url: map['url'] ?? '', + cover: map['cover'] ?? '', + ); + + factory ArticlePo.fromCache(dynamic map) => ArticlePo( + id: map['article_id'] ?? 0, + title: map['title'] ?? '', + type: map['type'] ?? '', + status: map['status'] ?? '', + create: map['create_at'], + update: map['update'], + subtitle: map['subtitle'] ?? '', + url: map['url'] ?? '', + cover: map['cover'] ?? '', + ); + + @override + Map toJson() { + return { + 'article_id': id, + 'title': title, + 'type': type, + 'status': status, + 'create_at': create, + 'update': update, + 'url': url, + 'cover': cover, + }; + } +} + +class ArticleCreatePayload { + final String subtitle; + final String title; + final String url; + final int type; + final String cover; + final String createAt; + + ArticleCreatePayload({ + required this.subtitle, + required this.title, + required this.url, + required this.type, + required this.cover, + required this.createAt, + }); + + Map get apiData => { + "title": title, + "create_at": createAt, + "subtitle": subtitle, + "url": url, + "type": type, + "cover": cover, + }; + + Map toJson() => apiData; +} + +class ArticleUpdatePayload { + final String? subtitle; + final String? title; + final String? url; + final String? cover; + + ArticleUpdatePayload({ + this.subtitle, + this.title, + this.url, + this.cover, + }); + + Map get apiData { + Map ret = {}; + if (title != null) { + ret['title'] = title; + } + if (url != null) { + ret['url'] = title; + } + if (subtitle != null) { + ret['subtitle'] = title; + } + if (cover != null) { + ret['cover'] = title; + } + return ret; + } + + Map toJson() => apiData; +} diff --git a/modules/knowledge_system/note/lib/src/repository/model/category.dart b/modules/knowledge_system/note/lib/src/repository/model/category.dart new file mode 100644 index 000000000..f175d011d --- /dev/null +++ b/modules/knowledge_system/note/lib/src/repository/model/category.dart @@ -0,0 +1,28 @@ +class Hierarchy { + final int cateId; + final String name; + final int cateType; + final int priority; + final List children; + + Hierarchy({ + required this.cateId, + required this.name, + this.cateType = 0, + this.priority = 0, + this.children = const [], + }); + + factory Hierarchy.fromJson(Map json) { + return Hierarchy( + cateId: json['cate_id'] as int, + name: json['name'] as String, + cateType: json['cate_type'] as int? ?? 0, + priority: json['priority'] as int? ?? 0, + children: (json['children'] as List?) + ?.map((child) => Hierarchy.fromJson(child)) + .toList() ?? + [], + ); + } +} \ No newline at end of file diff --git a/modules/knowledge_system/note/lib/src/repository/model/model.dart b/modules/knowledge_system/note/lib/src/repository/model/model.dart new file mode 100644 index 000000000..2afcd6879 --- /dev/null +++ b/modules/knowledge_system/note/lib/src/repository/model/model.dart @@ -0,0 +1,4 @@ +export 'status.dart'; +export 'article.dart'; +export 'category.dart'; +export 'query.dart'; \ No newline at end of file diff --git a/modules/knowledge_system/note/lib/src/repository/model/query.dart b/modules/knowledge_system/note/lib/src/repository/model/query.dart new file mode 100644 index 000000000..b2d7c2363 --- /dev/null +++ b/modules/knowledge_system/note/lib/src/repository/model/query.dart @@ -0,0 +1,9 @@ +class SizeFilter { + final int pageSize; + final int page; + + const SizeFilter({ + this.pageSize = 20, + this.page = 1, + }); +} diff --git a/modules/knowledge_system/note/lib/src/repository/model/status.dart b/modules/knowledge_system/note/lib/src/repository/model/status.dart new file mode 100644 index 000000000..fe57ecebb --- /dev/null +++ b/modules/knowledge_system/note/lib/src/repository/model/status.dart @@ -0,0 +1,21 @@ +sealed class TaskStatus { + const TaskStatus(); +} + +class TaskNone extends TaskStatus { + const TaskNone(); +} + +class TaskLoading extends TaskStatus { + const TaskLoading(); +} + +class TaskSuccess extends TaskStatus { + const TaskSuccess(); +} + +class TaskFailed extends TaskStatus { + final Object? error; + final StackTrace? trace; + const TaskFailed(this.error, [this.trace]); +} diff --git a/modules/knowledge_system/note/lib/src/repository/repository.dart b/modules/knowledge_system/note/lib/src/repository/repository.dart new file mode 100644 index 000000000..7c731af37 --- /dev/null +++ b/modules/knowledge_system/note/lib/src/repository/repository.dart @@ -0,0 +1,2 @@ +export 'article_repository.dart'; +export 'model/model.dart'; diff --git a/modules/knowledge_system/note/lib/src/view/art_sys_scope.dart b/modules/knowledge_system/note/lib/src/view/art_sys_scope.dart new file mode 100644 index 000000000..c2ae1fada --- /dev/null +++ b/modules/knowledge_system/note/lib/src/view/art_sys_scope.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:note/note.dart'; + +class ArtSysScope extends StatelessWidget { + final Widget child; + + const ArtSysScope({super.key, required this.child}); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (_) => ArtSysBloc()..loadFirstFrame(), + child: child, + ); + } +} diff --git a/modules/knowledge_system/note/lib/src/view/article_admin.dart b/modules/knowledge_system/note/lib/src/view/article_admin.dart new file mode 100644 index 000000000..8038828f7 --- /dev/null +++ b/modules/knowledge_system/note/lib/src/view/article_admin.dart @@ -0,0 +1,259 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_quill/flutter_quill.dart'; +import 'package:note/note.dart'; +import 'package:tolyui/tolyui.dart'; +import 'package:app/app.dart'; +import 'article_editor.dart'; +import 'article_list.dart'; +import 'desktop/article_display.dart'; + +class ArticleAdmin extends StatefulWidget { + const ArticleAdmin({super.key}); + + @override + State createState() => _ArticleAdminState(); +} + +class _ArticleAdminState extends State { + @override + Widget build(BuildContext context) { + ArtSysBloc bloc = context.watch(); + ListStatus status = bloc.state.status; + bool hasActive = bloc.state.active != null; + + Widget table = switch (status) { + LoadingStatus() => const CupertinoActivityIndicator(), + SuccessStatus() => ArticleList( + articles: bloc.state.articles, + activeId: bloc.state.active?.id ?? -1, + onTap: bloc.select, + onUpdateTitle: bloc.updateTitle, + ), + FailedStatus() => Text("Error:${status.error}"), + }; + return Scaffold( + backgroundColor: Colors.white, + body: Row( + children: [ + Container( + width: 240, + decoration: BoxDecoration(color: Color(0xfffafbfc) + // gradient: LinearGradient(colors: [ + // Color(0xffe9f1f8), + // Color(0xffebf2f8), + // ]) + ), + child: Column( + children: [ + DragToMoveWrapper( + child: Container( + padding: EdgeInsets.symmetric(horizontal: 12), + height: 46, + child: Row( + spacing: 6, + children: [ + Icon( + Icons.note_alt_outlined, + color: Theme.of(context).primaryColor, + ), + Text( + '匠心巧记', + style: TextStyle( + fontWeight: FontWeight.bold, + color: Color(0xff242a39)), + ), + Spacer(), + TolyAction( + child: Icon( + Icons.sync, + size: 20, + color: Color(0xff242a39), + ), + onTap: () async { + bloc.loadFirstFrame(); + }, + ), + ], + ), + ), + ), + Row( + children: [ + Expanded( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 12), + child: ElevatedButton( + onPressed: bloc.newArticle, + child: Wrap( + spacing: 6, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + Icon( + Icons.add, + color: Colors.white, + ), + Text( + "新建", + style: TextStyle(fontSize: 12), + ), + ], + ), + style: FillButtonPalette( + padding: EdgeInsets.symmetric(vertical: 0), + foregroundPalette: Palette.all(Colors.white), + borderRadius: BorderRadius.circular(6), + backgroundPalette: const Palette( + normal: Color(0xff1890ff), + hover: Color(0xff40a9ff), + pressed: Color(0xff096dd9), + ), + ).style, + ), + ), + ), + ], + ), + const SizedBox(height: 16), + Expanded(child: table) + ], + ), + ), + VerticalDivider(), + Expanded( + child: Column( + children: [ + Container( + height: 46, + child: Row( + children: [ + if (hasActive) + Expanded( + child: Padding( + padding: const EdgeInsets.only(left: 12.0), + child: TextField( + onTapOutside: (_) => bloc.updateTitleV2(), + onSubmitted: (_) => bloc.updateTitleV2(), + controller: bloc.titleCtrl, + decoration: + InputDecoration(border: InputBorder.none), + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + if (!hasActive) Spacer(), + WindowButtons() + ], + ), + ), + Divider(), + // Expanded(child: RichEditor()), + Expanded(child: ArticleDisplay()), + ], + )) + // Expanded( + // child: Column( + // children: [ + // Container( + // height: 52, + // child: Padding( + // padding: const EdgeInsets.symmetric(horizontal: 12.0), + // child: Row( + // children: [ + // Spacer(), + // Button(onPressed: () => showAddDialog(context)), + // ], + // ), + // ), + // ), + // Expanded(child: table), + // Padding( + // padding: const EdgeInsets.all(8.0), + // child: TolyPagination( + // pageSize: 20, + // total: (total*1.0), + // initIndex: currentIndex, + // onPageChanged: _onPageChanged, + // ), + // ) + // ], + // ), + // ), + ], + ), + ); + } + + // void _onPageChanged(int value) { + // queryArticle(value); + // } + + // Future queryArticle(int page) async { + // setState(() { + // status = const TaskLoading(); + // }); + // ApiRet> ret = + // await _repository.list(SizeFilter(page: page)); + // if (ret.success) { + // articles = ret.data.list; + // total = ret.data.total; + // setState(() { + // status = const TaskSuccess(); + // }); + // } else { + // status = TaskFailed(ret.trace); + // setState(() {}); + // } + // } + + void showAddDialog(BuildContext context) { + showDialog( + context: context, + builder: (context) { + return EditArticleDialog( + onCreate: (payload) async { + // 在这里处理更新后的文章 + // ApiRet ret = await _repository.create(payload); + // if (ret.success) { + // currentIndex = 1; + // queryArticle(currentIndex); + // } + }, + ); + }, + ); + } +} + +class RichEditor extends StatefulWidget { + const RichEditor({super.key}); + + @override + State createState() => _RichEditorState(); +} + +class _RichEditorState extends State { + QuillController _controller = QuillController.basic(); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + QuillSimpleToolbar( + controller: _controller, + config: const QuillSimpleToolbarConfig(), + ), + Expanded( + child: QuillEditor.basic( + controller: _controller, + config: const QuillEditorConfig(), + ), + ) + ], + ); + } +} diff --git a/modules/knowledge_system/note/lib/src/view/article_editor.dart b/modules/knowledge_system/note/lib/src/view/article_editor.dart new file mode 100644 index 000000000..a1f5bddb9 --- /dev/null +++ b/modules/knowledge_system/note/lib/src/view/article_editor.dart @@ -0,0 +1,200 @@ + +import 'package:flutter/material.dart'; +import 'package:tolyui/tolyui.dart'; + +import '../repository/model/model.dart'; + + +class EditArticleDialog extends StatefulWidget { + final ArticlePo? article; + final Function(ArticlePo po)? onSave; + final Function(ArticleCreatePayload payload)? onCreate; + + const EditArticleDialog({ + this.article, + this.onSave, + this.onCreate, + }); + + @override + _EditArticleDialogState createState() => _EditArticleDialogState(); +} + +class _EditArticleDialogState extends State { + late TextEditingController _titleController; + late TextEditingController _subtitleController; + late TextEditingController _urlController; + late TextEditingController _coverController; + late TextEditingController _columnController; + + @override + void initState() { + super.initState(); + if (widget.article != null) { + _titleController = TextEditingController(text: widget.article!.title); + _subtitleController = + TextEditingController(text: widget.article!.subtitle); + _urlController = TextEditingController(text: widget.article!.url); + _coverController = TextEditingController(text: widget.article!.cover); + _columnController = TextEditingController(); + } else { + _titleController = TextEditingController(); + _subtitleController = TextEditingController(); + _urlController = TextEditingController(); + _coverController = TextEditingController(); + _columnController = TextEditingController(); + } + } + + @override + void dispose() { + _titleController.dispose(); + _subtitleController.dispose(); + _urlController.dispose(); + _coverController.dispose(); + super.dispose(); + } + + bool get editModel => widget.article != null; + + @override + Widget build(BuildContext context) { + Palette foreground = const Palette( + normal: Color(0xff606266), + hover: Color(0xff096dd9), + pressed: Color(0xff096dd9)); + Palette border = const Palette( + normal: Color(0xffd9d9d9), + hover: Color(0x44409eff), + pressed: Color(0xff096dd9)); + Palette bg = const Palette( + normal: Color(0xff1890ff), + hover: Color(0xffecf5ff), + pressed: Color(0xffecf5ff)); + return AlertDialog( + title: Text( + editModel ? '编辑文章' : '新增文章', + style: TextStyle(fontSize: 16), + ), + content: SizedBox( + width: 420, + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + spacing: 8, + children: [ + Row( + children: [ + Text('文章标题: '), + const SizedBox(width: 6), + Expanded( + child: TolyInput( + controller: _titleController, + ), + ), + ], + ), + // TextField( + // controller: _titleController, + // decoration: InputDecoration(labelText: 'Title'), + // ), + Row( + children: [ + Text('文章摘要: '), + const SizedBox(width: 6), + Expanded( + child: TolyInput( + controller: _subtitleController, + ), + ), + ], + ), + Row( + children: [ + Text('文章链接: '), + const SizedBox(width: 6), + Expanded( + child: TolyInput( + controller: _urlController, + ), + ), + ], + ), + Row( + children: [ + Text('专栏收录: '), + const SizedBox(width: 6), + Expanded( + child: TolyInput( + controller: _columnController, + ), + ), + ], + ), + if (_coverController.text.isNotEmpty) + Image.network(_coverController.text), + if (_coverController.text.isEmpty) + Row( + children: [ + Text('文章封面: '), + const SizedBox(width: 6), + Expanded( + child: TolyInput( + controller: _coverController, + ), + ), + ], + ), + ], + ), + ), + ), + actions: [ + ElevatedButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text("取消"), + style: OutlineButtonPalette( + foregroundPalette: foreground, + borderPalette: border, + backgroundPalette: bg, + ).style, + ), + ElevatedButton( + onPressed: () { + ArticleCreatePayload payload = ArticleCreatePayload( + subtitle: _subtitleController.text, + title: _titleController.text, + url: _urlController.text, + cover: _coverController.text, + type: 1, + createAt: DateTime.now().toIso8601String(), + ); + widget.onCreate?.call(payload); + }, + child: Wrap( + spacing: 6, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + Text( + "保存", + style: TextStyle(fontSize: 14), + ), + ], + ), + style: FillButtonPalette( + padding: EdgeInsets.symmetric(vertical: 0), + foregroundPalette: Palette.all(Colors.white), + borderRadius: BorderRadius.circular(6), + backgroundPalette: const Palette( + normal: Color(0xff1890ff), + hover: Color(0xff40a9ff), + pressed: Color(0xff096dd9), + ), + ).style, + ) + ], + ); + } +} diff --git a/modules/knowledge_system/note/lib/src/view/article_item.dart b/modules/knowledge_system/note/lib/src/view/article_item.dart new file mode 100644 index 000000000..a8879bd60 --- /dev/null +++ b/modules/knowledge_system/note/lib/src/view/article_item.dart @@ -0,0 +1,45 @@ +// import 'package:flutter/material.dart'; +// import 'package:tolyui/tolyui.dart'; +// +// import '../repository/model/model.dart'; +// import 'article_editor.dart'; +// +// class ArticleItem extends StatelessWidget { +// final ArticlePo article; +// final int index; +// const ArticleItem({super.key, required this.article, required this.index}); +// +// @override +// Widget build(BuildContext context) { +// return Container( +// color: index % 2 == 0 ? const Color(0xfff7f9f9) : Colors.white, +// padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 12), +// child: Row( +// children: [ +// Text( +// article.title, +// style: const TextStyle(fontWeight: FontWeight.bold), +// ), +// const Spacer(), +// TolyAction( +// child: const Icon(Icons.edit, size: 20), +// onTap: () { +// showDialog( +// context: context, +// builder: (context) { +// return EditArticleDialog( +// article: article, +// onSave: (updatedArticle) { +// // 在这里处理更新后的文章 +// // print('Updated Article: ${updatedArticle.}'); +// }, +// ); +// }, +// ); +// }) +// ], +// ), +// ); +// } +// } +// diff --git a/modules/knowledge_system/note/lib/src/view/article_list.dart b/modules/knowledge_system/note/lib/src/view/article_list.dart new file mode 100644 index 000000000..55dffa5f3 --- /dev/null +++ b/modules/knowledge_system/note/lib/src/view/article_list.dart @@ -0,0 +1,173 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/gestures/events.dart'; +import 'package:note/src/repository/model/model.dart'; +import 'package:tolyui/tolyui.dart'; + +class ArticleList extends StatelessWidget { + final List articles; + final ValueChanged onTap; + final int activeId; + final OnUpdateTitle onUpdateTitle; + + const ArticleList( + {super.key, + required this.articles, + required this.activeId, + required this.onTap, + required this.onUpdateTitle}); + + @override + Widget build(BuildContext context) { + return ListView.builder( + itemCount: articles.length, + itemBuilder: (_, index) => ArticleItem( + onTap: onTap, + active: articles[index].id == activeId, + article: articles[index], + onUpdateTitle: onUpdateTitle, + ), + ); + } +} + +typedef OnUpdateTitle = Function(ArticlePo article, String title); + +class ArticleItem extends StatefulWidget { + final bool active; + final ArticlePo article; + final ValueChanged onTap; + final OnUpdateTitle onUpdateTitle; + + const ArticleItem({ + super.key, + required this.active, + required this.article, + required this.onTap, + required this.onUpdateTitle, + }); + + @override + State createState() => _ArticleItemState(); +} + +class _ArticleItemState extends State { + @override + Widget build(BuildContext context) { + return MouseRegion( + onEnter: _onEnter, + onExit: _onExit, + child: GestureDetector( + onTap: () => widget.onTap(widget.article), + child: Container( + alignment: Alignment.centerLeft, + padding: EdgeInsets.symmetric(horizontal: 8), + margin: EdgeInsets.symmetric(horizontal: 6), + width: 240, + decoration: BoxDecoration( + color: widget.active + ? Color(0xffd7e2ff) + : _hovered + ? Color(0xffe1e6ed) + : null, + borderRadius: BorderRadius.circular(4)), + height: 32, + child: Row( + spacing: 6, + children: [ + Icon( + widget.article.type == 1 ? Icons.event_note : Icons.wordpress, + size: 20, + color: widget.active ? Color(0xff5b89fe) : Color(0xffa6aebd), + ), + Expanded( + child: _editMode + ? TextField( + focusNode: _focusNode, + decoration: InputDecoration( + isCollapsed: true, border: InputBorder.none), + style: TextStyle( + fontSize: 14, + fontWeight: widget.active ? FontWeight.bold : null, + color: widget.active ? Color(0xff5b89fe) : null), + onTapOutside: (_) { + _updateTitle(); + setState(() { + _editMode = false; + }); + }, + onSubmitted: (v) { + _updateTitle(); + setState(() { + _editMode = false; + }); + }, + controller: _ctrl, + ) + : Text( + widget.article.title, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontWeight: widget.active ? FontWeight.bold : null, + color: widget.active ? Color(0xff5b89fe) : null), + ), + ), + if (_hovered) + TolyAction( + child: Icon( + _editMode ? Icons.check : Icons.edit, + size: 16, + color: Color(0xff4e5a70), + ), + onTap: () { + if (_editMode) { + _updateTitle(); + setState(() { + _editMode = false; + }); + return; + } + + widget.onTap(widget.article); + _ctrl = TextEditingController(text: widget.article.title); + _focusNode = FocusNode(); + Future.delayed(Duration(milliseconds: 20)).then((_) { + _focusNode!.requestFocus(); + _ctrl!.value = _ctrl!.value.copyWith( + selection: TextSelection( + baseOffset: 0, + extentOffset: _ctrl!.text.length)); + }); + setState(() { + _editMode = true; + }); + }), + ], + ), + ), + ), + ); + } + + void _updateTitle() { + String value = _ctrl?.text ?? ''; + if (value.isEmpty) return; + widget.onUpdateTitle(widget.article, _ctrl?.text ?? ''); + } + + bool _hovered = false; + bool _editMode = false; + TextEditingController? _ctrl; + FocusNode? _focusNode; + void _onEnter(PointerEnterEvent event) { + setState(() { + _hovered = true; + }); + } + + void _onExit(PointerExitEvent event) { + setState(() { + _hovered = false; + }); + } +} diff --git a/modules/knowledge_system/note/lib/src/view/components/button/button.dart b/modules/knowledge_system/note/lib/src/view/components/button/button.dart new file mode 100644 index 000000000..e9f6103ee --- /dev/null +++ b/modules/knowledge_system/note/lib/src/view/components/button/button.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import 'package:tolyui/tolyui.dart'; + +class Button extends StatelessWidget { + final VoidCallback onPressed; + + const Button({super.key, required this.onPressed}); + + @override + Widget build(BuildContext context) { + return ElevatedButton( + onPressed: onPressed, + child: const Wrap( + spacing: 6, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + Icon( + Icons.add, + size: 20, + color: Colors.white, + ), + Text( + "新增", + style: TextStyle(fontSize: 14), + ), + ], + ), + style: FillButtonPalette( + foregroundPalette: const Palette.all(Colors.white), + borderRadius: BorderRadius.circular(6), + backgroundPalette: const Palette( + normal: Color(0xff1890ff), + hover: Color(0xff40a9ff), + pressed: Color(0xff096dd9), + ), + ).style, + ); + } +} diff --git a/modules/knowledge_system/note/lib/src/view/desktop/article_display.dart b/modules/knowledge_system/note/lib/src/view/desktop/article_display.dart new file mode 100644 index 000000000..1d653e41e --- /dev/null +++ b/modules/knowledge_system/note/lib/src/view/desktop/article_display.dart @@ -0,0 +1,86 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:note/note.dart'; +import 'package:tolyui/tolyui.dart'; +import '../../repository/model/article.dart'; + +class ArticleDisplay extends StatelessWidget { + const ArticleDisplay({super.key}); + + @override + Widget build(BuildContext context) { + ArticlePo? selected = + context.select((ArtSysBloc bloc) => bloc.state.active); + if (selected == null) { + return SizedBox(); + } + if (selected.type == ArticleType.net.index) { + return NetworkArticleDisplay( + article: selected, + ); + } + return TextField( + style: TextStyle(fontSize: 14), + onChanged: (text) => context.read().write(text), + maxLines: null, + minLines: null, + controller: context.read().ctrl, + expands: true, + decoration: const InputDecoration( + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric( + horizontal: 8, + vertical: 8, + )), + ); + return const Placeholder(); + } +} + +class NetworkArticleDisplay extends StatelessWidget { + final ArticlePo article; + + const NetworkArticleDisplay({super.key, required this.article}); + + @override + Widget build(BuildContext context) { + return Center( + child: Container( + padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8), + width: 300, + decoration: BoxDecoration( + border: Border.all(), borderRadius: BorderRadius.circular(8)), + child: Column( + spacing: 12, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + article.title, + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ), + if (article.subtitle != null) + Text( + article.subtitle!, + style: TextStyle(fontSize: 12, color: Colors.grey), + ), + Image.network(article.cover ?? ''), + ElevatedButton( + onPressed: () {}, + child: Text('前往查看'), + style: FillButtonPalette( + padding: EdgeInsets.symmetric(vertical: 0, horizontal: 24), + foregroundPalette: Palette.all(Colors.white), + borderRadius: BorderRadius.circular(6), + backgroundPalette: const Palette( + normal: Color(0xff1890ff), + hover: Color(0xff40a9ff), + pressed: Color(0xff096dd9), + ), + ).style, + ), + ], + ), + ), + ); + } +} diff --git a/modules/knowledge_system/note/lib/src/view/mobile/mobile_article_list.dart b/modules/knowledge_system/note/lib/src/view/mobile/mobile_article_list.dart new file mode 100644 index 000000000..60becfac1 --- /dev/null +++ b/modules/knowledge_system/note/lib/src/view/mobile/mobile_article_list.dart @@ -0,0 +1,142 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/gestures/events.dart'; +import 'package:note/src/repository/model/model.dart'; +import 'package:tolyui/tolyui.dart'; +class MobileArticleList extends StatelessWidget { + final List articles; + final ValueChanged onTap; + final int activeId; + final OnUpdateTitle onUpdateTitle; + + const MobileArticleList( + {super.key, + required this.articles, + required this.activeId, + required this.onTap, + required this.onUpdateTitle}); + + @override + Widget build(BuildContext context) { + return ListView.separated( + padding: EdgeInsets.symmetric(vertical: 8), + separatorBuilder: (_, __) => SizedBox( + height: 6, + ), + itemCount: articles.length, + itemBuilder: (_, index) => MobileArticleItem( + onTap: onTap, + active: articles[index].id == activeId, + article: articles[index], + onUpdateTitle: onUpdateTitle, + ), + ); + } +} + +typedef OnUpdateTitle = Function(int id, String title); + +class MobileArticleItem extends StatefulWidget { + final bool active; + final ArticlePo article; + final ValueChanged onTap; + final OnUpdateTitle onUpdateTitle; + + const MobileArticleItem({ + super.key, + required this.active, + required this.article, + required this.onTap, + required this.onUpdateTitle, + }); + + @override + State createState() => _MobileArticleItemState(); +} + +class _MobileArticleItemState extends State { + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () => widget.onTap(widget.article), + child: Container( + alignment: Alignment.centerLeft, + padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8), + margin: EdgeInsets.symmetric(horizontal: 8), + decoration: BoxDecoration( + color: Colors.white, borderRadius: BorderRadius.circular(4)), + child: Column( + spacing: 4, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + spacing: 6, + children: [ + Icon( + widget.article.type == 1 ? Icons.event_note : Icons.wordpress, + size: 20, + color: Color(0xffa6aebd), + ), + Expanded( + child: Text( + widget.article.title, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontWeight: FontWeight.bold,), + ), + ), + ], + ), + Row( + children: [ + Text( + style: TextStyle(fontSize: 12), + widget.article.createDate, + ), + Text(" → "), + Text( + style: TextStyle(fontSize: 12), + widget.article.updateDate, + ) + ], + ) + ], + ), + ), + ); + } + + void _updateTitle() { + String value = _ctrl?.text ?? ''; + if (value.isEmpty) return; + widget.onUpdateTitle(widget.article.id, _ctrl?.text ?? ''); + } + + TextEditingController? _ctrl; + FocusNode? _focusNode; +} + +// Container( +// alignment: Alignment.centerLeft, +// padding: EdgeInsets.symmetric(horizontal: 8), +// margin: EdgeInsets.symmetric(horizontal: 6), +// height: 32, +// width: 200, +// decoration: BoxDecoration( +// color: Color(0xffd7e2ff), +// borderRadius: BorderRadius.circular(4) +// ), +// child: Text('第一篇',style: TextStyle(fontWeight: FontWeight.bold,color: Color(0xff5b89fe)),), +// ), +// Container( +// alignment: Alignment.centerLeft, +// padding: EdgeInsets.symmetric(horizontal: 8), +// margin: EdgeInsets.symmetric(horizontal: 6), +// height: 32, +// width: 200, +// decoration: BoxDecoration( +// // color: Color(0xffd7e2ff), +// borderRadius: BorderRadius.circular(4) +// ), +// child: Text('第二篇',style: TextStyle(color: Colors.black),), +// ), diff --git a/modules/knowledge_system/note/lib/src/view/mobile/mobile_article_page.dart b/modules/knowledge_system/note/lib/src/view/mobile/mobile_article_page.dart new file mode 100644 index 000000000..817d43ed1 --- /dev/null +++ b/modules/knowledge_system/note/lib/src/view/mobile/mobile_article_page.dart @@ -0,0 +1,104 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:fx_dio/fx_dio.dart'; + +import '../../repository/article_repository.dart'; +import '../../repository/model/model.dart'; +import '../article_list.dart'; +import 'mobile_article_list.dart'; +import 'mobile_editor.dart'; +import 'note.dart'; + +class MobileArticlePage extends StatefulWidget { + const MobileArticlePage({super.key}); + + @override + State createState() => _MobileArticlePageState(); +} + +class _MobileArticlePageState extends State { + ArticleRepository _repository = HttpArticleRepository(); + + @override + void initState() { + super.initState(); + _queryScienceArticle(); + } + + List articles = []; + int total = 0; + int currentIndex = 1; + ArticlePo? active; + TaskStatus status = const TaskNone(); + + TextEditingController ctrl = TextEditingController(); + TextEditingController titleCtrl = TextEditingController(); + + Future _queryScienceArticle() async { + setState(() { + status = const TaskLoading(); + }); + ApiRet> ret = await _repository.list(SizeFilter()); + if (ret.success) { + articles = ret.data; + total = ret.paginate?.total ?? 0; + setState(() { + status = TaskSuccess(); + }); + } else { + status = TaskFailed(ret.trace); + setState(() {}); + } + } + + @override + Widget build(BuildContext context) { + return PinnedHeaderSliverNode2(); + Widget body = switch (status) { + TaskNone() => SizedBox(), + TaskLoading() => const CupertinoActivityIndicator(), + TaskSuccess() => MobileArticleList( + articles: articles, + activeId: active?.id ?? -1, + onTap: (ArticlePo article) { + if (article.type == 1) { + Navigator.of(context).push(MaterialPageRoute(builder: (ctx) { + return MobileEditor( + article: article, + ); + })); + // _loadArticleContent(article.id); + } else {} + titleCtrl.text = article.title; + + setState(() { + active = article; + }); + }, + onUpdateTitle: updateTitle, + ), + TaskFailed() => Scaffold(), + }; + return Scaffold( + backgroundColor: Color(0xfff5f5f5), + bottomNavigationBar: Container( + height: 56, + ), + floatingActionButton: FloatingActionButton( + shape: StadiumBorder(), + mini: true, + elevation: 4, + backgroundColor: Theme.of(context).primaryColor, + foregroundColor: Colors.white, + onPressed: () {}, + child: Icon(Icons.add), + ), + appBar: AppBar( + title: Text('匠心巧记'), + ), + body: body, + ); + } + + updateTitle(int id, String title) {} +} diff --git a/modules/knowledge_system/note/lib/src/view/mobile/mobile_editor.dart b/modules/knowledge_system/note/lib/src/view/mobile/mobile_editor.dart new file mode 100644 index 000000000..0326c9f11 --- /dev/null +++ b/modules/knowledge_system/note/lib/src/view/mobile/mobile_editor.dart @@ -0,0 +1,226 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:fx_dio/fx_dio.dart'; +import 'package:note/note.dart'; + +import '../../repository/article_repository.dart'; +import '../../repository/model/model.dart'; + +class MobileEditor extends StatefulWidget { + final ArticlePo article; + + const MobileEditor({super.key, required this.article}); + + @override + State createState() => _MobileEditorState(); +} + +class _MobileEditorState extends State { + TextEditingController ctrl = TextEditingController(); + ArticleRepository _repository = HttpArticleRepository(); + FocusNode titleFocusNode = FocusNode(); + + @override + void initState() { + super.initState(); + // _loadArticleContent(widget.article.id); + titleFocusNode.addListener(_titleFocusNode); + } + + // void _loadArticleContent(int id) async { + // ApiRet ret = await _repository.open(id); + // if (ret.success) { + // ctrl.text = ret.data; + // } + // } + + @override + Widget build(BuildContext context) { + ArtSysBloc bloc = context.watch(); + + return Scaffold( + backgroundColor: Color(0xfffafafa), + appBar: AppBar( + surfaceTintColor: Colors.transparent, + backgroundColor: Color(0xfffafafa), + // title: Text(widget.article.title), + actions: [IconButton(onPressed: () { + showBottomTip(context); + }, icon: Icon(Icons.more_vert))], + bottom: PreferredSize( + preferredSize: Size.fromHeight(32), + child: Padding( + padding: const EdgeInsets.only(bottom: 4.0, left: 18), + child: Row( + spacing: 8, + children: [ + Text( + '${bloc.state.active?.updateDate}', + style: TextStyle( + fontSize: 12, + color: Color( + 0xffadadad, + )), + ), + SizedBox(height: 14, child: VerticalDivider()), + Text( + '${bloc.ctrl.text.length} 字', + style: TextStyle( + fontSize: 12, + color: Color( + 0xffadadad, + )), + ), + SizedBox(height: 14, child: VerticalDivider()), + Text( + '全部文件', + style: TextStyle( + fontSize: 12, + color: Color( + 0xffadadad, + )), + ), + ], + ), + )), + ), + body: Column( + children: [ + Divider(), + TextField( + controller: bloc.titleCtrl, + focusNode: titleFocusNode, + maxLines: 4, + minLines: 1, + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + decoration: InputDecoration( + border: InputBorder.none, + isCollapsed: true, + contentPadding: EdgeInsets.only(left: 16, right: 16, top: 8)), + ), + Expanded( + child: TextField( + style: TextStyle(fontSize: 14), + onChanged: (String value) async { + if (widget.article.id != null) { + ApiRet ret = + await _repository.write(widget.article.id, value); + } + }, + maxLines: null, + minLines: null, + controller: bloc.ctrl, + expands: true, + decoration: InputDecoration( + border: InputBorder.none, + contentPadding: + EdgeInsets.symmetric(horizontal: 16, vertical: 8)), + ), + ), + ], + ), + ); + } + + void _titleFocusNode() { + print("=====_titleFocusNode============"); + if(!titleFocusNode.hasFocus){ + ArtSysBloc bloc = context.read(); + bloc.updateTitleV2(); + } + } + + void showBottomTip(BuildContext context) { + showCupertinoModalPopup( + context: context, + builder: (_) => PopBottomTip( + onDelete: () async{ + await context.read().delete(); + Navigator.of(context).pop(); + }, + message: '当前文当更多操作', + deleteText: '删除文档', + ), + ); + } +} + + +class PopBottomTip extends StatelessWidget { + final VoidCallback onDelete; + final String message; + final String deleteText; + + const PopBottomTip({ + super.key, + required this.onDelete, + required this.message, + required this.deleteText, + }); + + @override + Widget build(BuildContext context) { + return SafeArea( + bottom: true, + child: Material( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(6), + topRight: Radius.circular(6), + )), + color: Colors.white, + child: SizedBox( + width: MediaQuery.sizeOf(context).width, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + height: 52, + alignment: Alignment.center, + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + width: 0.5, + color: Colors.grey.withOpacity(0.2)))), + child: Text( + message, + style: TextStyle(color: Color(0xff8f8f8f)), + )), + + InkWell( + splashColor: Colors.white, + onTap: () { + Navigator.of(context).pop(); + onDelete(); + }, + child: Container( + height: 56, + alignment: Alignment.center, + child: Text( + deleteText, + style: TextStyle(color: Color(0xfff14835), fontSize: 16), + )), + ), + Container( + color: Color(0xfff2f3f5), + height: 8, + ), + InkWell( + splashColor: Colors.white, + onTap: () => Navigator.of(context).pop(), + child: Container( + height: 56, + alignment: Alignment.center, + child: Text( + '取消', + style: TextStyle(fontSize: 16), + )), + ), + ], + ), + ), + ), + ); + } +} diff --git a/modules/knowledge_system/note/lib/src/view/mobile/note.dart b/modules/knowledge_system/note/lib/src/view/mobile/note.dart new file mode 100644 index 000000000..a8dc13a6a --- /dev/null +++ b/modules/knowledge_system/note/lib/src/view/mobile/note.dart @@ -0,0 +1,177 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:note/note.dart'; + +import '../../repository/model/model.dart'; +import 'mobile_article_list.dart'; +import 'mobile_editor.dart'; + +class PinnedHeaderSliverNode2 extends StatefulWidget { + const PinnedHeaderSliverNode2({super.key}); + + @override + State createState() => + _PinnedHeaderSliverNode2State(); +} + +class _PinnedHeaderSliverNode2State extends State { + int count = 0; + late final ScrollController scrollController; + + @override + void initState() { + super.initState(); + scrollController = ScrollController(); + } + + @override + void dispose() { + scrollController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final ThemeData theme = Theme.of(context); + final ColorScheme colorScheme = theme.colorScheme; + ArtSysBloc bloc = context.watch(); + ListStatus status = bloc.state.status; + List articles = bloc.state.articles; + bool hasActive = bloc.state.active != null; + return Scaffold( + bottomNavigationBar: Container( + height: 52, + ), + floatingActionButton: FloatingActionButton( + shape: StadiumBorder(), + mini: true, + elevation: 4, + + backgroundColor: Theme.of(context).primaryColor, + foregroundColor: Colors.white, + onPressed: () async{ + ArtSysBloc bloc = context.read(); + await bloc.newArticle(); + ArticlePo article = bloc.state.articles.first; + bloc.select(article); + await Navigator.of(context).push(MaterialPageRoute(builder: (ctx) { + return BlocProvider.value( + value: bloc, + child: MobileEditor( + article: article, + ), + ); + })); + }, + child: Icon(Icons.add), + ), + backgroundColor: Color(0xfff5f5f5), + body: CustomScrollView( + controller: scrollController, + slivers: [ + _buildSliverBar(), + _buildTitleText(), + const PinnedHeaderSliver(child: Divider()), + ...ListView.separated( + padding: EdgeInsets.symmetric(vertical: 8), + separatorBuilder: (_, __) => SizedBox( + height: 6, + ), + itemCount: articles.length, + itemBuilder: (_, index) => MobileArticleItem( + onTap: _onTap, + active: false, + article: articles[index], + onUpdateTitle: onUpdateTitle, + ), + ).buildSlivers(context), + ], + ), + ); + } + + Widget _buildSliverBar() { + const Icon icon = Icon(Icons.more_vert); + const TextStyle style = + TextStyle(fontSize: 16, fontWeight: FontWeight.bold); + const Text text = Text('匠心巧记', style: style); + Widget action = IconButton(onPressed: () {}, icon: icon); + return SliverLayoutBuilder(builder: (_, scs) { + double factor = (scs.scrollOffset / kToolbarHeight).clamp(0, 1); + factor = factor < 0.2 ? 0 : factor; + AppBar header = AppBar( + backgroundColor: Color(0xfff5f5f5), + surfaceTintColor: Colors.transparent, + actions: [action], + centerTitle: true, + title: Opacity(opacity: factor, child: text), + ); + return PinnedHeaderSliver(child: header); + }); + } + + Widget _buildTitleText() { + const TextStyle style = + TextStyle(fontSize: 20, fontWeight: FontWeight.bold); + const Text text = Text('匠心巧记', style: style); + return const SliverToBoxAdapter( + child: Padding( + padding: EdgeInsets.only(left: 12.0, bottom: 8), + child: text, + ), + ); + } + + void _onTap(ArticlePo article) async { + if (article.type == 1) { + ArtSysBloc bloc = context.read(); + bloc.select(article); + await Navigator.of(context).push(MaterialPageRoute(builder: (ctx) { + return BlocProvider.value( + value: context.read(), + child: MobileEditor( + article: article, + ), + ); + })); + // _loadArticleContent(article.id); + } else {} + } + + onUpdateTitle(int id, String title) {} +} + +class _ItemList extends StatelessWidget { + const _ItemList({ + super.key, + this.itemCount = 50, + }); + + final int itemCount; + + @override + Widget build(BuildContext context) { + final ColorScheme colorScheme = Theme.of(context).colorScheme; + return SliverList( + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + return ListTile( + dense: true, + textColor: colorScheme.secondary, + title: Text('#$index title'), + subtitle: Text('Subtitle in line $index'), + ); + }, + childCount: itemCount, + ), + ); + } +} diff --git a/modules/knowledge_system/note/lib/src/view/news/news_page.dart b/modules/knowledge_system/note/lib/src/view/news/news_page.dart new file mode 100644 index 000000000..5e6d84ca9 --- /dev/null +++ b/modules/knowledge_system/note/lib/src/view/news/news_page.dart @@ -0,0 +1,84 @@ +import 'package:flutter/material.dart'; +import 'package:fx_dio/fx_dio.dart'; +import 'package:note/note.dart'; + +class NewsPage extends StatefulWidget { + final String title; + const NewsPage({super.key, required this.title}); + + @override + State createState() => _NewsPageState(); +} + +class _NewsPageState extends State { + List data = []; + final ArticleRepository _repository = HttpArticleRepository(); + + @override + void initState() { + super.initState(); + _loadData(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + ), + body: ListView.builder( + padding: EdgeInsets.symmetric(vertical: 4), + itemExtent: 76, + itemCount: data.length, + itemBuilder: (_, index) => MouseRegion( + cursor: SystemMouseCursors.click, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 4), + child: Row( + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(4), + child: Image.network( + data[index].cover ?? '', + width: 68, + height: 68, + fit: BoxFit.cover, + ), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + data[index].title, + style: TextStyle( + fontSize: 16, fontWeight: FontWeight.bold), + ), + Text( + data[index].subtitle ?? '', + maxLines: 2, + style: TextStyle(fontSize: 12, color: Colors.grey), + ), + ], + ), + ), + ], + ), + ), + ), + )); + } + + void _loadData() async { + ApiRet> ret = await _repository.getArticlesByTag(1, + filter: const SizeFilter( + page: 1, + pageSize: 40, + )); + if (ret.success) { + data = ret.data; + setState(() {}); + } + } +} diff --git a/modules/knowledge_system/note/lib/src/view/view.dart b/modules/knowledge_system/note/lib/src/view/view.dart new file mode 100644 index 000000000..d5a50f181 --- /dev/null +++ b/modules/knowledge_system/note/lib/src/view/view.dart @@ -0,0 +1,4 @@ +export 'article_admin.dart'; +export 'art_sys_scope.dart'; +export 'mobile/mobile_article_page.dart'; +export 'news/news_page.dart'; diff --git a/modules/knowledge_system/note/pubspec.yaml b/modules/knowledge_system/note/pubspec.yaml new file mode 100644 index 000000000..04cb81d81 --- /dev/null +++ b/modules/knowledge_system/note/pubspec.yaml @@ -0,0 +1,61 @@ +name: note +description: "A new Flutter package project." +version: 0.0.1 +homepage: + +environment: + sdk: ^3.6.1 + flutter: ">=1.17.0" + +resolution: workspace + +dependencies: + flutter: + sdk: flutter + fx_dio: 0.0.4+3 + fx_dao: 0.0.3+4 + flutter_bloc: ^8.1.6 # 状态管理 + two_dimensional_scrollables: ^0.3.3 + flutter_quill: ^11.0.0-dev.21 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^4.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # To add assets to your package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/to/asset-from-package + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/to/resolution-aware-images + + # To add custom fonts to your package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/to/font-from-package diff --git a/modules/knowledge_system/note/test/note_test.dart b/modules/knowledge_system/note/test/note_test.dart new file mode 100644 index 000000000..cbe754408 --- /dev/null +++ b/modules/knowledge_system/note/test/note_test.dart @@ -0,0 +1,3 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:note/note.dart'; diff --git a/modules/knowledge_system/note/test/parser.dart b/modules/knowledge_system/note/test/parser.dart new file mode 100644 index 000000000..863dce2f2 --- /dev/null +++ b/modules/knowledge_system/note/test/parser.dart @@ -0,0 +1,68 @@ +import 'dart:convert'; + +main(){ + // 示例 JSON 字符串 + final jsonString = ''' + { + "status": true, + "msg": "请求成功!", + "data": { + "cate_id": 1, + "name": "匠心空间", + "cate_type": 0, + "priority": 0, + "children": [ + { + "cate_id": 2, + "name": "全部笔记", + "children": [ + {"cate_id":4, "name":"我的随笔"}, + {"cate_id":5, "name":"编程技术"} + ] + }, + { + "cate_id": 3, + "name": "网络博文", + "children": [ + {"cate_id":6, "name":"掘金文章"}, + {"cate_id":7, "name":"微信公众号"} + ] + } + ] + } + } + '''; + // 解析过程 + final Map parsedJson = jsonDecode(jsonString); + final Category category = Category.fromJson(parsedJson['data']); + print(category); +} + +class Category { + final int cateId; + final String name; + final int cateType; + final int priority; + final List children; + + Category({ + required this.cateId, + required this.name, + this.cateType = 0, + this.priority = 0, + this.children = const [], + }); + + factory Category.fromJson(Map json) { + return Category( + cateId: json['cate_id'] as int, + name: json['name'] as String, + cateType: json['cate_type'] as int? ?? 0, + priority: json['priority'] as int? ?? 0, + children: (json['children'] as List?) + ?.map((child) => Category.fromJson(child)) + .toList() ?? + [], + ); + } +} \ No newline at end of file diff --git a/modules/painting_system/draw_system/.gitignore b/modules/painting_system/draw_system/.gitignore new file mode 100644 index 000000000..ac5aa9893 --- /dev/null +++ b/modules/painting_system/draw_system/.gitignore @@ -0,0 +1,29 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +build/ diff --git a/modules/painting_system/draw_system/.metadata b/modules/painting_system/draw_system/.metadata new file mode 100644 index 000000000..fe59252be --- /dev/null +++ b/modules/painting_system/draw_system/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9" + channel: "stable" + +project_type: package diff --git a/modules/painting_system/draw_system/CHANGELOG.md b/modules/painting_system/draw_system/CHANGELOG.md new file mode 100644 index 000000000..41cc7d819 --- /dev/null +++ b/modules/painting_system/draw_system/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/modules/painting_system/draw_system/LICENSE b/modules/painting_system/draw_system/LICENSE new file mode 100644 index 000000000..ba75c69f7 --- /dev/null +++ b/modules/painting_system/draw_system/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/modules/painting_system/draw_system/README.md b/modules/painting_system/draw_system/README.md new file mode 100644 index 000000000..02fe8ecab --- /dev/null +++ b/modules/painting_system/draw_system/README.md @@ -0,0 +1,39 @@ + + +TODO: Put a short description of the package here that helps potential users +know whether this package might be useful for them. + +## Features + +TODO: List what your package can do. Maybe include images, gifs, or videos. + +## Getting started + +TODO: List prerequisites and provide or point to information on how to +start using the package. + +## Usage + +TODO: Include short and useful examples for package users. Add longer examples +to `/example` folder. + +```dart +const like = 'sample'; +``` + +## Additional information + +TODO: Tell users more about the package: where to find more information, how to +contribute to the package, how to file issues, what response they can expect +from the package authors, and more. diff --git a/modules/painting_system/draw_system/analysis_options.yaml b/modules/painting_system/draw_system/analysis_options.yaml new file mode 100644 index 000000000..a5744c1cf --- /dev/null +++ b/modules/painting_system/draw_system/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/modules/painting_system/draw_system/lib/draw_system.dart b/modules/painting_system/draw_system/lib/draw_system.dart new file mode 100644 index 000000000..ca066748e --- /dev/null +++ b/modules/painting_system/draw_system/lib/draw_system.dart @@ -0,0 +1,6 @@ +library draw_system; + + +export 'src/gallery_detail_page.dart'; +export 'src/gallery_unit.dart'; +export 'src/bloc/gallery_unit/bloc.dart'; \ No newline at end of file diff --git a/lib/painter_system/anim/bezier3_player/bezier3_palyer.dart b/modules/painting_system/draw_system/lib/src/anim/bezier3_player/bezier3_palyer.dart similarity index 100% rename from lib/painter_system/anim/bezier3_player/bezier3_palyer.dart rename to modules/painting_system/draw_system/lib/src/anim/bezier3_player/bezier3_palyer.dart diff --git a/lib/painter_system/anim/bezier3_player/touch_info.dart b/modules/painting_system/draw_system/lib/src/anim/bezier3_player/touch_info.dart similarity index 100% rename from lib/painter_system/anim/bezier3_player/touch_info.dart rename to modules/painting_system/draw_system/lib/src/anim/bezier3_player/touch_info.dart diff --git a/lib/painter_system/anim/circle_halo.dart b/modules/painting_system/draw_system/lib/src/anim/circle_halo.dart similarity index 100% rename from lib/painter_system/anim/circle_halo.dart rename to modules/painting_system/draw_system/lib/src/anim/circle_halo.dart diff --git a/lib/painter_system/anim/curve_shower/anim_painter.dart b/modules/painting_system/draw_system/lib/src/anim/curve_shower/anim_painter.dart similarity index 100% rename from lib/painter_system/anim/curve_shower/anim_painter.dart rename to modules/painting_system/draw_system/lib/src/anim/curve_shower/anim_painter.dart diff --git a/lib/painter_system/anim/curve_shower/curve_anim_shower.dart b/modules/painting_system/draw_system/lib/src/anim/curve_shower/curve_anim_shower.dart similarity index 98% rename from lib/painter_system/anim/curve_shower/curve_anim_shower.dart rename to modules/painting_system/draw_system/lib/src/anim/curve_shower/curve_anim_shower.dart index bf37e495e..b45b72b9e 100644 --- a/lib/painter_system/anim/curve_shower/curve_anim_shower.dart +++ b/modules/painting_system/draw_system/lib/src/anim/curve_shower/curve_anim_shower.dart @@ -1,4 +1,4 @@ -import 'package:components/toly_ui/toly_ui.dart'; +import 'package:toly_ui/toly_ui.dart'; import 'package:flutter/material.dart'; import 'anim_painter.dart'; diff --git a/lib/painter_system/anim/curve_shower/point_data.dart b/modules/painting_system/draw_system/lib/src/anim/curve_shower/point_data.dart similarity index 100% rename from lib/painter_system/anim/curve_shower/point_data.dart rename to modules/painting_system/draw_system/lib/src/anim/curve_shower/point_data.dart diff --git a/lib/painter_system/anim/draw_path.dart b/modules/painting_system/draw_system/lib/src/anim/draw_path.dart similarity index 100% rename from lib/painter_system/anim/draw_path.dart rename to modules/painting_system/draw_system/lib/src/anim/draw_path.dart diff --git a/lib/painter_system/anim/dundun_path.dart b/modules/painting_system/draw_system/lib/src/anim/dundun_path.dart similarity index 100% rename from lib/painter_system/anim/dundun_path.dart rename to modules/painting_system/draw_system/lib/src/anim/dundun_path.dart diff --git a/lib/painter_system/anim/rotate_by_point/angle_panter.dart b/modules/painting_system/draw_system/lib/src/anim/rotate_by_point/angle_panter.dart similarity index 100% rename from lib/painter_system/anim/rotate_by_point/angle_panter.dart rename to modules/painting_system/draw_system/lib/src/anim/rotate_by_point/angle_panter.dart diff --git a/lib/painter_system/anim/rotate_by_point/line.dart b/modules/painting_system/draw_system/lib/src/anim/rotate_by_point/line.dart similarity index 100% rename from lib/painter_system/anim/rotate_by_point/line.dart rename to modules/painting_system/draw_system/lib/src/anim/rotate_by_point/line.dart diff --git a/lib/painter_system/anim/rotate_by_point/rotate_by_point.dart b/modules/painting_system/draw_system/lib/src/anim/rotate_by_point/rotate_by_point.dart similarity index 100% rename from lib/painter_system/anim/rotate_by_point/rotate_by_point.dart rename to modules/painting_system/draw_system/lib/src/anim/rotate_by_point/rotate_by_point.dart diff --git a/lib/painter_system/anim/spring_widget.dart b/modules/painting_system/draw_system/lib/src/anim/spring_widget.dart similarity index 100% rename from lib/painter_system/anim/spring_widget.dart rename to modules/painting_system/draw_system/lib/src/anim/spring_widget.dart diff --git a/lib/painter_system/art/circle_packing.dart b/modules/painting_system/draw_system/lib/src/art/circle_packing.dart similarity index 100% rename from lib/painter_system/art/circle_packing.dart rename to modules/painting_system/draw_system/lib/src/art/circle_packing.dart diff --git a/lib/painter_system/art/cubic_disarray.dart b/modules/painting_system/draw_system/lib/src/art/cubic_disarray.dart similarity index 100% rename from lib/painter_system/art/cubic_disarray.dart rename to modules/painting_system/draw_system/lib/src/art/cubic_disarray.dart diff --git a/lib/painter_system/art/hypnotic_squares.dart b/modules/painting_system/draw_system/lib/src/art/hypnotic_squares.dart similarity index 100% rename from lib/painter_system/art/hypnotic_squares.dart rename to modules/painting_system/draw_system/lib/src/art/hypnotic_squares.dart diff --git a/lib/painter_system/art/joy_division.dart b/modules/painting_system/draw_system/lib/src/art/joy_division.dart similarity index 100% rename from lib/painter_system/art/joy_division.dart rename to modules/painting_system/draw_system/lib/src/art/joy_division.dart diff --git a/lib/painter_system/art/piet_mondrian.dart b/modules/painting_system/draw_system/lib/src/art/piet_mondrian.dart similarity index 100% rename from lib/painter_system/art/piet_mondrian.dart rename to modules/painting_system/draw_system/lib/src/art/piet_mondrian.dart diff --git a/lib/painter_system/art/tiled_lines.dart b/modules/painting_system/draw_system/lib/src/art/tiled_lines.dart similarity index 100% rename from lib/painter_system/art/tiled_lines.dart rename to modules/painting_system/draw_system/lib/src/art/tiled_lines.dart diff --git a/lib/painter_system/art/triangular_mesh.dart b/modules/painting_system/draw_system/lib/src/art/triangular_mesh.dart similarity index 100% rename from lib/painter_system/art/triangular_mesh.dart rename to modules/painting_system/draw_system/lib/src/art/triangular_mesh.dart diff --git a/lib/painter_system/art/un_deux_trois.dart b/modules/painting_system/draw_system/lib/src/art/un_deux_trois.dart similarity index 100% rename from lib/painter_system/art/un_deux_trois.dart rename to modules/painting_system/draw_system/lib/src/art/un_deux_trois.dart diff --git a/lib/painter_system/base/clock_widget.dart b/modules/painting_system/draw_system/lib/src/base/clock_widget.dart similarity index 100% rename from lib/painter_system/base/clock_widget.dart rename to modules/painting_system/draw_system/lib/src/base/clock_widget.dart diff --git a/lib/painter_system/base/digital/digital_painter.dart b/modules/painting_system/draw_system/lib/src/base/digital/digital_painter.dart similarity index 100% rename from lib/painter_system/base/digital/digital_painter.dart rename to modules/painting_system/draw_system/lib/src/base/digital/digital_painter.dart diff --git a/lib/painter_system/base/digital/digital_path.dart b/modules/painting_system/draw_system/lib/src/base/digital/digital_path.dart similarity index 100% rename from lib/painter_system/base/digital/digital_path.dart rename to modules/painting_system/draw_system/lib/src/base/digital/digital_path.dart diff --git a/lib/painter_system/base/digital/digital_shower.dart b/modules/painting_system/draw_system/lib/src/base/digital/digital_shower.dart similarity index 100% rename from lib/painter_system/base/digital/digital_shower.dart rename to modules/painting_system/draw_system/lib/src/base/digital/digital_shower.dart diff --git a/lib/painter_system/base/digital/digital_widget.dart b/modules/painting_system/draw_system/lib/src/base/digital/digital_widget.dart similarity index 100% rename from lib/painter_system/base/digital/digital_widget.dart rename to modules/painting_system/draw_system/lib/src/base/digital/digital_widget.dart diff --git a/lib/painter_system/base/draw_grid_axis.dart b/modules/painting_system/draw_system/lib/src/base/draw_grid_axis.dart similarity index 100% rename from lib/painter_system/base/draw_grid_axis.dart rename to modules/painting_system/draw_system/lib/src/base/draw_grid_axis.dart diff --git a/lib/painter_system/base/draw_path_fun.dart b/modules/painting_system/draw_system/lib/src/base/draw_path_fun.dart similarity index 100% rename from lib/painter_system/base/draw_path_fun.dart rename to modules/painting_system/draw_system/lib/src/base/draw_path_fun.dart diff --git a/lib/painter_system/base/draw_picture.dart b/modules/painting_system/draw_system/lib/src/base/draw_picture.dart similarity index 100% rename from lib/painter_system/base/draw_picture.dart rename to modules/painting_system/draw_system/lib/src/base/draw_picture.dart diff --git a/lib/painter_system/base/n_side/n_side_page.dart b/modules/painting_system/draw_system/lib/src/base/n_side/n_side_page.dart similarity index 100% rename from lib/painter_system/base/n_side/n_side_page.dart rename to modules/painting_system/draw_system/lib/src/base/n_side/n_side_page.dart diff --git a/lib/painter_system/base/n_side/shape_painter.dart b/modules/painting_system/draw_system/lib/src/base/n_side/shape_painter.dart similarity index 100% rename from lib/painter_system/base/n_side/shape_painter.dart rename to modules/painting_system/draw_system/lib/src/base/n_side/shape_painter.dart diff --git a/lib/painter_system/base/polar/angle_painter.dart b/modules/painting_system/draw_system/lib/src/base/polar/angle_painter.dart similarity index 100% rename from lib/painter_system/base/polar/angle_painter.dart rename to modules/painting_system/draw_system/lib/src/base/polar/angle_painter.dart diff --git a/lib/painter_system/base/polar/polar.dart b/modules/painting_system/draw_system/lib/src/base/polar/polar.dart similarity index 100% rename from lib/painter_system/base/polar/polar.dart rename to modules/painting_system/draw_system/lib/src/base/polar/polar.dart diff --git a/lib/painter_system/base/polar/polar_painter_widget.dart b/modules/painting_system/draw_system/lib/src/base/polar/polar_painter_widget.dart similarity index 100% rename from lib/painter_system/base/polar/polar_painter_widget.dart rename to modules/painting_system/draw_system/lib/src/base/polar/polar_painter_widget.dart diff --git a/lib/painter_system/base/windmill.dart b/modules/painting_system/draw_system/lib/src/base/windmill.dart similarity index 100% rename from lib/painter_system/base/windmill.dart rename to modules/painting_system/draw_system/lib/src/base/windmill.dart diff --git a/lib/painter_system/bloc/gallery_unit/bloc.dart b/modules/painting_system/draw_system/lib/src/bloc/gallery_unit/bloc.dart similarity index 100% rename from lib/painter_system/bloc/gallery_unit/bloc.dart rename to modules/painting_system/draw_system/lib/src/bloc/gallery_unit/bloc.dart diff --git a/lib/painter_system/desk_ui/desk_frame.dart b/modules/painting_system/draw_system/lib/src/desk_ui/desk_frame.dart similarity index 90% rename from lib/painter_system/desk_ui/desk_frame.dart rename to modules/painting_system/draw_system/lib/src/desk_ui/desk_frame.dart index 6edbc0c91..143c8dbfb 100644 --- a/lib/painter_system/desk_ui/desk_frame.dart +++ b/modules/painting_system/draw_system/lib/src/desk_ui/desk_frame.dart @@ -1,8 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:components/toly_ui/toly_ui.dart'; - -import 'package:flutter_unit/painter_system/picture_frame.dart'; +import '../picture_frame.dart'; +import 'package:l10n/l10n.dart'; /// create by 张风捷特烈 on 2020/12/4 /// contact me by email 1981462002@qq.com @@ -44,9 +43,9 @@ class DeskFrameShower extends StatelessWidget { "作者: $author ", style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold), ), - const Text( - "源码地址 ", - style: TextStyle( + Text( + "${context.l10n.srcPath} ", + style: const TextStyle( fontSize: 12, fontWeight: FontWeight.bold, color: Colors.blueAccent), diff --git a/lib/painter_system/desk_ui/desk_gallery_unit.dart b/modules/painting_system/draw_system/lib/src/desk_ui/desk_gallery_unit.dart similarity index 77% rename from lib/painter_system/desk_ui/desk_gallery_unit.dart rename to modules/painting_system/draw_system/lib/src/desk_ui/desk_gallery_unit.dart index 441f92b7a..fef1eb291 100644 --- a/lib/painter_system/desk_ui/desk_gallery_unit.dart +++ b/modules/painting_system/draw_system/lib/src/desk_ui/desk_gallery_unit.dart @@ -1,15 +1,15 @@ import 'dart:convert'; import 'package:app/app.dart'; +import 'package:components/components.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:components/toly_ui/toly_ui.dart'; +import 'package:l10n/l10n.dart'; -import 'package:flutter_unit/painter_system/gallery_card_item.dart'; -import 'package:flutter_unit/painter_system/gallery_factory.dart'; +import 'package:toly_ui/toly_ui.dart'; -import '../../point_system/views/desk_ui/desk_point_page.dart'; +import '../gallery_card_item.dart'; import '../gallery_detail_page.dart'; +import '../gallery_factory.dart'; /// create by 张风捷特烈 on 2020/11/28 /// contact me by email 1981462002@qq.com @@ -52,8 +52,8 @@ class _DeskGalleryUnitState extends State { children: [ SimpleDeskTopBar( leading: Text( - 'Flutter 绘制集录', - style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + 'Flutter ${context.l10n.paintCollection}', + style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), ), Expanded(child: _buildContent()), @@ -64,9 +64,9 @@ class _DeskGalleryUnitState extends State { Widget _buildContent() { final List widgets = - (json.decode(StrUnit.galleryInfo) as List).map((e) { + (json.decode(StrUnit.galleryDesc(context)) as List).map((e) { GalleryInfo info = GalleryInfo.fromJson(e); - List children = GalleryFactory.getGalleryByName(info.type); + List children = GalleryFactory.getGalleryByName(info.type,context); return FeedbackWidget( a: 0.95, @@ -86,16 +86,16 @@ class _DeskGalleryUnitState extends State { SliverGridDelegate gridDelegate = const SliverGridDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: 460, - mainAxisSpacing: 10, - mainAxisExtent: 360, - crossAxisSpacing: 10, + maxCrossAxisExtent: 320, + mainAxisSpacing: 8, + mainAxisExtent: 340, + crossAxisSpacing: 8, ); return GridView.builder( controller: controller, gridDelegate: gridDelegate, - padding: const EdgeInsets.all(20), + padding: const EdgeInsets.all(12), itemCount: widgets.length, itemBuilder: (ctx, index) => widgets[index]); } diff --git a/lib/painter_system/fun/bufeng/bufeng_panel.dart b/modules/painting_system/draw_system/lib/src/fun/bufeng/bufeng_panel.dart similarity index 97% rename from lib/painter_system/fun/bufeng/bufeng_panel.dart rename to modules/painting_system/draw_system/lib/src/fun/bufeng/bufeng_panel.dart index 2366d017f..3a5341584 100644 --- a/lib/painter_system/fun/bufeng/bufeng_panel.dart +++ b/modules/painting_system/draw_system/lib/src/fun/bufeng/bufeng_panel.dart @@ -27,7 +27,7 @@ class _BufengPanelState extends State { alignment: Alignment.topCenter, children: [ Padding( - padding: EdgeInsets.only(top: 10), + padding: const EdgeInsets.only(top: 10), child: CustomPaint( painter: PiPainter(config), size: config.size, diff --git a/lib/painter_system/fun/bufeng/config.dart b/modules/painting_system/draw_system/lib/src/fun/bufeng/config.dart similarity index 100% rename from lib/painter_system/fun/bufeng/config.dart rename to modules/painting_system/draw_system/lib/src/fun/bufeng/config.dart diff --git a/lib/painter_system/fun/bufeng/painter.dart b/modules/painting_system/draw_system/lib/src/fun/bufeng/painter.dart similarity index 97% rename from lib/painter_system/fun/bufeng/painter.dart rename to modules/painting_system/draw_system/lib/src/fun/bufeng/painter.dart index e15cf9ebb..3abe7fba7 100644 --- a/lib/painter_system/fun/bufeng/painter.dart +++ b/modules/painting_system/draw_system/lib/src/fun/bufeng/painter.dart @@ -1,4 +1,3 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'config.dart'; diff --git a/lib/painter_system/fun/dundun_view.dart b/modules/painting_system/draw_system/lib/src/fun/dundun_view.dart similarity index 100% rename from lib/painter_system/fun/dundun_view.dart rename to modules/painting_system/draw_system/lib/src/fun/dundun_view.dart diff --git a/lib/painter_system/fun/random_portrait.dart b/modules/painting_system/draw_system/lib/src/fun/random_portrait.dart similarity index 100% rename from lib/painter_system/fun/random_portrait.dart rename to modules/painting_system/draw_system/lib/src/fun/random_portrait.dart diff --git a/lib/painter_system/fun/stemp/stamp_data.dart b/modules/painting_system/draw_system/lib/src/fun/stemp/stamp_data.dart similarity index 100% rename from lib/painter_system/fun/stemp/stamp_data.dart rename to modules/painting_system/draw_system/lib/src/fun/stemp/stamp_data.dart diff --git a/lib/painter_system/fun/stemp/stamp_paper.dart b/modules/painting_system/draw_system/lib/src/fun/stemp/stamp_paper.dart similarity index 98% rename from lib/painter_system/fun/stemp/stamp_paper.dart rename to modules/painting_system/draw_system/lib/src/fun/stemp/stamp_paper.dart index a9f1ebbdb..1be90b4ad 100644 --- a/lib/painter_system/fun/stemp/stamp_paper.dart +++ b/modules/painting_system/draw_system/lib/src/fun/stemp/stamp_paper.dart @@ -42,7 +42,7 @@ class _StampPaperState extends State @override Widget build(BuildContext context) { - width = MediaQuery.of(context).size.shortestSide * 0.8; + width = MediaQuery.of(context).size.shortestSide * 0.5; return GestureDetector( onTapDown: _onTapDown, diff --git a/lib/painter_system/gallery_card_item.dart b/modules/painting_system/draw_system/lib/src/gallery_card_item.dart similarity index 82% rename from lib/painter_system/gallery_card_item.dart rename to modules/painting_system/draw_system/lib/src/gallery_card_item.dart index f87d13caa..9bded3955 100644 --- a/lib/painter_system/gallery_card_item.dart +++ b/modules/painting_system/draw_system/lib/src/gallery_card_item.dart @@ -1,7 +1,9 @@ -import 'package:components/toly_ui/toly_ui.dart'; + import 'package:flutter/material.dart'; -import 'package:flutter_unit/app/utils/convert.dart'; -import 'package:flutter_unit/painter_system/gallery_factory.dart'; +import 'package:toly_ui/toly_ui.dart'; +import 'package:l10n/l10n.dart'; +import 'gallery_factory.dart'; + /// create by 张风捷特烈 on 2020/11/28 @@ -34,7 +36,7 @@ class GalleryCardItem extends StatelessWidget { Hero( tag: galleryInfo.name, child: Container( - height: 160, + height: 150, decoration: BoxDecoration( image: DecorationImage( fit: BoxFit.cover, @@ -49,13 +51,13 @@ class GalleryCardItem extends StatelessWidget { ), Padding( padding: const EdgeInsets.only( - top: 20, left: 15, right: 15, bottom: 10), + top: 8, left: 15, right: 15, bottom: 8), child: Row( children: [ Text( galleryInfo.name, style: TextStyle( - fontSize: 24, + fontSize: 18, fontWeight: FontWeight.bold, shadows: [ Shadow( @@ -70,7 +72,7 @@ class GalleryCardItem extends StatelessWidget { padding: const EdgeInsets.only(left: 6, right: 6, top: 2, bottom: 3), child: Text( - "$count 幅", + "$count ${context.l10n.picture}", style: const TextStyle( fontWeight: FontWeight.bold, color: Colors.white, @@ -81,10 +83,14 @@ class GalleryCardItem extends StatelessWidget { ), ), Padding( - padding: const EdgeInsets.only(left: 15, right: 15, top: 10), + padding: const EdgeInsets.only(left: 15, right: 15, top: 6), child: Text( galleryInfo.info, - style: TextStyle(color: isDark?Colors.white:Colors.grey, shadows: [ + maxLines: 7, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 12, + color: isDark?Colors.white:Colors.grey, shadows: [ Shadow( color: Theme.of(context).primaryColor, offset: const Offset(.2, .2), @@ -103,6 +109,14 @@ class GalleryCardItem extends StatelessWidget { } } +const Map galleryTypeMap = { + GalleryType.base: "基础绘制", + GalleryType.fun: "趣味绘制", + GalleryType.particle: "粒子绘制", + GalleryType.anim: "动画手势", + GalleryType.art: "艺术画廊", +}; + class GalleryInfo { final int count; final String name; @@ -112,7 +126,7 @@ class GalleryInfo { GalleryType get type { GalleryType galleryType = GalleryType.base; - Convert.galleryTypeMap.forEach((key, value) { + galleryTypeMap.forEach((key, value) { if (value == name) { galleryType = key; } diff --git a/lib/painter_system/gallery_detail_page.dart b/modules/painting_system/draw_system/lib/src/gallery_detail_page.dart similarity index 96% rename from lib/painter_system/gallery_detail_page.dart rename to modules/painting_system/draw_system/lib/src/gallery_detail_page.dart index 56ab945b3..9bf828084 100644 --- a/lib/painter_system/gallery_detail_page.dart +++ b/modules/painting_system/draw_system/lib/src/gallery_detail_page.dart @@ -1,10 +1,12 @@ import 'dart:io'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:components/toly_ui/toly_ui.dart'; +import 'package:toly_ui/toly_ui.dart'; + +import 'gallery_card_item.dart'; -import 'package:flutter_unit/painter_system/gallery_card_item.dart'; /// create by 张风捷特烈 on 2020/12/4 @@ -181,7 +183,7 @@ class _GalleryDetailPageState extends State { } } - bool isDesk = Platform.isMacOS || Platform.isWindows || Platform.isLinux; + bool isDesk = kIsWeb || Platform.isMacOS || Platform.isWindows || Platform.isLinux; Widget buildTitle(BuildContext context) { diff --git a/modules/painting_system/draw_system/lib/src/gallery_factory.dart b/modules/painting_system/draw_system/lib/src/gallery_factory.dart new file mode 100644 index 000000000..84e84328a --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/gallery_factory.dart @@ -0,0 +1,254 @@ +import 'package:flutter/material.dart'; + +import 'anim/bezier3_player/bezier3_palyer.dart'; +import 'anim/circle_halo.dart'; +import 'anim/curve_shower/curve_anim_shower.dart'; +import 'anim/draw_path.dart'; +import 'anim/dundun_path.dart'; +import 'anim/rotate_by_point/rotate_by_point.dart'; +import 'anim/spring_widget.dart'; +import 'art/circle_packing.dart'; +import 'art/cubic_disarray.dart'; +import 'art/hypnotic_squares.dart'; +import 'art/joy_division.dart'; +import 'art/piet_mondrian.dart'; +import 'art/tiled_lines.dart'; +import 'art/triangular_mesh.dart'; +import 'art/un_deux_trois.dart'; +import 'base/clock_widget.dart'; +import 'base/digital/digital_shower.dart'; +import 'base/draw_grid_axis.dart'; +import 'base/draw_path_fun.dart'; +import 'base/draw_picture.dart'; + +import 'base/n_side/n_side_page.dart'; +import 'base/polar/polar_painter_widget.dart'; +import 'base/windmill.dart'; +import 'fun/bufeng/bufeng_panel.dart'; +import 'fun/dundun_view.dart'; +import 'fun/random_portrait.dart'; +import 'fun/stemp/stamp_paper.dart'; +import 'particle/random/random_particle.dart'; +import 'particle/split/particle_split.dart'; +import 'particle/split_img/split_image.dart'; +import 'picture_frame.dart'; +import 'package:l10n/l10n.dart'; +/// create by 张风捷特烈 on 2020/12/5 +/// contact me by email 1981462002@qq.com +/// 说明: +/// + +enum GalleryType { base, anim, particle, fun, art } + +class GalleryFactory { + static List getGalleryByName(GalleryType type,BuildContext context) { + switch (type) { + case GalleryType.base: + return [ + FrameShower( + title: "The Chaos", + author: "张风捷特烈", + srcUrl: "/base/draw_picture.dart", + info: context.l10n.drawingOfImages, + content: DrawPicture()), + FrameShower( + title: "数字显示管", + author: "张风捷特烈", + srcUrl: "/base/digital", + info: context.l10n.digitalDisplayTube, + content: DigitalShower()), + FrameShower( + title: "旋转风车", + author: "张风捷特烈", + srcUrl: "/base/windmill.dart", + info: context.l10n.pathDrawing, + content: WindmillWidget()), + FrameShower( + title: "平面直角坐标系", + author: "张风捷特烈", + srcUrl: "/base/draw_grid_axis.dart", + info: context.l10n.gridCoordinateSystem, + content: DrawGridAxis()), + FrameShower( + title: "平面极坐标系", + author: "张风捷特烈", + srcUrl: "/base/polar", + info: context.l10n.polarCoordinateSystemOfFaces, + content: PolarPainterWidget()), + FrameShower( + title: "曲线拟合", + author: "张风捷特烈", + srcUrl: "/base/draw_path_fun.dart", + info: context.l10n.drawFunctionCurvesForPathPairs, + content: DrawPathFun()), + FrameShower( + title: "圆中取形", + author: "张风捷特烈", + srcUrl: "/base/n_side", + info: context.l10n.drawRegularPolygons, + content: NSidePage()), + FrameShower( + title: "随机对称图", + author: "张风捷特烈", + srcUrl: '/fun/random_portrait.dart', + info: context.l10n.randomNumberProcessing, + content: RandomPortrait()), + FrameShower( + title: "简单时钟", + author: "张风捷特烈", + srcUrl: '/base/clock_widget.dart', + info: context.l10n.clockDrawing, + content: ClockWidget()), + ]; + case GalleryType.anim: + return [ + FrameShower( + title: "手势弹簧", + author: "张风捷特烈", + srcUrl: '/anim/spring_widget.dart', + info: context.l10n.drawSprings, + content: SpringWidget()), + FrameShower( + title: "绕定点旋转", + author: "张风捷特烈", + srcUrl: '/anim/rotate_by_point', + info: context.l10n.theApplicationOfAnglesInDrawing, + content: RotateByPointWidget()), + FrameShower( + title: "流光", + author: "张风捷特烈", + srcUrl: '/anim/circle_halo.dart', + info: context.l10n.usingShadersAndFilters, + content: CircleHalo()), + FrameShower( + title: "曲线路径动画", + author: "张风捷特烈", + srcUrl: '/anim/draw_path.dart', + info: context.l10n.pathDrawingFunctionCurve, + content: DrawPath()), + FrameShower( + title: "冰墩墩线条动画", + author: "张风捷特烈", + srcUrl: '/anim/dundun_path.dart', + info: context.l10n.thePathOfBingDwenDwen, + content: DunDunPathPage()), + FrameShower( + title: "Bezier3 演示", + author: "张风捷特烈", + srcUrl: '/anim/bezier3_player', + info: context.l10n.drawCubicBesselCurve, + content: Bezier3Player()), + FrameShower( + title: "动画曲线散点图", + author: "张风捷特烈", + srcUrl: '/anim/curve_shower', + info: context.l10n.theEffectOfAnimationCurve, + content: CurveAnimShower()), + + ]; + case GalleryType.particle: + return [ + FrameShower( + title: "随机粒子生成器", + author: "张风捷特烈", + srcUrl: '/particle/random', + info: context.l10n.randomParticlesAndBoundaryBouncing, + content: RandomParticle()), + FrameShower( + title: "粒子分裂", + author: "张风捷特烈", + srcUrl: '/particle/split', + info: context.l10n.particleCollision, + content: ParticleSplit()), + FrameShower( + title: "图片粒子分裂", + author: "张风捷特烈", + srcUrl: '/particle/split_img', + info: context.l10n.particle, + content: SplitImage()), + ]; + case GalleryType.fun: + return [ + FrameShower( + title: "Random Portrait", + author: "张风捷特烈", + srcUrl: '/fun/random_portrait.dart', + info: context.l10n.rectangleAndRandomNumbers, + content: RandomPortrait()), + FrameShower( + title: "冰墩墩", + author: "张风捷特烈", + srcUrl: '/fun/dundun_view.dart', + info: context.l10n.bingDwenDwen, + content: DunDunView()), + FrameShower( + title: "蒲丰投针试验", + author: "张风捷特烈", + srcUrl: '/fun/bufeng', + info: context.l10n.pufengInjectionTest, + content: BufengPanel()), + FrameShower( + title: "井字棋", + author: "张风捷特烈", + srcUrl: '/fun/stemp', + info: context.l10n.ticTacToe, + content: StampPaper()), + ]; + case GalleryType.art: + return [ + FrameShower( + title: "Tiled Line", + author: "generativeartistry.com", + srcUrl: '/art/tiled_lines.dart', + info: context.l10n.tiledLines, + content: TiledLines(), + ), + FrameShower( + title: "Joy Division", + author: "generativeartistry.com", + srcUrl: '/art/joy_division.dart', + info: context.l10n.joyDivision, + content: JoyDivision(), + ), + FrameShower( + title: "Cubic Disarray", + author: "generativeartistry.com", + srcUrl: '/art/cubic_disarray.dart', + info: context.l10n.cubicDisarray, content: CubicDisarray(), + ), + FrameShower( + title: "Triangular Mesh", + author: "generativeartistry.com", + srcUrl: '/art/triangular_mesh.dart', + info: context.l10n.triangularMesh, content: TriangularMesh(), + ), + FrameShower( + title: "Un Deux Trois", + srcUrl: '/art/un_deux_trois.dart', + author: "generativeartistry.com", + info: context.l10n.unDeuxTrois, content: UnDeuxTrois(), + ), + FrameShower( + title: "Circle Packing", + author: "generativeartistry.com", + srcUrl: '/art/circle_packing.dart', + info: context.l10n.circlePacking, content: CirclePacking(), + ), + FrameShower( + title: "Hypnotic Squares", + author: "generativeartistry.com", + srcUrl: '/art/hypnotic_squares.dart', + info: context.l10n.hypnoticSquares, content: HypnoticSquares(), + ), + FrameShower( + title: "Piet Mondrian", + author: "generativeartistry.com", + srcUrl: '/art/piet_mondrian.dart', + info: context.l10n.pietMondrian, content: PietMondrian(), + ) + ]; + default: + return []; + } + } +} diff --git a/lib/painter_system/gallery_unit.dart b/modules/painting_system/draw_system/lib/src/gallery_unit.dart similarity index 92% rename from lib/painter_system/gallery_unit.dart rename to modules/painting_system/draw_system/lib/src/gallery_unit.dart index c734f15a1..156e91a99 100644 --- a/lib/painter_system/gallery_unit.dart +++ b/modules/painting_system/draw_system/lib/src/gallery_unit.dart @@ -1,17 +1,16 @@ import 'dart:convert'; +import 'package:app/app.dart'; import 'package:components/project_ui/project_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:components/toly_ui/toly_ui.dart'; - -import 'package:flutter_unit/painter_system/gallery_card_item.dart'; -import 'package:flutter_unit/painter_system/bloc/gallery_unit/bloc.dart'; -import 'package:flutter_unit/painter_system/gallery_factory.dart'; +import 'package:toly_ui/toly_ui.dart'; +import 'package:l10n/l10n.dart'; import 'desk_ui/desk_gallery_unit.dart'; +import 'gallery_card_item.dart'; import 'gallery_detail_page.dart'; +import 'gallery_factory.dart'; /// create by 张风捷特烈 on 2020/11/28 /// contact me by email 1981462002@qq.com @@ -97,9 +96,7 @@ class _PhoneGalleryUnitState extends State { Expanded( child: Container( margin: const EdgeInsets.only(left: 8, right: 8), - child: BlocBuilder( - builder: _buildContentByState, - ), + child: _buildContent(StrUnit.galleryDesc(context)), decoration: boxDecoration, )) ], @@ -124,7 +121,7 @@ class _PhoneGalleryUnitState extends State { height: MediaQuery.of(context).size.height * 0.2, child: Row( mainAxisSize: MainAxisSize.min, - children: const [ + children: [ FlutterLogo( size: 40, ), @@ -132,7 +129,7 @@ class _PhoneGalleryUnitState extends State { width: 10, ), Text( - "绘制集录", + context.l10n.paintCollection, style: TextStyle(fontSize: 26, color: Colors.white), ), ], @@ -144,13 +141,14 @@ class _PhoneGalleryUnitState extends State { if(state.isEmpty){ return const LoadingShower(); } - return _buildContent(state); + return _buildContent(StrUnit.galleryDesc(context)); } Widget _buildContent(String galleryInfo) { + final List widgets = (json.decode(galleryInfo) as List).map((e) { GalleryInfo info = GalleryInfo.fromJson(e); - List children = GalleryFactory.getGalleryByName(info.type); + List children = GalleryFactory.getGalleryByName(info.type,context); return FeedbackWidget( a: 0.95, diff --git a/lib/painter_system/particle/out/clock_fx.dart b/modules/painting_system/draw_system/lib/src/particle/out/clock_fx.dart similarity index 100% rename from lib/painter_system/particle/out/clock_fx.dart rename to modules/painting_system/draw_system/lib/src/particle/out/clock_fx.dart diff --git a/lib/painter_system/particle/out/clock_widget.dart b/modules/painting_system/draw_system/lib/src/particle/out/clock_widget.dart similarity index 100% rename from lib/painter_system/particle/out/clock_widget.dart rename to modules/painting_system/draw_system/lib/src/particle/out/clock_widget.dart diff --git a/lib/painter_system/particle/out/particle.dart b/modules/painting_system/draw_system/lib/src/particle/out/particle.dart similarity index 100% rename from lib/painter_system/particle/out/particle.dart rename to modules/painting_system/draw_system/lib/src/particle/out/particle.dart diff --git a/lib/painter_system/particle/out/rnd.dart b/modules/painting_system/draw_system/lib/src/particle/out/rnd.dart similarity index 100% rename from lib/painter_system/particle/out/rnd.dart rename to modules/painting_system/draw_system/lib/src/particle/out/rnd.dart diff --git a/lib/painter_system/particle/random/particle.dart b/modules/painting_system/draw_system/lib/src/particle/random/particle.dart similarity index 100% rename from lib/painter_system/particle/random/particle.dart rename to modules/painting_system/draw_system/lib/src/particle/random/particle.dart diff --git a/lib/painter_system/particle/random/particle_manage.dart b/modules/painting_system/draw_system/lib/src/particle/random/particle_manage.dart similarity index 100% rename from lib/painter_system/particle/random/particle_manage.dart rename to modules/painting_system/draw_system/lib/src/particle/random/particle_manage.dart diff --git a/lib/painter_system/particle/random/random_particle.dart b/modules/painting_system/draw_system/lib/src/particle/random/random_particle.dart similarity index 100% rename from lib/painter_system/particle/random/random_particle.dart rename to modules/painting_system/draw_system/lib/src/particle/random/random_particle.dart diff --git a/lib/painter_system/particle/random/world_render.dart b/modules/painting_system/draw_system/lib/src/particle/random/world_render.dart similarity index 100% rename from lib/painter_system/particle/random/world_render.dart rename to modules/painting_system/draw_system/lib/src/particle/random/world_render.dart diff --git a/lib/painter_system/particle/split/particle.dart b/modules/painting_system/draw_system/lib/src/particle/split/particle.dart similarity index 100% rename from lib/painter_system/particle/split/particle.dart rename to modules/painting_system/draw_system/lib/src/particle/split/particle.dart diff --git a/lib/painter_system/particle/split/particle_manage.dart b/modules/painting_system/draw_system/lib/src/particle/split/particle_manage.dart similarity index 100% rename from lib/painter_system/particle/split/particle_manage.dart rename to modules/painting_system/draw_system/lib/src/particle/split/particle_manage.dart diff --git a/lib/painter_system/particle/split/particle_split.dart b/modules/painting_system/draw_system/lib/src/particle/split/particle_split.dart similarity index 100% rename from lib/painter_system/particle/split/particle_split.dart rename to modules/painting_system/draw_system/lib/src/particle/split/particle_split.dart diff --git a/lib/painter_system/particle/split/world_render.dart b/modules/painting_system/draw_system/lib/src/particle/split/world_render.dart similarity index 100% rename from lib/painter_system/particle/split/world_render.dart rename to modules/painting_system/draw_system/lib/src/particle/split/world_render.dart diff --git a/lib/painter_system/particle/split_img/particle.dart b/modules/painting_system/draw_system/lib/src/particle/split_img/particle.dart similarity index 100% rename from lib/painter_system/particle/split_img/particle.dart rename to modules/painting_system/draw_system/lib/src/particle/split_img/particle.dart diff --git a/lib/painter_system/particle/split_img/particle_manage.dart b/modules/painting_system/draw_system/lib/src/particle/split_img/particle_manage.dart similarity index 100% rename from lib/painter_system/particle/split_img/particle_manage.dart rename to modules/painting_system/draw_system/lib/src/particle/split_img/particle_manage.dart diff --git a/lib/painter_system/particle/split_img/split_image.dart b/modules/painting_system/draw_system/lib/src/particle/split_img/split_image.dart similarity index 100% rename from lib/painter_system/particle/split_img/split_image.dart rename to modules/painting_system/draw_system/lib/src/particle/split_img/split_image.dart diff --git a/lib/painter_system/particle/split_img/world_render.dart b/modules/painting_system/draw_system/lib/src/particle/split_img/world_render.dart similarity index 100% rename from lib/painter_system/particle/split_img/world_render.dart rename to modules/painting_system/draw_system/lib/src/particle/split_img/world_render.dart diff --git a/lib/painter_system/picture_frame.dart b/modules/painting_system/draw_system/lib/src/picture_frame.dart similarity index 95% rename from lib/painter_system/picture_frame.dart rename to modules/painting_system/draw_system/lib/src/picture_frame.dart index 7cb425ca3..591c2e00b 100644 --- a/lib/painter_system/picture_frame.dart +++ b/modules/painting_system/draw_system/lib/src/picture_frame.dart @@ -1,11 +1,12 @@ import 'dart:io'; import 'dart:ui'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart'; import 'desk_ui/desk_frame.dart'; - +import 'package:l10n/l10n.dart'; class PictureFrame extends StatelessWidget { final Widget? child; final double? width; @@ -124,7 +125,7 @@ class FrameShower extends StatelessWidget { @override Widget build(BuildContext context) { - bool isDesk = Platform.isMacOS || Platform.isWindows || Platform.isLinux; + bool isDesk = kIsWeb||Platform.isMacOS || Platform.isWindows || Platform.isLinux; if (isDesk) { return DeskFrameShower( content: content, @@ -155,8 +156,8 @@ class FrameShower extends StatelessWidget { const Spacer(), GestureDetector( onTap: _launch, - child: const Text( - "源码地址 ", + child: Text( + "${context.l10n.srcPath} ", style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, diff --git a/lib/painter_system/utils/colors.dart b/modules/painting_system/draw_system/lib/src/utils/colors.dart similarity index 100% rename from lib/painter_system/utils/colors.dart rename to modules/painting_system/draw_system/lib/src/utils/colors.dart diff --git a/lib/painter_system/utils/coordinate.dart b/modules/painting_system/draw_system/lib/src/utils/coordinate.dart similarity index 100% rename from lib/painter_system/utils/coordinate.dart rename to modules/painting_system/draw_system/lib/src/utils/coordinate.dart diff --git a/modules/painting_system/draw_system/pubspec.yaml b/modules/painting_system/draw_system/pubspec.yaml new file mode 100644 index 000000000..28e38deff --- /dev/null +++ b/modules/painting_system/draw_system/pubspec.yaml @@ -0,0 +1,50 @@ +name: draw_system +description: "A new Flutter package project." +version: 0.0.1 +homepage: +publish_to: none + +environment: + sdk: ">=3.5.0 <4.0.0" + flutter: ">=1.17.0" +resolution: workspace +dependencies: + flutter: + sdk: flutter + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # To add assets to your package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # To add custom fonts to your package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/modules/painting_system/draw_system/test/draw_system_test.dart b/modules/painting_system/draw_system/test/draw_system_test.dart new file mode 100644 index 000000000..516a3964a --- /dev/null +++ b/modules/painting_system/draw_system/test/draw_system_test.dart @@ -0,0 +1,12 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:draw_system/draw_system.dart'; + +void main() { + // test('adds one to input values', () { + // final calculator = Calculator(); + // expect(calculator.addOne(2), 3); + // expect(calculator.addOne(-7), -6); + // expect(calculator.addOne(0), 1); + // }); +} diff --git a/modules/tools_system/treasure_tools/.gitignore b/modules/tools_system/treasure_tools/.gitignore new file mode 100644 index 000000000..ac5aa9893 --- /dev/null +++ b/modules/tools_system/treasure_tools/.gitignore @@ -0,0 +1,29 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +build/ diff --git a/modules/tools_system/treasure_tools/.metadata b/modules/tools_system/treasure_tools/.metadata new file mode 100644 index 000000000..fe59252be --- /dev/null +++ b/modules/tools_system/treasure_tools/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9" + channel: "stable" + +project_type: package diff --git a/modules/tools_system/treasure_tools/CHANGELOG.md b/modules/tools_system/treasure_tools/CHANGELOG.md new file mode 100644 index 000000000..41cc7d819 --- /dev/null +++ b/modules/tools_system/treasure_tools/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/modules/tools_system/treasure_tools/LICENSE b/modules/tools_system/treasure_tools/LICENSE new file mode 100644 index 000000000..ba75c69f7 --- /dev/null +++ b/modules/tools_system/treasure_tools/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/modules/tools_system/treasure_tools/README.md b/modules/tools_system/treasure_tools/README.md new file mode 100644 index 000000000..02fe8ecab --- /dev/null +++ b/modules/tools_system/treasure_tools/README.md @@ -0,0 +1,39 @@ + + +TODO: Put a short description of the package here that helps potential users +know whether this package might be useful for them. + +## Features + +TODO: List what your package can do. Maybe include images, gifs, or videos. + +## Getting started + +TODO: List prerequisites and provide or point to information on how to +start using the package. + +## Usage + +TODO: Include short and useful examples for package users. Add longer examples +to `/example` folder. + +```dart +const like = 'sample'; +``` + +## Additional information + +TODO: Tell users more about the package: where to find more information, how to +contribute to the package, how to file issues, what response they can expect +from the package authors, and more. diff --git a/modules/tools_system/treasure_tools/analysis_options.yaml b/modules/tools_system/treasure_tools/analysis_options.yaml new file mode 100644 index 000000000..a5744c1cf --- /dev/null +++ b/modules/tools_system/treasure_tools/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/modules/tools_system/treasure_tools/lib/src/bloc/state.dart b/modules/tools_system/treasure_tools/lib/src/bloc/state.dart new file mode 100644 index 000000000..d852422c6 --- /dev/null +++ b/modules/tools_system/treasure_tools/lib/src/bloc/state.dart @@ -0,0 +1,8 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../model/class.dart'; + + +class ClassGenBloc extends Cubit{ + ClassGenBloc():super(Class(fields: [], name: '')); +} \ No newline at end of file diff --git a/lib/code_gen/class_generator.dart b/modules/tools_system/treasure_tools/lib/src/class_generator.dart similarity index 100% rename from lib/code_gen/class_generator.dart rename to modules/tools_system/treasure_tools/lib/src/class_generator.dart diff --git a/lib/code_gen/code_gen_page.dart b/modules/tools_system/treasure_tools/lib/src/code_gen_page.dart similarity index 96% rename from lib/code_gen/code_gen_page.dart rename to modules/tools_system/treasure_tools/lib/src/code_gen_page.dart index 5df5945c9..25a872bb2 100644 --- a/lib/code_gen/code_gen_page.dart +++ b/modules/tools_system/treasure_tools/lib/src/code_gen_page.dart @@ -1,13 +1,14 @@ import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:flutter_unit/code_gen/icon_font_gen/icon_font_gen_page.dart'; import 'class_generator.dart'; import 'desk_widget_top_bar.dart'; +import 'icon_font_gen/icon_font_gen_page.dart'; import 'model/class.dart'; import 'model/field.dart'; import 'popable/class_gen_field.dart'; import 'popable/toly_select.dart'; +import 'view/json_display/json_display.dart'; class A {} @@ -58,11 +59,7 @@ class _CodeGenPageState extends State { controller:_ctrl, children: [ IconFontGenPage(), - Center( - child: Text( - '敬请期待' - ), - ), + JsonAnalysisTool(), Center( child: Text( '敬请期待' diff --git a/lib/code_gen/data/task_result.dart b/modules/tools_system/treasure_tools/lib/src/data/task_result.dart similarity index 100% rename from lib/code_gen/data/task_result.dart rename to modules/tools_system/treasure_tools/lib/src/data/task_result.dart diff --git a/modules/tools_system/treasure_tools/lib/src/desk_widget_top_bar.dart b/modules/tools_system/treasure_tools/lib/src/desk_widget_top_bar.dart new file mode 100644 index 000000000..a966facb9 --- /dev/null +++ b/modules/tools_system/treasure_tools/lib/src/desk_widget_top_bar.dart @@ -0,0 +1,73 @@ +import 'package:app/app.dart'; +import 'package:toly_ui/toly_ui.dart'; +import 'package:flutter/material.dart'; + + + +class DeskCodeGenTopBar extends StatefulWidget { + final ValueChanged onTabPressed; + final VoidCallback onTapGen; + + const DeskCodeGenTopBar({Key? key,required this.onTabPressed, required this.onTapGen}) : super(key: key); + + @override + State createState() => _DeskCodeGenTopBarState(); +} + +class _DeskCodeGenTopBarState extends State with SingleTickerProviderStateMixin { + late TabController tabController; + + static const List _tabs = ['IconFont','Json 解析', '数据类' , '状态管理',]; + + @override + void initState() { + super.initState(); + tabController = TabController(length: _tabs.length, vsync: this); + } + + @override + Widget build(BuildContext context) { + Color themeColor = Theme.of(context).primaryColor; + + bool isDark = Theme.of(context).brightness == Brightness.dark; + + return DragToMoveWrapper( + child: Container( + height: 64, + color: isDark?Color(0xff2C3036):Colors.white, + child: Row( + children: [ + const SizedBox(width: 12,), + SizedBox( + width: 350, + child: TabBar( + onTap: widget.onTabPressed, + indicatorSize: TabBarIndicatorSize.label, + labelPadding: const EdgeInsets.symmetric(horizontal: 6), + isScrollable: false, + indicator: RoundRectTabIndicator( + borderSide: BorderSide(color: themeColor, width: 3), + ), + labelStyle: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + ), + controller: tabController, + labelColor: themeColor, + indicatorWeight: 3, + unselectedLabelColor: Colors.grey, + indicatorColor: themeColor, + tabs: + _tabs.map((String name) => Tab(text: name)).toList(), + ), + ), + Spacer(), + + const SizedBox(width: 20,), + WindowButtons(), + ], + ), + ), + ); + } +} diff --git a/lib/code_gen/icon_font_gen/gen_message_action.dart b/modules/tools_system/treasure_tools/lib/src/icon_font_gen/gen_message_action.dart similarity index 100% rename from lib/code_gen/icon_font_gen/gen_message_action.dart rename to modules/tools_system/treasure_tools/lib/src/icon_font_gen/gen_message_action.dart diff --git a/lib/code_gen/icon_font_gen/icon_font_class_parser.dart b/modules/tools_system/treasure_tools/lib/src/icon_font_gen/icon_font_class_parser.dart similarity index 98% rename from lib/code_gen/icon_font_gen/icon_font_class_parser.dart rename to modules/tools_system/treasure_tools/lib/src/icon_font_gen/icon_font_class_parser.dart index 8eb709511..224bd81e1 100644 --- a/lib/code_gen/icon_font_gen/icon_font_class_parser.dart +++ b/modules/tools_system/treasure_tools/lib/src/icon_font_gen/icon_font_class_parser.dart @@ -10,7 +10,7 @@ class IconFontClassParser{ void gen(IconFontGenConfig config){ final inputStream = InputFileStream(config.srcZip); // 将压缩包有用资源解压到目标文件 - final archive = ZipDecoder().decodeBuffer(inputStream); + final archive = ZipDecoder().decodeStream(inputStream); for (var file in archive.files) { if (file.isFile) { if (file.name.endsWith('.ttf')) { diff --git a/lib/code_gen/icon_font_gen/icon_font_gen_config.dart b/modules/tools_system/treasure_tools/lib/src/icon_font_gen/icon_font_gen_config.dart similarity index 100% rename from lib/code_gen/icon_font_gen/icon_font_gen_config.dart rename to modules/tools_system/treasure_tools/lib/src/icon_font_gen/icon_font_gen_config.dart diff --git a/lib/code_gen/icon_font_gen/icon_font_gen_page.dart b/modules/tools_system/treasure_tools/lib/src/icon_font_gen/icon_font_gen_page.dart similarity index 100% rename from lib/code_gen/icon_font_gen/icon_font_gen_page.dart rename to modules/tools_system/treasure_tools/lib/src/icon_font_gen/icon_font_gen_page.dart diff --git a/lib/code_gen/model/class.dart b/modules/tools_system/treasure_tools/lib/src/model/class.dart similarity index 100% rename from lib/code_gen/model/class.dart rename to modules/tools_system/treasure_tools/lib/src/model/class.dart diff --git a/lib/code_gen/model/field.dart b/modules/tools_system/treasure_tools/lib/src/model/field.dart similarity index 100% rename from lib/code_gen/model/field.dart rename to modules/tools_system/treasure_tools/lib/src/model/field.dart diff --git a/lib/code_gen/popable/class_gen_field.dart b/modules/tools_system/treasure_tools/lib/src/popable/class_gen_field.dart similarity index 98% rename from lib/code_gen/popable/class_gen_field.dart rename to modules/tools_system/treasure_tools/lib/src/popable/class_gen_field.dart index b57b06e2c..562c927be 100644 --- a/lib/code_gen/popable/class_gen_field.dart +++ b/modules/tools_system/treasure_tools/lib/src/popable/class_gen_field.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:flutter_unit/code_gen/model/class.dart'; import '../code_gen_page.dart'; +import '../model/class.dart'; import '../model/field.dart'; import 'toly_select.dart'; diff --git a/lib/code_gen/popable/toly_select.dart b/modules/tools_system/treasure_tools/lib/src/popable/toly_select.dart similarity index 100% rename from lib/code_gen/popable/toly_select.dart rename to modules/tools_system/treasure_tools/lib/src/popable/toly_select.dart diff --git a/modules/tools_system/treasure_tools/lib/src/view/json_display/json_display.dart b/modules/tools_system/treasure_tools/lib/src/view/json_display/json_display.dart new file mode 100644 index 000000000..41f572a2c --- /dev/null +++ b/modules/tools_system/treasure_tools/lib/src/view/json_display/json_display.dart @@ -0,0 +1,362 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; + +/// 颜色定义 +class FieldColor { + static const keyColor = Colors.grey; + static const intColor = Colors.deepOrange; + static const stringColor = Colors.green; + static const nullColor = Colors.blueGrey; + static const arrayColor = Colors.blue; + static const objectColor = Colors.purple; +} + +/// 基础类型抽象类 +abstract class BaseValue { + final String? key; + final T value; + bool isExpanded; + + BaseValue(this.value, {this.key, this.isExpanded = true}); + + Widget buildValue(); +} + +/// 整数值 +class IntValue extends BaseValue { + IntValue(super.value, {super.key}); + + @override + Widget buildValue() { + return Text(value.toString()+",", + style: const TextStyle(color: FieldColor.intColor)); + } +} + +/// 字符串值 +class StringValue extends BaseValue { + StringValue(super.value, {super.key}); + + @override + Widget buildValue() { + return Text('"$value",', + style: const TextStyle(color: FieldColor.stringColor)); + } +} + +/// Null 值 +class NullValue extends BaseValue { + NullValue({super.key}) : super(null); + + @override + Widget buildValue() { + return const Text('null', style: TextStyle(color: FieldColor.nullColor)); + } +} + +/// 数组值 +class ArrayValue extends BaseValue> { + ArrayValue(super.value, {super.key}); + + @override + Widget buildValue() { + return StatefulBuilder(builder: (context, setState) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + InkWell( + onTap: () { + setState(() { + isExpanded = !isExpanded; + }); + }, + child: Row( + children: [ + Icon(isExpanded ? Icons.arrow_drop_down : Icons.arrow_right, + color: FieldColor.arrayColor), + Text('(Array)${isExpanded?'【':'【...'}', + style: TextStyle(color: FieldColor.arrayColor)), + + if (!isExpanded) + const Text('】', + style: TextStyle(color: FieldColor.arrayColor)), + ], + ), + ), + if (isExpanded) ...[ + Padding( + padding: const EdgeInsets.only(left: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: value.map((e) => parseJson(e).buildValue()).toList(), + ), + ), + const Text('】', style: TextStyle(color: FieldColor.arrayColor)), + ] + ], + ); + }); + } +} + +/// 对象值 +class ObjectValue extends BaseValue> { + ObjectValue(super.value, {super.key}); + + @override + Widget buildValue() { + return StatefulBuilder( + builder: (context, setState) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + InkWell( + onTap: () { + setState(() { + isExpanded = !isExpanded; // 触发 UI 重新渲染 + }); + }, + child: Row( + children: [ + Icon(isExpanded ? Icons.arrow_drop_down : Icons.arrow_right, + color: FieldColor.objectColor), + Text('(Object)${isExpanded?'{':'{...'}', + style: TextStyle(color: FieldColor.objectColor)), + if (!isExpanded) + const Text('}', + style: TextStyle(color: FieldColor.objectColor)), + ], + ), + ), + if (isExpanded) ...[ + Padding( + padding: const EdgeInsets.only(left: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: value.entries + .map((e) => + KeyBlock(keyText: e.key, value: parseJson(e.value))) + .toList(), + ), + ), + const Text('}', style: TextStyle(color: FieldColor.objectColor)), + ] + ], + ); + }, + ); + } +} + +/// 键值对组件 +class KeyBlock extends StatelessWidget { + final String keyText; + final BaseValue value; + + const KeyBlock({super.key, required this.keyText, required this.value}); + + @override + Widget build(BuildContext context) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('$keyText: ', + style: const TextStyle( + color: FieldColor.keyColor, fontWeight: FontWeight.bold)), + Expanded(child: value.buildValue()), + ], + ); + } +} + +/// 解析 JSON 数据 +BaseValue parseJson(dynamic json, {String? key}) { + if (json == null) return NullValue(key: key); + if (json is int) return IntValue(json, key: key); + if (json is String) return StringValue(json, key: key); + if (json is List) return ArrayValue(json, key: key); + if (json is Map) return ObjectValue(json, key: key); + return StringValue(json.toString(), key: key); +} + +/// JSON 解析工具 UI +class JsonAnalysisTool extends StatefulWidget { + const JsonAnalysisTool({super.key}); + + @override + State createState() => _JsonAnalysisToolState(); +} + +class _JsonAnalysisToolState extends State { + final TextEditingController _controller = TextEditingController( + text: """ +{ + "id": 1, + "name": "Container", + "localName": "容器组件", + "info": "用于容纳单个子组件的容器组件。集成了若干个单子组件的功能,如内外边距、形变、装饰、约束等...", + "lever": 5, + "family": 0, + "linkIds": [ + 74, + 85, + 80, + 78, + 70, + 123 + ], + "nodes": [ + { + "file": "node1_base.dart", + "name": "可用于显示一个指定宽高的区域", + "desc": [ + "【width】 : 宽 【int】", + "【height】: 高 【int】", + "【color】: 颜色 【Color】" + ] + }, + { + "file": "node2_child.dart", + "name": "可以在区域中放入一个子组件", + "desc": [ + "【padding】 : 内边距 【EdgeInsetsGeometry】", + "【margin】: 外边距 【EdgeInsetsGeometry】", + "【child】: 子组件 【Widget】" + ] + }, + { + "file": "node3_alignment.dart", + "name": "可对子组件进行对齐定位", + "desc": [ + "【alignment】 : 对齐定位 【AlignmentGeometry】" + ] + }, + { + "file": "node4_decoration.dart", + "name": "可对子组件进行装饰", + "desc": [ + "【decoration】 : 装饰 【Decoration】", + "可装饰: 边线、圆弧、颜色、渐变色、阴影、图片等内容" + ] + }, + { + "file": "node5_transform.dart", + "name": "Container还具有变换性", + "desc": [ + "【transform】 : 变换矩阵 【Matrix4】", + "基于Matrix4的矩阵变换,变换详情见线性代数" + ] + }, + { + "file": "node6_constraints.dart", + "name": "Container的约束性", + "desc": [ + "【constraints】 : 约束 【BoxConstraints】", + "会约束该区域的尺寸,不会小于指定的最小宽高,也不会大于指定的最大宽高。" + ] + } + ] +}""", + ); + BaseValue? _parsedData; + String? _error; + + final ScrollController _hCtrl = ScrollController(); + final ScrollController _vCtrl = ScrollController(); + + @override + void initState() { + super.initState(); + _parseJson(); + } + + void _parseJson() { + setState(() { + _error = null; + try { + final parsed = jsonDecode(_controller.text); + _parsedData = parseJson(parsed); + } catch (e) { + _error = 'JSON 解析失败: $e'; + _parsedData = null; + } + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Row( + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: TextField( + style: TextStyle(fontSize: 14,fontFamily: '楷体'), + controller: _controller, + onChanged: _onChanged, + expands: true, + maxLines: null, + decoration: const InputDecoration( + fillColor: Colors.white, + filled: true, + labelText: '输入 JSON', + border: OutlineInputBorder(), + ), + ), + ), + ), + VerticalDivider(), + Expanded( + child: Align( + alignment: Alignment.topLeft, + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(4) + ), + margin: EdgeInsets.all(8), + child: Scrollbar( //-> ::tag1:: + thumbVisibility: true, + //-> ::tag6:: + notificationPredicate: (ScrollNotification notification) => notification.depth == 1, + key: const Key('debuggerCodeViewVerticalScrollbarKey'), + controller: _vCtrl, + child: LayoutBuilder( + builder: (context,cts) { + final double boxHeight = 800; + return Scrollbar( //-> ::tag2:: + key: const Key('debuggerCodeViewHorizontalScrollbarKey'), + thumbVisibility: true, + controller: _hCtrl, + child: SingleChildScrollView( + controller: _hCtrl, //-> ::tag3:: + child: SingleChildScrollView( + controller: _vCtrl, + scrollDirection: Axis.horizontal, + padding: EdgeInsets.symmetric(horizontal: 8,vertical: 6), + child: SizedBox( + width: boxHeight, + child: _error != null + ? Text(_error!, style: const TextStyle(color: Colors.red)) + : _parsedData?.buildValue() ?? const Text('请输入 JSON'), + ), + ), + ), + ); + } + ), + ), + ), + ), + ), + ], + ), + ); + } + + void _onChanged(String value) { + _parseJson(); + } +} diff --git a/modules/tools_system/treasure_tools/lib/src/view/mobile/mobile_tool_page.dart b/modules/tools_system/treasure_tools/lib/src/view/mobile/mobile_tool_page.dart new file mode 100644 index 000000000..f7ab00495 --- /dev/null +++ b/modules/tools_system/treasure_tools/lib/src/view/mobile/mobile_tool_page.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; +import 'package:l10n/l10n.dart'; +class MobileToolPage extends StatelessWidget { + const MobileToolPage({super.key}); + + @override + Widget build(BuildContext context) { + AppLocalizations l10n = context.l10n; + String title = l10n.treasureTools; + String building = l10n.knowledgeConstruction; + return Scaffold( + appBar: AppBar(title: Text(title),), + body: Center( + child: Text(building), + ), + ); + } +} + diff --git a/lib/code_gen/wrapper.dart b/modules/tools_system/treasure_tools/lib/src/wrapper.dart similarity index 100% rename from lib/code_gen/wrapper.dart rename to modules/tools_system/treasure_tools/lib/src/wrapper.dart diff --git a/modules/tools_system/treasure_tools/lib/treasure_tools.dart b/modules/tools_system/treasure_tools/lib/treasure_tools.dart new file mode 100644 index 000000000..c61f6a347 --- /dev/null +++ b/modules/tools_system/treasure_tools/lib/treasure_tools.dart @@ -0,0 +1,4 @@ +library treasure_tools; + +export 'src/code_gen_page.dart'; +export 'src/view/mobile/mobile_tool_page.dart'; \ No newline at end of file diff --git a/modules/tools_system/treasure_tools/pubspec.yaml b/modules/tools_system/treasure_tools/pubspec.yaml new file mode 100644 index 000000000..a70d90ba4 --- /dev/null +++ b/modules/tools_system/treasure_tools/pubspec.yaml @@ -0,0 +1,50 @@ +name: treasure_tools +description: "A new Flutter package project." +version: 0.0.1 +homepage: + +environment: + sdk: ">=3.5.0 <4.0.0" + flutter: ">=1.17.0" +resolution: workspace +dependencies: + flutter: + sdk: flutter + + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + +# To add assets to your package, add an assets section, like this: +# assets: +# - images/a_dot_burr.jpeg +# - images/a_dot_ham.jpeg +# +# For details regarding assets in packages, see +# https://flutter.dev/assets-and-images/#from-packages +# +# An image asset can refer to one or more resolution-specific "variants", see +# https://flutter.dev/assets-and-images/#resolution-aware + +# To add custom fonts to your package, add a fonts section here, +# in this "flutter" section. Each entry in this list should have a +# "family" key with the font family name, and a "fonts" key with a +# list giving the asset and other descriptors for the font. For +# example: +# fonts: +# - family: Schyler +# fonts: +# - asset: fonts/Schyler-Regular.ttf +# - asset: fonts/Schyler-Italic.ttf +# style: italic +# - family: Trajan Pro +# fonts: +# - asset: fonts/TrajanPro.ttf +# - asset: fonts/TrajanPro_Bold.ttf +# weight: 700 +# +# For details regarding fonts in packages, see +# https://flutter.dev/custom-fonts/#from-packages diff --git a/test/iconfont_parser_test.dart b/modules/tools_system/treasure_tools/test/iconfont_parser_test.dart similarity index 81% rename from test/iconfont_parser_test.dart rename to modules/tools_system/treasure_tools/test/iconfont_parser_test.dart index 37780b406..ae20198dc 100644 --- a/test/iconfont_parser_test.dart +++ b/modules/tools_system/treasure_tools/test/iconfont_parser_test.dart @@ -2,7 +2,8 @@ import 'dart:convert'; import 'dart:io'; -import 'package:flutter_unit/code_gen/icon_font_gen/icon_font_class_parser.dart'; +import 'package:treasure_tools/src/icon_font_gen/icon_font_class_parser.dart'; + void main(){ final String filePath = r'E:\Download\out\font_1717416_cwm89ioqkfo\iconfont.json'; diff --git a/modules/tools_system/treasure_tools/test/treasure_tools_test.dart b/modules/tools_system/treasure_tools/test/treasure_tools_test.dart new file mode 100644 index 000000000..1a5dd707f --- /dev/null +++ b/modules/tools_system/treasure_tools/test/treasure_tools_test.dart @@ -0,0 +1,12 @@ +// import 'package:flutter_test/flutter_test.dart'; +// +// import 'package:treasure_tools/treasure_tools.dart'; +// +// void main() { +// test('adds one to input values', () { +// final calculator = Calculator(); +// expect(calculator.addOne(2), 3); +// expect(calculator.addOne(-7), -6); +// expect(calculator.addOne(0), 1); +// }); +// } diff --git a/modules/tools_system/treasure_tools/test/yaml_parser_test.dart b/modules/tools_system/treasure_tools/test/yaml_parser_test.dart new file mode 100644 index 000000000..ea8e36e85 --- /dev/null +++ b/modules/tools_system/treasure_tools/test/yaml_parser_test.dart @@ -0,0 +1,51 @@ +// +// import 'dart:convert'; +// import 'dart:io'; +// +// // import 'package:yaml/yaml.dart'; +// import 'package:yaml_modify/yaml_modify.dart'; +// +// void main(){ +// final String filePath = r'E:\Projects\Flutter\FlutterUnit\pubspec.yaml'; +// File pubspecFile = File(filePath); +// final String pubspec = pubspecFile.readAsStringSync(); +// print(pubspec); +// +// // YamlEditor doc = YamlEditor(pubspec); +// // print(doc); +// final doc = loadYaml(pubspec); +// print(doc); +// +// YamlList fontsList = doc['flutter']['fonts'] as YamlList; +// +// final modifiableDoc = getModifiableNode(doc); +// final modifiableList = getModifiableNode(fontsList); +// modifiableList.removeWhere((e) => e['family'] == 'TolyIcon'); +// modifiableList.add( +// YamlMap.wrap({ +// 'family': 'TolyIcon3', +// 'fonts':YamlList.wrap([YamlMap.wrap({'asset':'assets/iconfont/iconfont.ttf'})]) +// }) +// ); +// modifiableDoc['flutter']['fonts'] = modifiableList; +// final strYaml = toYamlString(modifiableDoc); +// print(modifiableList); +// +// // // YamlMap flutterNode = doc[]; +// // doc.update('flutter', (value) { +// // // YamlList fontsNode = value as YamlList; +// // // List filter = fontsNode.where((e) => e['family']!='TolyIcon').toList(); +// // +// // return YamlMap.wrap({ +// // 'family': 'TolyIcon3', +// // 'fonts':YamlList.wrap([YamlMap.wrap({'asset':'assets/iconfont/iconfont.ttf'})]) +// // }); +// // }); +// +// // YamlList fontsList = doc['flutter']['fonts'] as YamlList; +// // +// +// // // doc. +// print(doc); +// +// } \ No newline at end of file diff --git a/modules/tools_system/treasure_tools/test/yaml_parser_test2.dart b/modules/tools_system/treasure_tools/test/yaml_parser_test2.dart new file mode 100644 index 000000000..f8a47928f --- /dev/null +++ b/modules/tools_system/treasure_tools/test/yaml_parser_test2.dart @@ -0,0 +1,64 @@ +// +// import 'dart:convert'; +// import 'dart:io'; +// +// // import 'package:yaml/yaml.dart'; +// import 'package:yaml_modify/yaml_modify.dart'; +// +// void main(){ +// String familyName = 'TolyIcon'; +// String fontAssetsDist = 'assets/iconfont/iconfont.ttf'; +// +// // final String filePath = r'E:\Projects\Flutter\FlutterUnit\pubspec.yaml'; +// final String filePath = r'E:\Projects\Flutter\Work\toly_image_edit\pubspec.yaml'; +// File pubspecFile = File(filePath); +// +// List lines = pubspecFile.readAsLinesSync(); +// +// RegExp fontsRegex = RegExp(r'^ fonts:',multiLine: true); +// bool hasFonts = fontsRegex.hasMatch(lines.join('\n')); +// +// if(!hasFonts){ +// // 当前没有 fonts 节点,需要添加到 flutter 节点下 +// int index = lines.indexWhere((e) => e.startsWith('flutter:')); +// +// List fonts = [ +// ' fonts:', +// ' - family: TolyIcon', +// ' fonts:', +// ' - asset: assets/iconfont/iconfont.ttf', +// ]; +// +// lines.insertAll(index+1, fonts); +// print(lines); +// pubspecFile.writeAsStringSync(lines.join('\n')); +// return; +// } +// // 存在 fonts 节点,查询 family ,有没有当前字体图标 +// bool hasTargetFamily = false; +// RegExp regExp = RegExp(r'^ +- family: +(\w+)'); +// +// for(int i=0;i e.startsWith(fontsRegex)); +// List fonts = [ +// ' - family: TolyIcon', +// ' fonts:', +// ' - asset: $fontAssetsDist', +// ]; +// lines.insertAll(index+1, fonts); +// print(lines); +// pubspecFile.writeAsStringSync(lines.join('\n')); +// return; +// } +// +// } \ No newline at end of file diff --git a/packages/widget_module/.gitignore b/modules/widget_system/widget_module/.gitignore similarity index 100% rename from packages/widget_module/.gitignore rename to modules/widget_system/widget_module/.gitignore diff --git a/packages/widget_module/.metadata b/modules/widget_system/widget_module/.metadata similarity index 100% rename from packages/widget_module/.metadata rename to modules/widget_system/widget_module/.metadata diff --git a/modules/widget_system/widget_module/CHANGELOG.md b/modules/widget_system/widget_module/CHANGELOG.md new file mode 100644 index 000000000..41cc7d819 --- /dev/null +++ b/modules/widget_system/widget_module/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/modules/widget_system/widget_module/LICENSE b/modules/widget_system/widget_module/LICENSE new file mode 100644 index 000000000..ba75c69f7 --- /dev/null +++ b/modules/widget_system/widget_module/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/modules/widget_system/widget_module/README.md b/modules/widget_system/widget_module/README.md new file mode 100644 index 000000000..02fe8ecab --- /dev/null +++ b/modules/widget_system/widget_module/README.md @@ -0,0 +1,39 @@ + + +TODO: Put a short description of the package here that helps potential users +know whether this package might be useful for them. + +## Features + +TODO: List what your package can do. Maybe include images, gifs, or videos. + +## Getting started + +TODO: List prerequisites and provide or point to information on how to +start using the package. + +## Usage + +TODO: Include short and useful examples for package users. Add longer examples +to `/example` folder. + +```dart +const like = 'sample'; +``` + +## Additional information + +TODO: Tell users more about the package: where to find more information, how to +contribute to the package, how to file issues, what response they can expect +from the package authors, and more. diff --git a/modules/widget_system/widget_module/analysis_options.yaml b/modules/widget_system/widget_module/analysis_options.yaml new file mode 100644 index 000000000..a5744c1cf --- /dev/null +++ b/modules/widget_system/widget_module/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/modules/widget_system/widget_module/lib/blocs/action/widget_action.dart b/modules/widget_system/widget_module/lib/blocs/action/widget_action.dart new file mode 100644 index 000000000..09b814078 --- /dev/null +++ b/modules/widget_system/widget_module/lib/blocs/action/widget_action.dart @@ -0,0 +1,33 @@ +import 'package:flutter/cupertino.dart'; +import 'package:app/app.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:widget_module/blocs/blocs.dart'; +import 'package:widget_ui/widget_ui.dart'; + +import '../../widget_module.dart'; + +extension WidgetContext on BuildContext { + void initWidgetData() { + switchWidgetFamily(WidgetFamily.stateless); + } + + void switchWidgetFamily(WidgetFamily family) { + String locale = read().state.language.code; + read().add(EventTabTap(family, locale: locale)); + } + + void toggleLike(int widgetId) { + read().toggle(widgetId); + } + + void handleWidgetAction(WidgetAction value) { + switch (value) { + case JumpWidgetDetail(): + String? name = value.widgetName ?? value.model?.name; + push('${AppRoute.widgetDetail.url}$name', extra: value.model); + return; + case ToggleLikeWidget(): + toggleLike(value.widgetId); + } + } +} diff --git a/modules/widget_system/widget_module/lib/blocs/blocs.dart b/modules/widget_system/widget_module/lib/blocs/blocs.dart new file mode 100644 index 000000000..443306fb9 --- /dev/null +++ b/modules/widget_system/widget_module/lib/blocs/blocs.dart @@ -0,0 +1,4 @@ +export 'category_bloc/category_bloc.dart'; +export 'category_widget_bloc/category_widget_bloc.dart'; +export 'widget_detail_bloc/widget_detail_bloc.dart'; +export 'widgets_bloc/widgets_bloc.dart'; diff --git a/packages/widget_module/lib/blocs/category_bloc/category_bloc.dart b/modules/widget_system/widget_module/lib/blocs/category_bloc/category_bloc.dart similarity index 98% rename from packages/widget_module/lib/blocs/category_bloc/category_bloc.dart rename to modules/widget_system/widget_module/lib/blocs/category_bloc/category_bloc.dart index cc892dbff..cd4f03c9a 100644 --- a/packages/widget_module/lib/blocs/category_bloc/category_bloc.dart +++ b/modules/widget_system/widget_module/lib/blocs/category_bloc/category_bloc.dart @@ -1,8 +1,8 @@ -import 'package:storage/storage.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:widget_repository/widget_repository.dart'; + part 'category_event.dart'; part 'category_state.dart'; diff --git a/packages/widget_module/lib/blocs/category_bloc/category_event.dart b/modules/widget_system/widget_module/lib/blocs/category_bloc/category_event.dart similarity index 100% rename from packages/widget_module/lib/blocs/category_bloc/category_event.dart rename to modules/widget_system/widget_module/lib/blocs/category_bloc/category_event.dart diff --git a/packages/widget_module/lib/blocs/category_bloc/category_state.dart b/modules/widget_system/widget_module/lib/blocs/category_bloc/category_state.dart similarity index 100% rename from packages/widget_module/lib/blocs/category_bloc/category_state.dart rename to modules/widget_system/widget_module/lib/blocs/category_bloc/category_state.dart diff --git a/modules/widget_system/widget_module/lib/blocs/category_widget_bloc/category_widget_bloc.dart b/modules/widget_system/widget_module/lib/blocs/category_widget_bloc/category_widget_bloc.dart new file mode 100644 index 000000000..9ddea8b70 --- /dev/null +++ b/modules/widget_system/widget_module/lib/blocs/category_widget_bloc/category_widget_bloc.dart @@ -0,0 +1,41 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:widget_repository/widget_repository.dart'; + +import '../category_bloc/category_bloc.dart'; + +part 'category_widget_event.dart'; +part 'category_widget_state.dart'; + +/// create by 张风捷特烈 on 2020-04-21 +/// contact me by email 1981462002@qq.com +/// 说明: + +class CategoryWidgetBloc + extends Bloc { + final CategoryBloc categoryBloc; + + CategoryWidgetBloc({required this.categoryBloc}) + : super(CategoryWidgetEmptyState()) { + on(_onEventLoadCategoryWidget); + on(_onEventToggleCategoryWidget); + } + + CategoryRepository get repository => categoryBloc.repository; + + void _onEventLoadCategoryWidget( + EventLoadCategoryWidget event, Emitter emit) async { + final widgets = + await repository.loadCategoryWidgets(categoryId: event.categoryId); + widgets.isNotEmpty + ? emit(CategoryWidgetLoadedState(widgets)) + : emit(CategoryWidgetEmptyState()); + categoryBloc.add(const EventLoadCategory()); + } + + void _onEventToggleCategoryWidget(EventToggleCategoryWidget event, + Emitter emit) async { + await repository.toggleCategory(event.categoryId, event.widgetId); + add(EventLoadCategoryWidget(event.categoryId, 'zh-cn')); + } +} diff --git a/modules/widget_system/widget_module/lib/blocs/category_widget_bloc/category_widget_event.dart b/modules/widget_system/widget_module/lib/blocs/category_widget_bloc/category_widget_event.dart new file mode 100644 index 000000000..45a23fd69 --- /dev/null +++ b/modules/widget_system/widget_module/lib/blocs/category_widget_bloc/category_widget_event.dart @@ -0,0 +1,30 @@ +part of 'category_widget_bloc.dart'; + +/// create by 张风捷特烈 on 2020-04-21 +/// contact me by email 1981462002@qq.com +/// 说明: + +abstract class CategoryWidgetEvent extends Equatable { + @override + List get props => []; +} + +class EventLoadCategoryWidget extends CategoryWidgetEvent { + final int categoryId; + final String locale; + + EventLoadCategoryWidget(this.categoryId, this.locale); + + @override + List get props => [categoryId, locale]; +} + +class EventToggleCategoryWidget extends CategoryWidgetEvent { + final int categoryId; + final int widgetId; + + EventToggleCategoryWidget(this.categoryId, this.widgetId); + + @override + List get props => [categoryId, widgetId]; +} diff --git a/packages/widget_module/lib/blocs/category_widget_bloc/category_widget_state.dart b/modules/widget_system/widget_module/lib/blocs/category_widget_bloc/category_widget_state.dart similarity index 100% rename from packages/widget_module/lib/blocs/category_widget_bloc/category_widget_state.dart rename to modules/widget_system/widget_module/lib/blocs/category_widget_bloc/category_widget_state.dart diff --git a/modules/widget_system/widget_module/lib/blocs/widget_detail_bloc/widget_detail_bloc.dart b/modules/widget_system/widget_module/lib/blocs/widget_detail_bloc/widget_detail_bloc.dart new file mode 100644 index 000000000..73ae25c0e --- /dev/null +++ b/modules/widget_system/widget_module/lib/blocs/widget_detail_bloc/widget_detail_bloc.dart @@ -0,0 +1,66 @@ +import 'dart:ui'; + +import 'package:equatable/equatable.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:widget_repository/widget_repository.dart'; + +part 'widget_detail_state.dart'; + +/// create by 张风捷特烈 on 2020-03-03 +/// contact me by email 1981462002@qq.com +/// 说明: 组件详情页状态数据维护核心类 +/// [_modelStack] 组件详情页支持管理跳转,栈数据交由 Bloc 维护 + +class WidgetDetailBloc extends Cubit { + final WidgetRepository widgetRepo; + final NodeRepository nodeRepo; + + WidgetDetailBloc({ + required this.widgetRepo, + required this.nodeRepo, + }) : super(DetailLoading()); + + List _modelStack = []; + + WidgetModel get currentWidget => _modelStack.last; + + void push(WidgetModel model, {String? locale}) { + _modelStack.add(model); + queryDetail(model, locale: locale); + } + + Future pop() async { + if (_modelStack.isEmpty) { + return true; + } + _modelStack.removeLast(); + if (_modelStack.isNotEmpty) { + queryDetail(currentWidget); + return false; + } else { + return true; + } + } + + void queryDetail(WidgetModel widget, {String? locale}) async { + emit(DetailLoading()); + try { + final List nodes = + await nodeRepo.loadNode(widget.id, locale: locale); + final List links = + await widgetRepo.loadWidget(widget.links, locale); + emit(DetailWithData(widgetModel: widget, nodes: nodes, links: links)); + } catch (e, s) { + print("queryDetail=error===${e}=$s=="); + emit(DetailFailed()); + } + } + + void changeLocale(Locale locale) async { + String localeStr = + '${locale.languageCode}-${locale.countryCode}'.toLowerCase(); + List ids = _modelStack.map((e) => e.id).toList(); + _modelStack = await widgetRepo.loadWidget(ids, localeStr); + queryDetail(currentWidget, locale: localeStr); + } +} diff --git a/packages/widget_module/lib/blocs/widget_detail_bloc/widget_detail_state.dart b/modules/widget_system/widget_module/lib/blocs/widget_detail_bloc/widget_detail_state.dart similarity index 96% rename from packages/widget_module/lib/blocs/widget_detail_bloc/widget_detail_state.dart rename to modules/widget_system/widget_module/lib/blocs/widget_detail_bloc/widget_detail_state.dart index d54e96638..31376c33c 100644 --- a/packages/widget_module/lib/blocs/widget_detail_bloc/widget_detail_state.dart +++ b/modules/widget_system/widget_module/lib/blocs/widget_detail_bloc/widget_detail_state.dart @@ -45,6 +45,4 @@ class DetailWithData extends DetailState { class DetailLoading extends DetailState {} -class DetailEmpty extends DetailState {} - class DetailFailed extends DetailState {} diff --git a/modules/widget_system/widget_module/lib/blocs/widgets_bloc/widgets_bloc.dart b/modules/widget_system/widget_module/lib/blocs/widgets_bloc/widgets_bloc.dart new file mode 100644 index 000000000..8a809104f --- /dev/null +++ b/modules/widget_system/widget_module/lib/blocs/widgets_bloc/widgets_bloc.dart @@ -0,0 +1,136 @@ +import 'dart:async'; +import 'dart:ui'; + +import 'package:equatable/equatable.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:fx_platform_adapter/fx_platform_adapter.dart'; +import 'package:widget_repository/widget_repository.dart'; + +part 'widgets_event.dart'; + +part 'widgets_state.dart'; + +/// create by 张风捷特烈 on 2020-03-03 +/// contact me by email 1981462002@qq.com +/// 说明: 处理主页 Widget 列表加载逻辑 + +class WidgetsBloc extends Bloc { + final WidgetRepository repository; + + WidgetsBloc({required this.repository}) : super(const WidgetsLoading()) { + on(_onEventTabTap); + on(_onEventLoadMore); + on(_onEventRefresh); + on(_onEventSearchWidget); + } + + /// 切换页签,以 [family] 为过滤项 + void _onEventTabTap(EventTabTap event, Emitter emit) async { + // emit(const WidgetsLoading(operate: LoadOperate.load)); + int size = kAppEnv.isDesktop ? 1000 : 20; + WidgetFilter filter = + WidgetFilter.family(event.family, pageSize: size, locale: event.locale); + try { + final List widgets = await repository.searchWidgets(filter); + emit(WidgetsLoaded( + widgets: widgets, + filter: filter, + operate: LoadOperate.load, + )); + } catch (err, t) { + print("======$err==========$t=============="); + emit(WidgetsLoadFailed( + err.toString(), + filter: filter, + operate: LoadOperate.load, + )); + } + } + + FutureOr _onEventRefresh( + EventRefresh event, Emitter emit) async { + try { + await Future.delayed(const Duration(milliseconds: 500)); + final List widgets = + await repository.searchWidgets(state.filter.copyWith(page: 1)); + emit(WidgetsLoaded( + widgets: widgets, + filter: state.filter, + operate: LoadOperate.refresh, + fetchTime: DateTime.now().millisecondsSinceEpoch, + )); + } catch (err) { + print(err); + emit(WidgetsLoadFailed( + err.toString(), + filter: state.filter, + operate: LoadOperate.refresh, + )); + } + } + + FutureOr _onEventLoadMore( + EventLoadMore event, Emitter emit) async { + if (state is WidgetsLoaded) { + WidgetsLoaded old = (state as WidgetsLoaded); + int total = await repository.total(old.filter); + if (old.widgets.length < old.filter.pageSize) { + // 不满一页 + emit(old.copyWith( + full: true, + operate: LoadOperate.more, + fetchTime: DateTime.now().millisecondsSinceEpoch, + )); + return; + } + + if (total <= old.widgets.length) { + // 已满 + emit(old.copyWith( + full: true, + operate: LoadOperate.more, + fetchTime: DateTime.now().millisecondsSinceEpoch, + )); + return; + } + // 未满,继续加载下一页 + int pageIndex = old.widgets.length ~/ old.filter.pageSize + 1; + WidgetFilter filter = old.filter.copyWith(page: pageIndex); + final List newData = await repository.searchWidgets(filter); + List newWidget = old.widgets + newData; + emit(old.copyWith( + widgets: newWidget, + full: newWidget.length == total, + operate: LoadOperate.more, + fetchTime: DateTime.now().millisecondsSinceEpoch, + filter: filter, + )); + } + } + + void _onEventSearchWidget( + EventSearchWidget event, Emitter emit) async { + emit(const WidgetsLoading(operate: LoadOperate.load)); + try { + final List widgets = + await repository.searchWidgets(event.filter); + emit(WidgetsLoaded( + widgets: widgets, + filter: event.filter, + operate: LoadOperate.load, + )); + } catch (err) { + print(err); + emit(WidgetsLoadFailed( + err.toString(), + filter: event.filter, + operate: LoadOperate.load, + )); + } + } + + void changeLocale(Locale locale) { + add(EventTabTap(state.filter.family ?? WidgetFamily.stateless, + locale: '${locale.languageCode}-${locale.countryCode}'.toLowerCase())); + } +} diff --git a/packages/widget_module/lib/blocs/widgets_bloc/widgets_event.dart b/modules/widget_system/widget_module/lib/blocs/widgets_bloc/widgets_event.dart similarity index 90% rename from packages/widget_module/lib/blocs/widgets_bloc/widgets_event.dart rename to modules/widget_system/widget_module/lib/blocs/widgets_bloc/widgets_event.dart index 99a0e6019..7dcdacbdd 100644 --- a/packages/widget_module/lib/blocs/widgets_bloc/widgets_event.dart +++ b/modules/widget_system/widget_module/lib/blocs/widgets_bloc/widgets_event.dart @@ -12,8 +12,9 @@ abstract class WidgetsEvent extends Equatable { class EventTabTap extends WidgetsEvent { final WidgetFamily family; + final String? locale; - const EventTabTap(this.family); + const EventTabTap(this.family,{this.locale}); @override List get props => [family]; diff --git a/packages/widget_module/lib/blocs/widgets_bloc/widgets_state.dart b/modules/widget_system/widget_module/lib/blocs/widgets_bloc/widgets_state.dart similarity index 80% rename from packages/widget_module/lib/blocs/widgets_bloc/widgets_state.dart rename to modules/widget_system/widget_module/lib/blocs/widgets_bloc/widgets_state.dart index 2add619c0..e9fd3efc2 100644 --- a/packages/widget_module/lib/blocs/widgets_bloc/widgets_state.dart +++ b/modules/widget_system/widget_module/lib/blocs/widgets_bloc/widgets_state.dart @@ -7,9 +7,12 @@ part of 'widgets_bloc.dart'; /// 对于一个可查询、可分页的数据集 /// 状态类持有过滤数据 Filter, 任何衍生状态都需要感知过滤的信息,比如加载异常,点击重试,可以基于最后的过滤状态。 /// 分页加载 信息需要 了解 总数据、页数、每页含量 -abstract class WidgetsState extends Equatable { +/// + +sealed class WidgetsState extends Equatable { final WidgetFilter filter; final LoadOperate operate; + const WidgetsState({required this.filter, required this.operate}); @override @@ -17,8 +20,10 @@ abstract class WidgetsState extends Equatable { } class WidgetsLoading extends WidgetsState { - const WidgetsLoading( - {super.filter = const WidgetFilter(), super.operate = LoadOperate.load}); + const WidgetsLoading({ + super.filter = const WidgetFilter(), + super.operate = LoadOperate.load, + }); } /// [full] 是否已满,用于加载更多到底的标识 @@ -37,7 +42,7 @@ class WidgetsLoaded extends WidgetsState { }); @override - List get props => [widgets,full,filter,operate,fetchTime]; + List get props => [widgets, full, filter, operate, fetchTime]; @override String toString() { @@ -50,16 +55,15 @@ class WidgetsLoaded extends WidgetsState { LoadOperate? operate, WidgetFilter? filter, int? fetchTime, -}){ + }) { return WidgetsLoaded( - widgets:widgets??this.widgets, - full:full??this.full, - operate:operate??this.operate, - filter:filter??this.filter, - fetchTime:fetchTime??this.fetchTime, + widgets: widgets ?? this.widgets, + full: full ?? this.full, + operate: operate ?? this.operate, + filter: filter ?? this.filter, + fetchTime: fetchTime ?? this.fetchTime, ); } - } class WidgetsLoadFailed extends WidgetsState { diff --git a/modules/widget_system/widget_module/lib/event/widget_event.dart b/modules/widget_system/widget_module/lib/event/widget_event.dart new file mode 100644 index 000000000..0a38e6e55 --- /dev/null +++ b/modules/widget_system/widget_module/lib/event/widget_event.dart @@ -0,0 +1,16 @@ +import 'package:fx_trace/fx_trace.dart'; + +import '../widget_module.dart'; + +class SelectWidgetEvent extends FxEvent { + final String name; + final int? id; + final WidgetModel? model; + + + SelectWidgetEvent({ + required this.name, + this.id, + this.model, + }); +} diff --git a/modules/widget_system/widget_module/lib/event/widget_statistics_event.dart b/modules/widget_system/widget_module/lib/event/widget_statistics_event.dart new file mode 100644 index 000000000..31830feb1 --- /dev/null +++ b/modules/widget_system/widget_module/lib/event/widget_statistics_event.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; +import 'package:storage/storage.dart'; +import 'package:widget_repository/widget_repository.dart'; + +/// 初始化统计数据 +Future initWidgetStatistics() async { + await WidgetStatisticsProvider().loadStatistics( + AppStorage().flutter(), + ); +} diff --git a/modules/widget_system/widget_module/lib/views/components/collected_tag.dart b/modules/widget_system/widget_module/lib/views/components/collected_tag.dart new file mode 100644 index 000000000..f39b3f82d --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/components/collected_tag.dart @@ -0,0 +1,29 @@ +// import 'package:flutter/material.dart'; +// import 'package:l10n/ext.dart'; +// import 'package:wrapper/wrapper.dart'; +// +// class CollectedTag extends StatelessWidget { +// const CollectedTag({super.key}); +// +// @override +// Widget build(BuildContext context) { +// bool isDark = Theme.of(context).brightness == Brightness.dark; +// Color color = Theme.of(context).primaryColor; +// String text = context.l10n.favorite; +// return Wrapper.just( +// radius: 10, +// color: isDark ? const Color(0xff292A2D) : const Color(0xffF3F3F5), +// padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), +// child: Text( +// text, +// style: TextStyle(color: color, height: 1, fontSize: 10, shadows: [ +// Shadow( +// color: isDark ? Colors.black : Colors.white, +// blurRadius: 2, +// offset: const Offset(1, 1), +// ) +// ]), +// ), +// ); +// } +// } diff --git a/modules/widget_system/widget_module/lib/views/components/widget_logo_map.dart b/modules/widget_system/widget_module/lib/views/components/widget_logo_map.dart new file mode 100644 index 000000000..f142b23de --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/components/widget_logo_map.dart @@ -0,0 +1,64 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:fx_env/fx_env.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +class WidgetLogo extends StatelessWidget { + final Color background; + final String widgetName; + + const WidgetLogo({ + super.key, + required this.background, + required this.widgetName, + }); + + @override + Widget build(BuildContext context) { + return Container( + width: 110, + height: 110, + alignment: Alignment.center, + decoration: BoxDecoration( + color: background, + gradient: LinearGradient( + transform: const GradientRotation(270 * 180 / pi), + colors: [ + background.withValues(alpha: 0.9), + background.withValues(alpha: 0.5) + ]), + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(6), bottomLeft: Radius.circular(6)), + ), + child: SvgPicture.asset( + 'assets/images/widgets/${widgetLogo(widgetName)}', + width: kApp.isDesktopUI ? 90 : 80, + ), + ); + } +} + +String widgetLogo(String widgetName) { + return switch (widgetName) { + 'Container' => 'Container.svg', + 'Text' => 'Text.svg', + 'GestureDetector' => 'GestureDetector.svg', + 'CircleAvatar' => 'CircleAvatar.svg', + 'Card' => 'Card.svg', + 'ListView' => 'ListView.svg', + 'GridView' => 'GridView.svg', + 'SingleChildScrollView' => 'SingleChildScrollView.svg', + 'PageView' => 'PageView.svg', + 'InputChip' => 'InputChip.svg', + 'Chip' => 'Chip.svg', + 'FilterChip' => 'FilterChip.svg', + 'MaterialButton' => 'MaterialButton.svg', + 'FlutterLogo' => 'FlutterLogo.svg', + 'RichText' => 'RichText.svg', + 'FloatingActionButton' => 'FloatingActionButton.svg', + 'Banner' => 'Banner.svg', + 'Icon' => 'Icon.svg', + _ => 'Widget.svg', + }; +} diff --git a/modules/widget_system/widget_module/lib/views/desk_ui/category_panel/desk_category_page.dart b/modules/widget_system/widget_module/lib/views/desk_ui/category_panel/desk_category_page.dart new file mode 100644 index 000000000..7817fd20f --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/desk_ui/category_panel/desk_category_page.dart @@ -0,0 +1,153 @@ +import 'package:app/app.dart'; +import 'package:components/components.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; +import 'package:toly_ui/toly_ui.dart'; +import 'package:widget_module/blocs/blocs.dart'; + +import 'package:widget_repository/widget_repository.dart'; +import '../../mobile/category_page/category_list_item.dart'; +import '../../mobile/category_page/delete_category_dialog.dart'; +import '../../mobile/category_page/edit_category_panel.dart'; +import 'desk_top_like_panel.dart'; + +class DeskCategoryPage extends StatefulWidget { + const DeskCategoryPage({Key? key}) : super(key: key); + + @override + State createState() => _DeskCategoryPageState(); +} + +class _DeskCategoryPageState extends State { + final PageController _ctrl = PageController(); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Column( + children: [ + DeskTabTopBar( + onTabPressed: (int value) { + _ctrl.jumpToPage(value); + }, + tabs: ['组件酒肆', '珍藏组件'], + ), + Expanded( + child: PageView( + controller: _ctrl, + children: [ + DeskCateGoryPage(), + DeskLikePage(), + ], + )) + ], + ), + ); + } +} + +class DeskCateGoryPage extends StatelessWidget { + const DeskCateGoryPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + const SliverGridDelegate deskGridDelegate = + SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: 240, + mainAxisSpacing: 8, + mainAxisExtent: 220, + crossAxisSpacing: 8, + ); + + CategoryBloc bloc = context.read(); + CategoryState state = bloc.state; + if (state is CategoryLoadedState) { + return GridView.builder( + itemCount: state.categories.length, + padding: EdgeInsets.all(12), + gridDelegate: deskGridDelegate, + itemBuilder: (_, index) => GestureDetector( + onTap: () => _toDetailPage(context, state.categories[index]), + child: CategoryListItem( + data: state.categories[index], + onDeleteItemClick: (model) => _deleteCollect(context, model), + onEditItemClick: (model) => _editCollect(context, model), + ))); + } + + return SizedBox.shrink(); + } + + ShapeBorder get rRectBorder => const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(10))); + + void _deleteCollect(BuildContext context, CategoryModel model) { + showDialog( + context: context, + builder: (ctx) => Dialog( + elevation: 5, + shape: rRectBorder, + child: SizedBox( + width: 50, + child: DeleteCategoryDialog( + title: '删除收藏集', + content: ' 删除【${model.name}】收藏集,你将会失去其中的所有收藏组件,是否确定继续执行?', + onSubmit: () { + BlocProvider.of(context) + .add(EventDeleteCategory(id: model.id!)); + Navigator.of(context).pop(); + }, + ), + ), + )); + } + + void _editCollect(BuildContext context, CategoryModel model) { + showDialog( + context: context, + builder: (ctx) => Dialog( + backgroundColor: const Color(0xFFF2F2F2), + elevation: 5, + shape: rRectBorder, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Gap.H5, + Row( + children: [ + Padding( + padding: const EdgeInsets.only(left: 20, right: 10), + child: Circle( + color: Theme.of(context).primaryColor, + ), + ), + const Text( + '修改收藏集', + style: TextStyle(fontSize: 20), + ), + const Spacer(), + const CloseButton() + ], + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: EditCategoryPanel( + model: model, + type: EditType.update, + ), + ), + ], + ), + )); + } + + void _toDetailPage(BuildContext context, CategoryModel model) { + String locale = context.read().language.code; + + BlocProvider.of(context) + .add(EventLoadCategoryWidget(model.id!, locale)); + // Navigator.pushNamed(context, UnitRouter.category_show, arguments: model); + context.push('${AppRoute.collectionDetail.url}${model.id}', extra: model); + } +} diff --git a/modules/widget_system/widget_module/lib/views/desk_ui/category_panel/desk_top_like_panel.dart b/modules/widget_system/widget_module/lib/views/desk_ui/category_panel/desk_top_like_panel.dart new file mode 100644 index 000000000..2c1d06ac4 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/desk_ui/category_panel/desk_top_like_panel.dart @@ -0,0 +1,47 @@ +import 'package:app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:widget_module/blocs/action/widget_action.dart'; +import 'package:widget_module/blocs/blocs.dart'; + +import 'package:widget_repository/widget_repository.dart'; +import 'package:widget_ui/widget_ui.dart'; +import '../../mobile/widget_detail/collect_widget_list_item.dart'; +import '../../mobile/widget_detail/widget_detail_page.dart'; + +class DeskLikePage extends StatelessWidget { + const DeskLikePage({Key? key}) : super(key: key); + + final SliverGridDelegate deskGridDelegate = + const SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: 300, + mainAxisSpacing: 15, + mainAxisExtent: 80, + crossAxisSpacing: 15, + ); + + @override + Widget build(BuildContext context) { + LikeWidgetBloc bloc = context.watch(); + List state = bloc.state; + return GridView.builder( + itemCount: state.length, + padding: EdgeInsets.all(20), + gridDelegate: deskGridDelegate, + itemBuilder: (_, index) => GestureDetector( + onTap: () => _toDetailPage(context, state[index]), + child: CollectWidgetListItem( + data: state[index], + onDeleteItemClick: (model) => context.toggleLike(model.id), + ))); + } + + ShapeBorder get rRectBorder => const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(10))); + + _toDetailPage(BuildContext context, WidgetModel model) { + // BlocProvider.of(context).add(FetchWidgetDetail(model)); + Navigator.push( + context, SlidePageRoute(child: WidgetDetailPageScope(model: model))); + } +} diff --git a/modules/widget_system/widget_module/lib/views/desk_ui/desk_ui.dart b/modules/widget_system/widget_module/lib/views/desk_ui/desk_ui.dart new file mode 100644 index 000000000..77d7097aa --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/desk_ui/desk_ui.dart @@ -0,0 +1,5 @@ +export 'widget_detail/widget_detail_page.dart'; +export 'category_panel/desk_category_page.dart'; +export 'widget_panel/widget_panel.dart'; +export 'widget_panel/desk_search_bar.dart'; +export 'widget_panel/desk_search_bar_v2.dart'; \ No newline at end of file diff --git a/lib/widget_ui/desk_ui/widget_detail/link_widget_buttons.dart b/modules/widget_system/widget_module/lib/views/desk_ui/widget_detail/link_widget_buttons.dart similarity index 99% rename from lib/widget_ui/desk_ui/widget_detail/link_widget_buttons.dart rename to modules/widget_system/widget_module/lib/views/desk_ui/widget_detail/link_widget_buttons.dart index b52e2d093..b357dc4c4 100644 --- a/lib/widget_ui/desk_ui/widget_detail/link_widget_buttons.dart +++ b/modules/widget_system/widget_module/lib/views/desk_ui/widget_detail/link_widget_buttons.dart @@ -2,6 +2,7 @@ import 'package:app/app.dart'; import 'package:flutter/material.dart'; import 'package:widget_repository/widget_repository.dart'; + class LinkWidgetButtons extends StatelessWidget { final List links; final ValueChanged onSelect; diff --git a/modules/widget_system/widget_module/lib/views/desk_ui/widget_detail/widget_detail_bar.dart b/modules/widget_system/widget_module/lib/views/desk_ui/widget_detail/widget_detail_bar.dart new file mode 100644 index 000000000..5fcdba687 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/desk_ui/widget_detail/widget_detail_bar.dart @@ -0,0 +1,128 @@ +import 'dart:math'; + +import 'package:app/app.dart'; +import 'package:components/components.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:toly_ui/toly_ui.dart'; +import 'package:utils/utils.dart'; +import 'package:widget_module/blocs/action/widget_action.dart'; +import 'package:widget_ui/widget_ui.dart'; + +import 'package:widget_module/blocs/blocs.dart'; +import 'package:widget_repository/widget_repository.dart'; + +class DeskSliverWidgetDetailBar extends StatelessWidget { + final WidgetModel model; + + const DeskSliverWidgetDetailBar({Key? key, required this.model}) + : super(key: key); + + final Color backgroundColor = const Color(0xffFAFAFA); + static const Color textColor = Color(0xff262626); + + @override + Widget build(BuildContext context) { + bool isDark = Theme.of(context).brightness == Brightness.dark; + Color? appBarColor = Theme.of(context).appBarTheme.backgroundColor; + Color? appBarTextColor = + Theme.of(context).appBarTheme.titleTextStyle?.color; + + return SliverAppBar( + pinned: true, + backgroundColor: isDark ? appBarColor : backgroundColor, + titleTextStyle: + TextStyle(color: isDark ? appBarTextColor : Color(0xff696969)), + iconTheme: + IconThemeData(color: isDark ? appBarTextColor : Color(0xff696969)), + expandedHeight: 120.0, + scrolledUnderElevation: 0.5, + flexibleSpace: DragToMoveWrapper( + child: DiyFlexibleSpaceBar( + centerTitle: false, + expandedTitleScale: 2, + titleIconBuilder: (t) => WindmillWidget( + rotate: t * 2 * pi * 2, + radius: 15, + ), + fixedSubtitle: Text( + model.name, + style: TextStyle( + color: isDark ? appBarTextColor : Color(0xff696969), + fontSize: 12), + ), + title: Padding( + padding: const EdgeInsets.only(bottom: 3), + child: Text( + model.nameCN, + style: TextStyle( + color: isDark ? appBarTextColor : textColor, fontSize: 16), + ), + ), + //伸展处布局 + titlePadding: const EdgeInsets.only(left: 20, bottom: 10), + //标题边距 + collapseMode: CollapseMode.parallax, + ), + ), + elevation: 0, + actions: [ + WindowButtons( + actions: [ + _buildToHome(context), + FeedbackWidget( + onPressed: () => context.toggleLike(model.id), + child: BlocConsumer>( + listener: _listenLikeStateChange, + builder: _buildByLikeState, + ), + ), + ], + ) + ], + ); + } + + // 监听 LikeWidgetBloc 伺机弹出 toast + void _listenLikeStateChange(BuildContext context, List state) { + bool collected = state.contains(model); + String msg = + collected ? "收藏【${model.name}】组件成功!" : "已取消【${model.name}】组件收藏!"; + Toast.toast( + context, + msg, + duration: Duration(milliseconds: collected ? 1500 : 600), + action: collected + ? SnackBarAction( + textColor: Colors.white, + label: '收藏夹管理', + onPressed: () => Scaffold.of(context).openEndDrawer()) + : null, + ); + } + + // 根据 [LikeWidgetState ] 构建图标 + Widget _buildByLikeState(BuildContext context, List state) { + bool liked = state.contains(model); + return SizedBox( + width: 30, + height: 30, + child: Icon( + liked ? TolyIcon.icon_star_ok : TolyIcon.icon_star_add, + size: 20, + ), + ); + } + + Widget _buildToHome(BuildContext context) => GestureDetector( + onLongPress: () => Scaffold.of(context).openEndDrawer(), + child: const SizedBox( + width: 30, + height: 30, + child: Icon( + Icons.home, + size: 20, + ), + ), + onTap: () => Navigator.of(context).pop()); +} diff --git a/modules/widget_system/widget_module/lib/views/desk_ui/widget_detail/widget_detail_page.dart b/modules/widget_system/widget_module/lib/views/desk_ui/widget_detail/widget_detail_page.dart new file mode 100644 index 000000000..cfa080ac1 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/desk_ui/widget_detail/widget_detail_page.dart @@ -0,0 +1,152 @@ +import 'package:app/app.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:l10n/l10n.dart'; +import 'package:widget_module/blocs/blocs.dart'; +import 'package:widget_repository/widget_repository.dart'; + +import '../../mobile/mobile_ui.dart'; +import '../../mobile/widget_detail/category_end_drawer.dart'; +import '../../mobile/widget_detail/node_display/node_display.dart'; +import '../../mobile/widget_detail/widget_fields_sliver.dart'; +import 'link_widget_buttons.dart'; +import 'widget_detail_bar.dart'; +import 'widget_detail_panel.dart'; + +// 用于组件详情不需要在一开始就加载 +// WidgetDetailBloc 可以在稍后提供 +class DeskWidgetDetailPageScope extends StatefulWidget { + final WidgetModel? model; + final String? widgetName; + + const DeskWidgetDetailPageScope( + {super.key, required this.model, this.widgetName}); + + @override + State createState() => + _DeskWidgetDetailPageScopeState(); +} + +class _DeskWidgetDetailPageScopeState extends State { + WidgetModel? _model; + + WidgetRepository get widgetRepository => + context.read().repository; + + NodeRepository get nodeRepository => + kIsWeb ? MemoryNodeRepository() : const NodeDbRepository(); + + @override + void initState() { + super.initState(); + _model = widget.model; + if (_model == null) { + _loadModelByName(); + } + } + + void _loadModelByName() async { + _model = await widgetRepository.queryWidgetByName(widget.widgetName); + setState(() {}); + } + + @override + Widget build(BuildContext context) { + if (_model == null) + return const Center(child: CupertinoActivityIndicator()); + Locale locale = context.read().state.language.locale; + // Locale locale = Localizations.localeOf(context); + // String? countryCode = locale.countryCode; + // if (countryCode == null) {} + String localeStr = + '${locale.languageCode}-${locale.countryCode}'.toLowerCase(); + + return BlocProvider( + create: (_) => WidgetDetailBloc( + widgetRepo: widgetRepository, + nodeRepo: nodeRepository, + )..push(_model!, locale: localeStr), + child: DeskWidgetDetailPage( + model: widget.model, + ), + ); + } +} + +class DeskWidgetDetailPage extends StatelessWidget { + final WidgetModel? model; + + const DeskWidgetDetailPage({Key? key, required this.model}) : super(key: key); + + @override + Widget build(BuildContext context) { + WidgetDetailBloc bloc = context.watch(); + DetailState state = context.watch().state; + WidgetModel widget = bloc.currentWidget; + return BlocListener( + listenWhen: (p, n) => p.language != n.language, + listener: (_, state) { + BlocProvider.of(context) + .changeLocale(state.language.locale); + }, + child: Scaffold( + backgroundColor: Theme.of(context).appBarTheme.backgroundColor, + endDrawer: CategoryEndDrawer(widget: widget), + body: Builder(builder: (ctx) { + return _buildContent(ctx, bloc, state); + }), + ), + ); + } + + Widget linkText(BuildContext context) => Row( + children: [ + const Padding( + padding: EdgeInsets.only(left: 15, right: 5), + child: Icon(Icons.link, color: Colors.blue), + ), + Text(context.l10n.relatedComponents, style: UnitTextStyle.labelBold), + ], + ); + + Widget _buildContent( + BuildContext context, WidgetDetailBloc bloc, DetailState state) { + return WillPopScope( + onWillPop: () => _whenPop(context), + child: CustomScrollView( + slivers: [ + DeskSliverWidgetDetailBar(model: bloc.currentWidget), + SliverToBoxAdapter( + child: Column( + children: [ + DeskWidgetDetailPanel( + model: bloc.currentWidget, + state: state, + ), + const Divider( + height: 18, + ) + ], + ), + ), + if (state is DetailWithData) + state.nodes.isNotEmpty + ? SliverNodeList( + nodes: state.nodes, + model: state.widgetModel, + ) + : SliverWidgetFieldsList(widgetId: model!.id), + ], + )); + } + + Future _whenPop(BuildContext context) async { + WidgetDetailBloc detailBloc = context.read(); + if (Scaffold.of(context).isEndDrawerOpen) { + return true; + } + return detailBloc.pop(); + } +} diff --git a/modules/widget_system/widget_module/lib/views/desk_ui/widget_detail/widget_detail_panel.dart b/modules/widget_system/widget_module/lib/views/desk_ui/widget_detail/widget_detail_panel.dart new file mode 100644 index 000000000..3ca8a6c91 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/desk_ui/widget_detail/widget_detail_panel.dart @@ -0,0 +1,78 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_star/flutter_star.dart'; +import 'package:toly_ui/toly_ui.dart'; +import 'package:widget_repository/widget_repository.dart'; +import 'package:widget_ui/widget_ui.dart'; +import 'dart:math'; + +import '../../../blocs/blocs.dart'; +import '../../components/widget_logo_map.dart'; + +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:l10n/l10n.dart'; +import 'link_widget_buttons.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class DeskWidgetDetailPanel extends StatelessWidget { + final WidgetModel model; + final DetailState state; + + const DeskWidgetDetailPanel( + {Key? key, required this.model, required this.state}) + : super(key: key); + + @override + Widget build(BuildContext context) { + Color color = Theme.of(context).primaryColor; + + return Padding( + padding: const EdgeInsets.all(12.0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildLeft(model, context), + const SizedBox(width: 12), + Hero( + tag: "hero_widget_image_${model.id}", + child: WidgetDetailLogo( + model: model, + background: color, + widgetName: model.name, + ), + ) + ], + ), + ); + } + + Widget linkText(BuildContext context) => Row( + children: [ + const Padding( + padding: EdgeInsets.only(left: 15, right: 5), + child: Icon(Icons.link, color: Colors.blue), + ), + Text(context.l10n.relatedComponents, + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)), + ], + ); + + Widget _buildLeft(WidgetModel model, BuildContext context) => Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Panel( + color: Color(0x33E5EAE1), + child: Text(model.info), + ), + const SizedBox(height: 16), + linkText(context), + if (state is DetailWithData) + LinkWidgetButtons( + links: (state as DetailWithData).links, + onSelect: (v) { + context.read().push(v); + }) + ], + ), + ); +} diff --git a/modules/widget_system/widget_module/lib/views/desk_ui/widget_detail/widget_node_panel.dart b/modules/widget_system/widget_module/lib/views/desk_ui/widget_detail/widget_node_panel.dart new file mode 100644 index 000000000..cdf6533a5 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/desk_ui/widget_detail/widget_node_panel.dart @@ -0,0 +1,166 @@ +import 'package:app/app.dart'; +import 'package:toly_ui/toly_ui.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +import 'package:toggle_rotate/toggle_rotate.dart'; +import 'package:utils/utils.dart'; + +/// create by 张风捷特烈 on 2020-04-13 +/// contact me by email 1981462002@qq.com +/// 说明: 一个Widget的知识点对应的界面 + +class DeskWidgetNodePanel extends StatefulWidget { + final String text; + final String subText; + final String code; + final Widget? show; + final HighlighterStyle? codeStyle; + final String? codeFamily; + final bool death; + + const DeskWidgetNodePanel( + {Key? key, + this.text = '', + this.subText = '', + this.code = '', + this.death = false, + this.show, + required this.codeStyle, + this.codeFamily}) + : super(key: key); + + @override + _DeskWidgetNodePanelState createState() => _DeskWidgetNodePanelState(); +} + +class _DeskWidgetNodePanelState extends State { + CrossFadeState _crossFadeState = CrossFadeState.showFirst; + + bool get isFirst => _crossFadeState == CrossFadeState.showFirst; + + Color get themeColor => Theme.of(context).primaryColor; + + @override + Widget build(BuildContext context) { + return Container( + margin: const EdgeInsets.all(10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + buildNodeTitle(), + const SizedBox( + height: 20, + ), + _buildCode(context), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Center( + child: Padding( + padding: const EdgeInsets.only(top: 10, bottom: 20), + child: widget.show, + ), + ), + ), + if (!widget.death) Expanded(child: _buildNodeInfo()), + ], + ), + const SizedBox( + height: 16, + ), + const Divider(), + ], + ), + ); + } + + Widget buildNodeTitle() => Row( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Circle( + color: themeColor, + radius: 5, + ), + ), + Expanded( + child: Text( + widget.text, + style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 15), + ), + ), + _buildShareButton(), + _buildCodeButton(), + ], + ); + + Widget _buildNodeInfo() => SizedBox( + width: double.infinity, + child: Panel( + color: Color(0x33E5EAE1), + child: Text( + widget.subText, + style: const TextStyle(fontSize: 12), + )), + ); + + Widget _buildCodeButton() => Padding( + padding: const EdgeInsets.only(right: 10.0), + child: ToggleRotate( + durationMs: 300, + child: Icon( + TolyIcon.icon_code, + color: themeColor, + ), + onTap: _toggleCodePanel, + ), + ); + + Widget _buildShareButton() => FeedbackWidget( + mode: FeedMode.fade, + a: 0.4, + onPressed: _doShare, + child: Padding( + padding: const EdgeInsets.only(right: 10), + child: Icon( + Icons.copy, + size: 20, + color: themeColor, + ), + ), + ); + + Widget _buildCode(BuildContext context) => AnimatedCrossFade( + firstCurve: Curves.easeInCirc, + secondCurve: Curves.easeInToLinear, + firstChild: const SizedBox(), + secondChild: SizedBox( + width: MediaQuery.of(context).size.width, + child: CodeWidget( + fontFamily: widget.codeFamily, + code: isFirst ? '' : widget.code, + style: widget.codeStyle ?? + HighlighterStyle.fromColors(HighlighterStyle.lightColor), + ), + ), + duration: const Duration(milliseconds: 200), + crossFadeState: _crossFadeState, + ); + + //执行分享 + void _doShare() async { + // Share.share(widget.code); + await Clipboard.setData(ClipboardData(text: widget.code)); + Toast.success(context, '代码复制成功!'); + } + + // 折叠代码面板 + void _toggleCodePanel() { + setState(() { + _crossFadeState = + !isFirst ? CrossFadeState.showFirst : CrossFadeState.showSecond; + }); + } +} diff --git a/lib/widget_ui/desk_ui/widget_panel/desk_search_bar.dart b/modules/widget_system/widget_module/lib/views/desk_ui/widget_panel/desk_search_bar.dart similarity index 94% rename from lib/widget_ui/desk_ui/widget_panel/desk_search_bar.dart rename to modules/widget_system/widget_module/lib/views/desk_ui/widget_panel/desk_search_bar.dart index e7c1e9121..f41f2f1a1 100644 --- a/lib/widget_ui/desk_ui/widget_panel/desk_search_bar.dart +++ b/modules/widget_system/widget_module/lib/views/desk_ui/widget_panel/desk_search_bar.dart @@ -1,9 +1,9 @@ import 'dart:async'; -import 'package:app/app.dart'; - import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; +import 'package:l10n/l10n.dart'; import 'package:widget_module/blocs/blocs.dart'; import 'package:widget_repository/widget_repository.dart'; @@ -36,12 +36,7 @@ class _DeskSearchBarState extends State { focusScope.unfocus(); } _controller.clear(); - - Navigator.pushNamed( - context, - UnitRouter.widget_detail, - arguments: model, - ); + context.push('/widget/detail/${model.name}',extra: model); } Future> buildOptions(TextEditingValue textEditingValue) async { @@ -65,6 +60,7 @@ class _DeskSearchBarState extends State { alignment: Alignment.topLeft, child: Material( elevation: 6, + color: Colors.white, borderRadius: BorderRadius.circular(8), shadowColor: Colors.black, child: ConstrainedBox( @@ -118,7 +114,6 @@ class _DeskSearchBarState extends State { color: Colors.grey, ), focusedBorder: OutlineInputBorder( - borderSide: BorderSide(color: Theme.of(context).primaryColor), borderRadius: BorderRadius.all(Radius.circular(8)), ), @@ -128,7 +123,7 @@ class _DeskSearchBarState extends State { borderSide: BorderSide.none, borderRadius: BorderRadius.all(Radius.circular(8)), ), - hintText: "输入组件名称", + hintText: context.l10n.enterComponentName, hintStyle: TextStyle(fontSize: 12, color: Colors.grey)), ); @@ -145,7 +140,7 @@ class _DeskSearchBarState extends State { Color? textColor = Theme.of(context).listTileTheme.textColor; List span = []; - RegExp regExp = RegExp(pattern, caseSensitive: false); + RegExp regExp = RegExp(RegExp.escape(pattern), caseSensitive: false); src.splitMapJoin(regExp, onMatch: (Match match) { span.add(TextSpan(text: match.group(0), style: lightTextStyle)); return ''; diff --git a/modules/widget_system/widget_module/lib/views/desk_ui/widget_panel/desk_search_bar_v2.dart b/modules/widget_system/widget_module/lib/views/desk_ui/widget_panel/desk_search_bar_v2.dart new file mode 100644 index 000000000..701ed44af --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/desk_ui/widget_panel/desk_search_bar_v2.dart @@ -0,0 +1,192 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; +import 'package:l10n/l10n.dart'; +import 'package:widget_module/blocs/blocs.dart'; +import 'package:widget_module/event/widget_event.dart'; +import 'package:widget_repository/widget_repository.dart'; +import 'package:tolyui/tolyui.dart'; +import 'package:fx_trace/fx_trace.dart'; + +class GlobalFindDialog extends StatefulWidget { + final ValueChanged? onChanged; + + const GlobalFindDialog({Key? key, this.onChanged}) : super(key: key); + + @override + State createState() => _GlobalFindDialogState(); +} + +class _GlobalFindDialogState extends State { + late TextEditingController _controller = TextEditingController(); + + // final PopoverController controller = PopoverController(); + + @override + void initState() { + _focusNode.addListener(_onFocusChange); + + super.initState(); + } + + @override + void dispose() { + super.dispose(); + _focusNode.removeListener(_onFocusChange); + } + + void _onFocusChange() { + // if (_focusNode.hasFocus) { + // controller.open(); + // } else { + // controller.close(); + // } + } + + @override + Widget build(BuildContext context) { + return Dialog( + surfaceTintColor: Colors.transparent, + backgroundColor: Colors.white, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + child: SizedBox( + width: 400, + height: 400, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + SizedBox(height: 32, child: _buildFieldView()), + Expanded(child: _buildOptionsView()), + ], + ), + ), + )); + ; + } + + void onSelected(WidgetModel model) { + final FocusScopeNode focusScope = FocusScope.of(context); + if (focusScope.hasFocus) { + focusScope.unfocus(); + } + _controller.clear(); + Navigator.of(context).pop(); + FxEmitter().emit(SelectWidgetEvent(name: model.name)); + } + + Iterable options = []; + + void searchByArgs(String text) async { + WidgetRepository repository = context.read().repository; + options = await repository.searchWidgets(WidgetFilter(name: text)); + setState(() {}); + } + + Widget _buildOptionsView() { + return Align( + alignment: Alignment.topLeft, + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 400), + child: ListView.builder( + itemCount: options.length, + padding: const EdgeInsets.symmetric(vertical: 10), + itemBuilder: (_, index) { + WidgetModel model = options.elementAt(index); + return InkWell( + onTap: () => onSelected(model), + child: Ink( + padding: + const EdgeInsets.symmetric(vertical: 6, horizontal: 15), + child: Row( + children: [ + Expanded( + child: Text.rich( + formSpan(model.name, _controller.text), + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: const TextStyle(fontSize: 12))), + // Spacer(), + const SizedBox( + width: 10, + ), + Text( + model.nameCN, + style: const TextStyle(fontSize: 12), + ), + ], + ), + ), + ); + }), + ), + ); + } + + final FocusNode _focusNode = FocusNode(); + FocusNode get focusNode => _focusNode; + + Widget _buildFieldView() { + bool isDark = Theme.of(context).brightness == Brightness.dark; + + return TextField( + autofocus: true, + controller: _controller, + onChanged: (value) { + searchByArgs(value); + }, + style: const TextStyle(fontSize: 12), + maxLines: 1, + focusNode: focusNode, + decoration: InputDecoration( + prefixIconConstraints: const BoxConstraints(minWidth: 30), + filled: true, + hoverColor: Colors.transparent, + contentPadding: const EdgeInsets.only(top: 0), + fillColor: isDark ? null : const Color(0xffF1F2F3), + prefixIcon: const Icon( + Icons.search, + size: 18, + color: Colors.grey, + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: Theme.of(context).primaryColor), + borderRadius: const BorderRadius.all(Radius.circular(8)), + ), + enabledBorder: const OutlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.all(Radius.circular(8)), + ), + hintText: '搜索', + hintStyle: const TextStyle(fontSize: 12, color: Colors.grey)), + ); + } + + final TextStyle lightTextStyle = const TextStyle( + color: Colors.red, + fontSize: 12, + fontWeight: FontWeight.bold, + ); + + InlineSpan formSpan(String src, String pattern) { + bool isDark = Theme.of(context).brightness == Brightness.dark; + Color? textColor = Theme.of(context).listTileTheme.textColor; + + List span = []; + RegExp regExp = RegExp(RegExp.escape(pattern), caseSensitive: false); + src.splitMapJoin(regExp, onMatch: (Match match) { + span.add(TextSpan(text: match.group(0), style: lightTextStyle)); + return ''; + }, onNonMatch: (str) { + span.add(TextSpan( + text: str, + style: lightTextStyle.copyWith( + color: isDark ? textColor : const Color(0xff2F3032), + fontSize: 12))); + return ''; + }); + return TextSpan(children: span); + } +} diff --git a/modules/widget_system/widget_module/lib/views/desk_ui/widget_panel/desk_widget_top_bar.dart b/modules/widget_system/widget_module/lib/views/desk_ui/widget_panel/desk_widget_top_bar.dart new file mode 100644 index 000000000..f5dcb97dc --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/desk_ui/widget_panel/desk_widget_top_bar.dart @@ -0,0 +1,149 @@ +import 'package:app/app.dart'; +import 'package:l10n/l10n.dart'; +import 'package:toly_ui/toly_ui.dart'; +import 'package:flutter/material.dart'; +import 'package:widget_repository/widget_repository.dart'; + +import 'desk_search_bar.dart'; + +class DeskWidgetTopBar extends StatefulWidget { + final ValueChanged onTabPressed; + + const DeskWidgetTopBar({Key? key, required this.onTabPressed}) + : super(key: key); + + @override + State createState() => _DeskWidgetTopBarState(); +} + +class _DeskWidgetTopBarState extends State + with SingleTickerProviderStateMixin { + late TabController tabController; + + List get _tabs { + final provider = WidgetStatisticsProvider(); + final stats = provider.statistics; + + if (stats == null) { + return [ + context.l10n.stateless, + context.l10n.stateful, + context.l10n.single, + context.l10n.multi, + context.l10n.sliver, + context.l10n.proxy, + context.l10n.other, + ]; + } + + return [ + context.l10n.stateless, + context.l10n.stateful, + context.l10n.single, + context.l10n.multi, + context.l10n.sliver, + context.l10n.proxy, + context.l10n.other, + ]; + } + + List _buildTabWidgets() { + final provider = WidgetStatisticsProvider(); + final stats = provider.statistics; + final counts = [ + stats?.familyCount[WidgetFamily.stateless] ?? 0, + stats?.familyCount[WidgetFamily.stateful] ?? 0, + stats?.familyCount[WidgetFamily.singleChildRender] ?? 0, + stats?.familyCount[WidgetFamily.multiChildRender] ?? 0, + stats?.familyCount[WidgetFamily.sliver] ?? 0, + stats?.familyCount[WidgetFamily.proxy] ?? 0, + stats?.familyCount[WidgetFamily.other] ?? 0, + ]; + + return List.generate( + _tabs.length, + (index) => Stack( + clipBehavior: Clip.none, + children: [ + Text(_tabs[index]), + if (tabController.index == index) + Positioned( + right: -10, + top: -6, + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 6, vertical: 3), + decoration: BoxDecoration( + color: Theme.of(context) + .primaryColor + .withValues(alpha: 0.6), + borderRadius: BorderRadius.circular(6), + ), + child: Text('${counts[index]}', + style: const TextStyle( + fontFamily: '黑体', + height: 1, + fontSize: 9, + color: Colors.white)), + ), + ), + ], + )); + } + + @override + void initState() { + super.initState(); + tabController = TabController(length: 7, vsync: this); + } + + @override + Widget build(BuildContext context) { + Color themeColor = Theme.of(context).primaryColor; + bool isDark = Theme.of(context).brightness == Brightness.dark; + + return DragToMoveWrapper( + child: Container( + padding: const EdgeInsets.only(left: 20), + height: 64, + color: isDark ? Color(0xff2C3036) : Colors.white, + child: Row( + children: [ + SizedBox( + width: 380, + child: TabBar( + onTap: widget.onTabPressed, + tabAlignment: TabAlignment.start, + indicatorSize: TabBarIndicatorSize.label, + labelPadding: const EdgeInsets.symmetric(horizontal: 6), + isScrollable: true, + indicator: RoundRectTabIndicator( + borderSide: BorderSide(color: themeColor, width: 3), + ), + labelStyle: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + ), + controller: tabController, + labelColor: themeColor, + indicatorWeight: 3, + unselectedLabelColor: Colors.grey, + indicatorColor: themeColor, + tabs: _buildTabWidgets() + .map((Widget widget) => Tab(child: widget)) + .toList(), + ), + ), + Spacer(), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: SizedBox(width: 250, height: 30, child: DeskSearchBar()), + ), + // const SizedBox(width: 20,), + const WindowButtons(), + ], + ), + ), + ); + } +} diff --git a/modules/widget_system/widget_module/lib/views/desk_ui/widget_panel/widget_panel.dart b/modules/widget_system/widget_module/lib/views/desk_ui/widget_panel/widget_panel.dart new file mode 100644 index 000000000..85165ba15 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/desk_ui/widget_panel/widget_panel.dart @@ -0,0 +1,94 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:widget_module/blocs/blocs.dart'; +import 'package:widget_module/widget_module.dart'; +import 'package:widget_repository/widget_repository.dart'; +import 'desk_widget_top_bar.dart'; +import 'package:widget_ui/widget_ui.dart'; + +class DeskWidgetPanel extends StatefulWidget { + final Widget? header; + + const DeskWidgetPanel({super.key, this.header}); + + @override + State createState() => _DeskWidgetPanelState(); +} + +class _DeskWidgetPanelState extends State { + @override + Widget build(BuildContext context) { + WidgetsState state = context.watch().state; + + return Scaffold( + body: Column( + children: [ + DeskWidgetTopBar(onTabPressed: _switchTab), + const Divider(height: 1), + Expanded( + child: switch (state) { + WidgetsLoading() => const CupertinoActivityIndicator(), + WidgetsLoaded() => WidgetList( + state: state, + header: widget.header, + ), + WidgetsLoadFailed() => + Center(child: Text("${state.runtimeType}")), + }, + ), + ], + ), + ); + } + + void _switchTab(int index) { + WidgetFamily widgetFamily = WidgetFamily.values[index]; + context.switchWidgetFamily(widgetFamily); + } +} + +class WidgetList extends StatelessWidget { + final Widget? header; + + final WidgetsLoaded state; + + const WidgetList({super.key, required this.state, this.header}); + + @override + Widget build(BuildContext context) { + SliverGridDelegate gridDelegate = + const SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: 400, + mainAxisSpacing: 10, + mainAxisExtent: 110, + crossAxisSpacing: 10, + ); + return CustomScrollView( + slivers: [ + SliverToBoxAdapter( + child: Container( + padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), + margin: EdgeInsets.symmetric(horizontal: 14, vertical: 8), + decoration: BoxDecoration( + color: Theme.of(context).listTileTheme.tileColor, + borderRadius: BorderRadius.circular(8)), + height: 180, + child: header, + ), + ), + SliverPadding( + padding: const EdgeInsets.only(left: 14, right: 14, bottom: 8), + sliver: SliverGrid.builder( + gridDelegate: gridDelegate, + itemBuilder: (_, index) => WidgetItem( + model: state.widgets[index], + onWidget: context.handleWidgetAction, + ), + itemCount: state.widgets.length, + ), + ) + ], + ); + } +} diff --git a/lib/widget_ui/mobile/category_page/category_detail.dart b/modules/widget_system/widget_module/lib/views/mobile/category_page/category_detail.dart similarity index 89% rename from lib/widget_ui/mobile/category_page/category_detail.dart rename to modules/widget_system/widget_module/lib/views/mobile/category_page/category_detail.dart index 1042918a8..70fd7c1fe 100644 --- a/lib/widget_ui/mobile/category_page/category_detail.dart +++ b/modules/widget_system/widget_module/lib/views/mobile/category_page/category_detail.dart @@ -1,13 +1,16 @@ -import 'package:app/app.dart'; -import 'package:components/toly_ui/toly_ui.dart'; + +import 'package:toly_ui/toly_ui.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_star/flutter_star.dart'; +import 'package:go_router/go_router.dart'; -import 'package:widget_repository/widget_repository.dart'; import 'package:widget_module/blocs/blocs.dart'; +import 'package:widget_repository/widget_repository.dart'; + + /// create by 张风捷特烈 on 2020-04-22 /// contact me by email 1981462002@qq.com @@ -20,16 +23,14 @@ class CategoryShow extends StatelessWidget { @override Widget build(BuildContext context) { - + CategoryWidgetState state = context.watch().state; + Widget child = const SizedBox(); + if (state is CategoryWidgetLoadedState) { + child = _buildWidgetList(state.widgets); + } return Scaffold( appBar: AppBar(title: Text(model.name)), - body: BlocBuilder( - builder: (_, state) { - if (state is CategoryWidgetLoadedState) { - return _buildWidgetList(state.widgets); - } - return const SizedBox(); - }), + body: child, ); } @@ -68,8 +69,9 @@ class CategoryShow extends StatelessWidget { itemCount: widgets.length); } - _toDetailPage(BuildContext context, WidgetModel model) async { - Navigator.pushNamed(context, UnitRouter.widget_detail, arguments: model); + void _toDetailPage(BuildContext context, WidgetModel model) async { + // Navigator.pushNamed(context, UnitRouter.widget_detail, arguments: model); + context.push('/widget/detail/${model.name}',extra: model); } } diff --git a/lib/widget_ui/mobile/category_page/category_list_item.dart b/modules/widget_system/widget_module/lib/views/mobile/category_page/category_list_item.dart similarity index 97% rename from lib/widget_ui/mobile/category_page/category_list_item.dart rename to modules/widget_system/widget_module/lib/views/mobile/category_page/category_list_item.dart index 2b98b44f6..570900f4c 100644 --- a/lib/widget_ui/mobile/category_page/category_list_item.dart +++ b/modules/widget_system/widget_module/lib/views/mobile/category_page/category_list_item.dart @@ -1,9 +1,11 @@ -import 'package:components/toly_ui/toly_ui.dart'; +import 'package:toly_ui/toly_ui.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:widget_repository/widget_repository.dart'; + + /// create by 张风捷特烈 on 2020-04-21 /// contact me by email 1981462002@qq.com /// 说明: @@ -46,7 +48,7 @@ class CategoryListItem extends StatelessWidget { maxLines: 4, style: TextStyle( color: Colors.grey, - fontSize: 12, + fontSize: 14, shadows: [ Shadow(color: themeColor.withOpacity(0.4), offset:const Offset(.2,.2),blurRadius: .5) ])), diff --git a/lib/widget_ui/mobile/category_page/category_page.dart b/modules/widget_system/widget_module/lib/views/mobile/category_page/category_page.dart similarity index 77% rename from lib/widget_ui/mobile/category_page/category_page.dart rename to modules/widget_system/widget_module/lib/views/mobile/category_page/category_page.dart index d17476c83..05e953fc1 100644 --- a/lib/widget_ui/mobile/category_page/category_page.dart +++ b/modules/widget_system/widget_module/lib/views/mobile/category_page/category_page.dart @@ -1,38 +1,40 @@ import 'package:app/app.dart'; import 'package:components/project_ui/project_ui.dart'; -import 'package:components/toly_ui/toly_ui.dart'; +import 'package:toly_ui/toly_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/widget_ui/mobile/category_page/delete_category_dialog.dart'; +import 'package:go_router/go_router.dart'; import 'package:widget_module/blocs/blocs.dart'; import 'package:widget_repository/widget_repository.dart'; import 'category_list_item.dart'; +import 'delete_category_dialog.dart'; import 'edit_category_panel.dart'; import 'empty_category.dart'; class CategoryPage extends StatelessWidget { final SliverGridDelegateWithMaxCrossAxisExtent gridDelegate = const SliverGridDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: 200, - mainAxisExtent: 200, - mainAxisSpacing: 8, - crossAxisSpacing: 8, + maxCrossAxisExtent: 200, + mainAxisExtent: 200, + mainAxisSpacing: 8, + crossAxisSpacing: 8, // crossAxisCount: 2, // childAspectRatio: 0.8, ); - final SliverGridDelegateWithFixedCrossAxisCount deskGridDelegate = const SliverGridDelegateWithFixedCrossAxisCount( + final SliverGridDelegateWithFixedCrossAxisCount deskGridDelegate = + const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, mainAxisSpacing: 10, crossAxisSpacing: 10, childAspectRatio: 0.9, ); - + const CategoryPage({Key? key}) : super(key: key); @override @@ -57,14 +59,14 @@ class CategoryPage extends StatelessWidget { double bottom = MediaQuery.of(context).padding.bottom; return SliverPadding( - padding: EdgeInsets.only(top: 10, left: 10, right: 10, bottom: 20+bottom), - sliver: SliverLayoutBuilder( - builder: (_,c){ - SliverGridDelegate delegate = gridDelegate; - if(c.crossAxisExtent>500){ - delegate = deskGridDelegate; - } - return SliverGrid( + padding: + EdgeInsets.only(top: 10, left: 10, right: 10, bottom: 20 + bottom), + sliver: SliverLayoutBuilder(builder: (_, c) { + SliverGridDelegate delegate = gridDelegate; + if (c.crossAxisExtent > 500) { + delegate = deskGridDelegate; + } + return SliverGrid( delegate: SliverChildBuilderDelegate( (_, index) => GestureDetector( onTap: () => @@ -73,12 +75,11 @@ class CategoryPage extends StatelessWidget { data: state.categories[index], onDeleteItemClick: (model) => _deleteCollect(context, model), - onEditItemClick: (model) => - _editCollect(context, model), + onEditItemClick: (model) => _editCollect(context, model), )), childCount: state.categories.length), - gridDelegate: delegate); } - ), + gridDelegate: delegate); + }), ); } @@ -135,7 +136,10 @@ class CategoryPage extends StatelessWidget { ), Padding( padding: const EdgeInsets.all(8.0), - child: EditCategoryPanel(model: model,type: EditType.update,), + child: EditCategoryPanel( + model: model, + type: EditType.update, + ), ), ], ), @@ -143,9 +147,15 @@ class CategoryPage extends StatelessWidget { } void _toDetailPage(BuildContext context, CategoryModel model) { - BlocProvider.of(context).add(EventLoadCategoryWidget(model.id!)); - Navigator.pushNamed(context, UnitRouter.category_show, arguments: model); + Locale l = Localizations.localeOf(context); + String locale = '${l.languageCode}'; + if (l.countryCode == null) { + if (locale == 'en') { + locale += '-US'; + } + } + BlocProvider.of(context) + .add(EventLoadCategoryWidget(model.id!, locale.toLowerCase())); + context.push('/collection/widgets/${model.id}', extra: model); } - - } diff --git a/lib/widget_ui/mobile/category_page/collect_page.dart b/modules/widget_system/widget_module/lib/views/mobile/category_page/collect_page.dart similarity index 92% rename from lib/widget_ui/mobile/category_page/collect_page.dart rename to modules/widget_system/widget_module/lib/views/mobile/category_page/collect_page.dart index d63ff1735..36b875403 100644 --- a/lib/widget_ui/mobile/category_page/collect_page.dart +++ b/modules/widget_system/widget_module/lib/views/mobile/category_page/collect_page.dart @@ -2,8 +2,8 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:components/toly_ui/toly_ui.dart'; +import 'package:l10n/l10n.dart'; +import 'package:toly_ui/toly_ui.dart'; import 'package:authentication/views/authentic_widget.dart'; import '../../desk_ui/category_panel/desk_category_page.dart'; @@ -14,16 +14,15 @@ import 'sync/upload_button.dart'; class CollectPageAdapter extends StatelessWidget { - final bool canPop; - const CollectPageAdapter({Key? key, this.canPop=false}) : super(key: key); + const CollectPageAdapter({Key? key,}) : super(key: key); @override Widget build(BuildContext context) { bool isDesk = Platform.isMacOS||Platform.isWindows||Platform.isLinux; if(isDesk){ - return DeskCategoryPage(); + return const DeskCategoryPage(); }else{ - return CollectPage(canPop:canPop); + return const CollectPage(canPop:true); } } } @@ -40,9 +39,9 @@ class CollectPage extends StatefulWidget { class _CollectPageState extends State with AutomaticKeepAliveClientMixin { - final List _tabs = const [ - '收藏集录', - '珍藏组件', + late final List _tabs = [ + context.l10n.widgetsInn, + context.l10n.likedWidgets, ]; @override @@ -108,8 +107,8 @@ class _CollectPageState extends State if(!widget.canPop) SizedBox(child: _buildAddAction(context)) ], - title: const Text( - '收藏集录', + title: Text( + context.l10n.collectCollection, style: TextStyle( color: Colors.white, //标题 fontSize: 18, diff --git a/lib/widget_ui/mobile/category_page/delete_category_dialog.dart b/modules/widget_system/widget_module/lib/views/mobile/category_page/delete_category_dialog.dart similarity index 98% rename from lib/widget_ui/mobile/category_page/delete_category_dialog.dart rename to modules/widget_system/widget_module/lib/views/mobile/category_page/delete_category_dialog.dart index ab21223db..6c74675b9 100644 --- a/lib/widget_ui/mobile/category_page/delete_category_dialog.dart +++ b/modules/widget_system/widget_module/lib/views/mobile/category_page/delete_category_dialog.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:components/toly_ui/toly_ui.dart'; +import 'package:toly_ui/toly_ui.dart'; /// create by 张风捷特烈 on 2020-04-21 diff --git a/lib/widget_ui/mobile/category_page/edit_category_panel.dart b/modules/widget_system/widget_module/lib/views/mobile/category_page/edit_category_panel.dart similarity index 96% rename from lib/widget_ui/mobile/category_page/edit_category_panel.dart rename to modules/widget_system/widget_module/lib/views/mobile/category_page/edit_category_panel.dart index 9798bd34b..0ee7740e1 100644 --- a/lib/widget_ui/mobile/category_page/edit_category_panel.dart +++ b/modules/widget_system/widget_module/lib/views/mobile/category_page/edit_category_panel.dart @@ -1,12 +1,14 @@ import 'package:app/app.dart'; -import 'package:components/toly_ui/toly_ui.dart'; +import 'package:toly_ui/toly_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/app/utils/color_utils.dart'; +import 'package:utils/utils.dart'; import 'package:widget_module/blocs/blocs.dart'; - import 'package:widget_repository/widget_repository.dart'; + + + /// create by 张风捷特烈 on 2020-04-23 /// contact me by email 1981462002@qq.com /// 说明: diff --git a/lib/widget_ui/mobile/category_page/empty_category.dart b/modules/widget_system/widget_module/lib/views/mobile/category_page/empty_category.dart similarity index 97% rename from lib/widget_ui/mobile/category_page/empty_category.dart rename to modules/widget_system/widget_module/lib/views/mobile/category_page/empty_category.dart index 1be2de19a..6f27ac091 100644 --- a/lib/widget_ui/mobile/category_page/empty_category.dart +++ b/modules/widget_system/widget_module/lib/views/mobile/category_page/empty_category.dart @@ -5,7 +5,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:components/toly_ui/toly_ui.dart'; +import 'package:toly_ui/toly_ui.dart'; import 'package:widget_module/blocs/blocs.dart'; diff --git a/lib/widget_ui/mobile/category_page/home_right_drawer.dart b/modules/widget_system/widget_module/lib/views/mobile/category_page/home_right_drawer.dart similarity index 91% rename from lib/widget_ui/mobile/category_page/home_right_drawer.dart rename to modules/widget_system/widget_module/lib/views/mobile/category_page/home_right_drawer.dart index 4a99a312a..e0d999b91 100644 --- a/lib/widget_ui/mobile/category_page/home_right_drawer.dart +++ b/modules/widget_system/widget_module/lib/views/mobile/category_page/home_right_drawer.dart @@ -1,8 +1,9 @@ -import 'package:components/toly_ui/ti/circle.dart'; + import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/app/navigation/unit_drawer_header.dart'; +import 'package:toly_ui/toly_ui.dart'; + +import '../widget_page/unit_drawer_header.dart'; import 'edit_category_panel.dart'; class HomeRightDrawer extends StatefulWidget { diff --git a/modules/widget_system/widget_module/lib/views/mobile/category_page/like_widget_page.dart b/modules/widget_system/widget_module/lib/views/mobile/category_page/like_widget_page.dart new file mode 100644 index 000000000..0284a6d04 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/category_page/like_widget_page.dart @@ -0,0 +1,83 @@ +import 'package:app/app.dart'; +import 'package:components/project_ui/project_ui.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:widget_module/blocs/action/widget_action.dart'; +import 'package:widget_repository/widget_repository.dart'; +import 'package:widget_ui/widget_ui.dart'; +import 'package:widget_module/blocs/blocs.dart'; + +import '../widget_detail/collect_widget_list_item.dart'; +import '../widget_detail/widget_detail_page.dart'; + +/// create by 张风捷特烈 on 2020/6/16 +/// contact me by email 1981462002@qq.com +/// 说明: + +class LikeWidgetPage extends StatelessWidget { + const LikeWidgetPage({Key? key}) : super(key: key); + + final SliverGridDelegate gridDelegate = + const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + mainAxisSpacing: 8, + crossAxisSpacing: 8, + childAspectRatio: 1 / 0.5, + ); + + final SliverGridDelegate deskGridDelegate = + const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, + mainAxisSpacing: 10, + crossAxisSpacing: 10, + childAspectRatio: 1 / 0.5, + ); + + @override + Widget build(BuildContext context) { + return BlocBuilder>( + builder: (ctx, state) { + return CustomScrollView( + slivers: [ + SliverOverlapInjector( + handle: NestedScrollView.sliverOverlapAbsorberHandleFor(ctx), + ), + _buildContent(context, state), + const SliverToBoxAdapter( + child: NoMoreWidget(), + ) + ], + ); + }); + } + + Widget _buildContent(BuildContext context, List state) { + return SliverPadding( + padding: const EdgeInsets.only(top: 10, left: 10, right: 10, bottom: 0), + sliver: SliverLayoutBuilder( + builder: (_, c) { + SliverGridDelegate delegate = gridDelegate; + if (c.crossAxisExtent > 500) { + delegate = deskGridDelegate; + } + return SliverGrid( + delegate: SliverChildBuilderDelegate( + (_, index) => GestureDetector( + onTap: () => _toDetailPage(context, state[index]), + child: CollectWidgetListItem( + data: state[index], + onDeleteItemClick: (model) => + context.toggleLike(model.id), + )), + childCount: state.length), + gridDelegate: delegate); + }, + )); + } + + _toDetailPage(BuildContext context, WidgetModel model) { + Navigator.push( + context, SlidePageRoute(child: WidgetDetailPageScope(model: model))); + } +} diff --git a/lib/widget_ui/mobile/category_page/sync/async_button.dart b/modules/widget_system/widget_module/lib/views/mobile/category_page/sync/async_button.dart similarity index 91% rename from lib/widget_ui/mobile/category_page/sync/async_button.dart rename to modules/widget_system/widget_module/lib/views/mobile/category_page/sync/async_button.dart index d933c488b..7acd72988 100644 --- a/lib/widget_ui/mobile/category_page/sync/async_button.dart +++ b/modules/widget_system/widget_module/lib/views/mobile/category_page/sync/async_button.dart @@ -5,13 +5,16 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/point_system/api/category_api.dart'; +import 'category_api.dart'; import 'package:utils/utils.dart'; import 'package:widget_module/blocs/blocs.dart'; import 'package:storage/storage.dart'; import 'package:widget_repository/widget_repository.dart'; -import 'package:components/toly_ui/toly_ui.dart'; +import 'package:widget_ui/widget_ui.dart'; + + +import 'package:toly_ui/toly_ui.dart'; /// create by 张风捷特烈 on 2021/2/24 @@ -82,13 +85,13 @@ class _SyncCategoryButtonState extends State { CategoryRepository repository = BlocProvider.of(context).repository; await repository.syncCategoryByData(result.data!.data,result.data!.likeData); BlocProvider.of(context).add(const EventLoadCategory()); - BlocProvider.of(context).add(const EventLoadLikeData()); + context.read().loadLikeData(); } else { // 说明还没有后台数据, // 这里防止有傻孩子没点备份,就点同步,哥哥好心,给备份一下。 CategoryRepository rep = BlocProvider.of(context).repository; List loadCategories = await rep.loadCategoryData(); - List likeData = await FlutterDbStorage.instance.likeDao.likeWidgetIds(); + List likeData = await AppStorage().flutter().likeWidgetIds(); String json = jsonEncode(loadCategories); String likeJson = jsonEncode(likeData); diff --git a/modules/widget_system/widget_module/lib/views/mobile/category_page/sync/category_api.dart b/modules/widget_system/widget_module/lib/views/mobile/category_page/sync/category_api.dart new file mode 100644 index 000000000..df6be8fb0 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/category_page/sync/category_api.dart @@ -0,0 +1,73 @@ +import 'package:app/app.dart'; +import 'package:utils/utils.dart'; + + +/// create by 张风捷特烈 on 2021/2/24 +/// contact me by email 1981462002@qq.com +/// 说明: + +class CategoryApi { + static Future> uploadCategoryData( + {required String data, required String likeData}) async { + String errorMsg = ""; + + try { + var result = await HttpUtil.instance.client.post( + PathUnit.categoryDataSync, + data: {"data": data, "likeData": likeData}); + print(result.data); + if (result.data != null) { + return TaskResult.success(data:result.data['status']); + } + } catch (e) { + print(e); + errorMsg = e.toString(); + } + + return TaskResult.error(msg: '请求错误: $errorMsg'); + } + + static Future> getCategoryData() async { + String errorMsg = ""; + var result = await HttpUtil.instance + .client + .get(PathUnit.categoryData) + .catchError((err) { + errorMsg =err.toString(); + + }); + + // 获取的数据非空且 status = true + if (result.data != null && result.data['status']) { + // 说明有数据 + if (result.data['data'] != null) { + return TaskResult.success(data:CategoryData.fromJson(result.data['data'])); + } else { + return const TaskResult.success(data:null); + } + } + + return TaskResult.error(msg: '请求错误: $errorMsg'); + } +} + +class CategoryData{ + final int categoryDataId; + final int userId; + final String data; + final String likeData; + + CategoryData( + {required this.categoryDataId, + required this.userId, + required this.data, + required this.likeData}); + + factory CategoryData.fromJson(Map map) { + return CategoryData( + categoryDataId: map['categoryDataId'], + userId: map["userId"], + likeData: map["likeData"], + data: map["data"]); + } +} \ No newline at end of file diff --git a/lib/widget_ui/mobile/category_page/sync/upload_button.dart b/modules/widget_system/widget_module/lib/views/mobile/category_page/sync/upload_button.dart similarity index 87% rename from lib/widget_ui/mobile/category_page/sync/upload_button.dart rename to modules/widget_system/widget_module/lib/views/mobile/category_page/sync/upload_button.dart index c9a37b309..06242ece4 100644 --- a/lib/widget_ui/mobile/category_page/sync/upload_button.dart +++ b/modules/widget_system/widget_module/lib/views/mobile/category_page/sync/upload_button.dart @@ -6,13 +6,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:storage/storage.dart'; -import 'package:flutter_unit/point_system/api/category_api.dart'; +import 'category_api.dart'; import 'package:utils/utils.dart'; import 'package:widget_module/blocs/blocs.dart'; -import 'package:components/toly_ui/toly_ui.dart'; +import 'package:toly_ui/toly_ui.dart'; import 'package:widget_repository/widget_repository.dart'; - /// create by 张风捷特烈 on 2021/2/24 /// contact me by email 1981462002@qq.com /// 说明: @@ -70,18 +69,18 @@ class _UploadCategoryButtonState extends State { onPressed: _doUploadCategoryData); } - void _doUploadCategoryData() async{ + void _doUploadCategoryData() async { setState(() => state = AsyncType.loading); CategoryRepository rep = BlocProvider.of(context).repository; List loadCategories = await rep.loadCategoryData(); - List likeData = await FlutterDbStorage.instance.likeDao.likeWidgetIds(); - + List likeData = await AppStorage().flutter().likeWidgetIds(); String json = jsonEncode(loadCategories); String likeJson = jsonEncode(likeData); - TaskResult result = await CategoryApi.uploadCategoryData(data: json,likeData: likeJson); + TaskResult result = + await CategoryApi.uploadCategoryData(data: json, likeData: likeJson); if (result.success) { setState(() => state = AsyncType.success); diff --git a/modules/widget_system/widget_module/lib/views/mobile/mobile_ui.dart b/modules/widget_system/widget_module/lib/views/mobile/mobile_ui.dart new file mode 100644 index 000000000..5996b986e --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/mobile_ui.dart @@ -0,0 +1,8 @@ +export 'widget_detail/widget_detail_page.dart'; +export 'widget_detail/node_display/node_title.dart'; +export 'widget_page/standard_home_page.dart'; +export 'widget_page/widget_page.dart'; +export 'category_page/category_page.dart'; +export 'category_page/collect_page.dart'; +export 'category_page/category_detail.dart'; +export 'category_page/home_right_drawer.dart'; diff --git a/lib/widget_ui/mobile/search_page/app_search_bar.dart b/modules/widget_system/widget_module/lib/views/mobile/search_page/app_search_bar.dart similarity index 99% rename from lib/widget_ui/mobile/search_page/app_search_bar.dart rename to modules/widget_system/widget_module/lib/views/mobile/search_page/app_search_bar.dart index 3011eaf60..1de3cf280 100644 --- a/lib/widget_ui/mobile/search_page/app_search_bar.dart +++ b/modules/widget_system/widget_module/lib/views/mobile/search_page/app_search_bar.dart @@ -3,9 +3,10 @@ import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:widget_repository/widget_repository.dart'; import 'package:widget_module/blocs/blocs.dart'; -import 'package:widget_repository/widget_repository.dart'; + class AppSearchBar extends StatefulWidget { const AppSearchBar({Key? key}):super(key: key); diff --git a/modules/widget_system/widget_module/lib/views/mobile/search_page/standard_search_bar.dart b/modules/widget_system/widget_module/lib/views/mobile/search_page/standard_search_bar.dart new file mode 100644 index 000000000..0db7d89ab --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/search_page/standard_search_bar.dart @@ -0,0 +1,101 @@ +import 'package:app/app.dart'; +import 'package:l10n/l10n.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:widget_repository/widget_repository.dart'; +import 'package:widget_module/blocs/blocs.dart'; + +class StandardSearchBarInner extends StatelessWidget + implements PreferredSizeWidget { + const StandardSearchBarInner({Key? key}) : super(key: key); + + @override + Size get preferredSize => const Size.fromHeight(35 + 8 * 2); + + @override + Widget build(BuildContext context) { + bool isDark = Theme.of(context).brightness == Brightness.dark; + Color? color = Theme.of(context).appBarTheme.backgroundColor; + return Container( + color: isDark ? color : Colors.white, + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Row( + children: [ + const SizedBox( + width: 15, + ), + GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + Navigator.of(context).maybePop(); + }, + child: const SizedBox( + height: 32, width: 32, child: Icon(Icons.arrow_back)), + ), + Expanded( + child: Container( + height: 35, + padding: const EdgeInsets.only(left: 10, right: 10), + child: Material( + color: Colors.transparent, + child: TextField( + autofocus: true, + enabled: true, + cursorColor: Colors.blue, + maxLines: 1, + onChanged: (str) => _doSearch(context, str), + onSubmitted: (str) { + //提交后,收起键盘 + FocusScope.of(context).requestFocus(FocusNode()); + }, + decoration: InputDecoration( + filled: true, + fillColor: + isDark ? Color(0xff292929) : Color(0xffF3F6F9), + prefixIcon: Icon( + Icons.search, + color: Colors.grey, + size: 20, + ), + + prefixIconConstraints: BoxConstraints( + maxHeight: 24, + minWidth: 36 + ), + isCollapsed: true, + contentPadding: EdgeInsets.only(top: 4,bottom: 4,right: 8), + border: UnderlineInputBorder( + borderSide: BorderSide.none, + borderRadius: + BorderRadius.all(Radius.circular(8)), + ), + hintText:context.l10n.searchWidget, + hintStyle: TextStyle(fontSize: 14)), + ), + )), + ), + Wrap( + spacing: 3, + alignment: WrapAlignment.center, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + const Icon(TolyIcon.icon_sound), + // Text('已签',style: TextStyle(color: Colors.grey),) + ], + ), + const SizedBox(width: 15) + ], + ), + ); + } + + void _doSearch(BuildContext context, String str) { + WidgetsBloc widgetsBloc = BlocProvider.of(context); + final WidgetFilter filter = widgetsBloc.state.filter.copyWith( + name: str, + ); + widgetsBloc.add( + EventSearchWidget(filter: filter), + ); + } +} diff --git a/modules/widget_system/widget_module/lib/views/mobile/search_page/standard_search_page.dart b/modules/widget_system/widget_module/lib/views/mobile/search_page/standard_search_page.dart new file mode 100644 index 000000000..335659242 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/search_page/standard_search_page.dart @@ -0,0 +1,95 @@ +import 'package:app/app.dart'; +import 'package:components/components.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; + +import 'package:widget_module/blocs/blocs.dart'; +import 'package:l10n/l10n.dart'; +import 'package:widget_module/widget_module.dart'; + +import 'package:widget_repository/widget_repository.dart'; +import 'package:widget_ui/widget_ui.dart'; +import 'standard_search_bar.dart'; + +// SearchPage 可以复用 WidgetsBloc,进行局部的 Bloc +// 不必单独提供 SearchBloc 增加复杂性 +class StandardSearchPageProvider extends StatelessWidget { + const StandardSearchPageProvider({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return BlocProvider( + lazy: false, + create: (BuildContext context) => WidgetsBloc( + repository: BlocProvider.of(context).repository, + ), + child: const StandardSearchPage(), + ); + } +} + +class StandardSearchPage extends StatelessWidget { + const StandardSearchPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + bool isDark = Theme.of(context).brightness == Brightness.dark; + Color color = isDark + ? Theme.of(context).appBarTheme.backgroundColor ?? Colors.black + : Colors.white; + return Scaffold( + body: Column( + children: [ + Container( + color: color, + height: MediaQuery.of(context).padding.top, + width: MediaQuery.of(context).size.width, + ), + const StandardSearchBarInner(), + Expanded( + child: BlocBuilder( + builder: _buildBodyByState, + )) + ], + ), + ); + } + + Widget _buildBodyByState(BuildContext context, WidgetsState state) { + Widget noSearchArg = NotSearchPage( + tips: context.l10n.searchSomething, + ); + if (state.filter.name.isEmpty) { + return noSearchArg; + } + + if (state is WidgetsLoaded) { + if (state.widgets.isEmpty) { + return EmptyShower(message: context.l10n.emptySearch); + } + return ListView.builder( + padding: EdgeInsets.zero, + itemBuilder: (_, index) => Padding( + padding: const EdgeInsets.only(left: 8.0, right: 8, top: 8), + child: WidgetItem( + searchArgs: state.filter.name, + model: state.widgets[index], + onWidget: context.handleWidgetAction, + ), + ), + itemCount: state.widgets.length, + ); + } + + if (state is WidgetsLoading) { + return const LoadingShower(); + } + + if (state is WidgetsLoadFailed) { + return const ErrorPage(); + } + + return noSearchArg; + } +} diff --git a/lib/widget_ui/mobile/widget_detail/category_end_drawer.dart b/modules/widget_system/widget_module/lib/views/mobile/widget_detail/category_end_drawer.dart similarity index 83% rename from lib/widget_ui/mobile/widget_detail/category_end_drawer.dart rename to modules/widget_system/widget_module/lib/views/mobile/widget_detail/category_end_drawer.dart index 1cb451810..1e8ae41a5 100644 --- a/lib/widget_ui/mobile/widget_detail/category_end_drawer.dart +++ b/modules/widget_system/widget_module/lib/views/mobile/widget_detail/category_end_drawer.dart @@ -1,12 +1,13 @@ import 'package:app/app.dart'; -import 'package:components/toly_ui/toly_ui.dart'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/app/navigation/unit_drawer_header.dart'; +import 'package:toly_ui/toly_ui.dart'; + import 'package:widget_module/blocs/blocs.dart'; import 'package:widget_repository/widget_repository.dart'; - +import '../widget_page/unit_drawer_header.dart'; /// create by 张风捷特烈 on 2020-04-22 /// contact me by email 1981462002@qq.com @@ -23,24 +24,24 @@ class CategoryEndDrawer extends StatelessWidget { child: ListView(padding: EdgeInsets.zero, children: [ UnitDrawerHeader(color: Theme.of(context).primaryColor), Padding( - padding: const EdgeInsets.all(10.0), - child: Row( - children: [ - Circle( - color: widget.color, + padding: const EdgeInsets.all(10.0), + child: Row( + children: [ + Circle( + color: widget.color, + ), + const SizedBox( + width: 10, + ), + Text(widget.name) + ], ), - const SizedBox( - width: 10, - ), - Text(widget.name) - ], - ), ), Padding( - padding: const EdgeInsets.symmetric(horizontal: 15.0), - child: Panel( - child: Text(widget.info, style: UnitTextStyle.shadowTextStyle), - ), + padding: const EdgeInsets.symmetric(horizontal: 15.0), + child: Panel( + child: Text(widget.info, style: UnitTextStyle.shadowTextStyle), + ), ), const Divider(), _buildTitle(context), @@ -130,7 +131,10 @@ class _CategoryInfoState extends State { onSelected: (v) async { await repository.toggleCategory(category.id!, widget.id); _loadCategoryIds(); - BlocProvider.of(context).add(EventLoadCategoryWidget(category.id!)); + String locale = context.read().language.code; + + BlocProvider.of(context) + .add(EventLoadCategoryWidget(category.id!, locale)); }); } diff --git a/lib/widget_ui/mobile/widget_detail/collect_widget_list_item.dart b/modules/widget_system/widget_module/lib/views/mobile/widget_detail/collect_widget_list_item.dart similarity index 98% rename from lib/widget_ui/mobile/widget_detail/collect_widget_list_item.dart rename to modules/widget_system/widget_module/lib/views/mobile/widget_detail/collect_widget_list_item.dart index e01600570..75e0bff0e 100644 --- a/lib/widget_ui/mobile/widget_detail/collect_widget_list_item.dart +++ b/modules/widget_system/widget_module/lib/views/mobile/widget_detail/collect_widget_list_item.dart @@ -2,9 +2,10 @@ import 'package:app/app.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_star/flutter_star.dart'; -import 'package:components/toly_ui/toly_ui.dart'; +import 'package:toly_ui/toly_ui.dart'; import 'package:widget_repository/widget_repository.dart'; + class CollectWidgetListItem extends StatelessWidget { final WidgetModel data; final Function(WidgetModel model)? onDeleteItemClick; diff --git a/modules/widget_system/widget_module/lib/views/mobile/widget_detail/node_display/code_display.dart b/modules/widget_system/widget_module/lib/views/mobile/widget_detail/node_display/code_display.dart new file mode 100644 index 000000000..d72b69ed8 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/widget_detail/node_display/code_display.dart @@ -0,0 +1,187 @@ +import 'dart:math'; + +import 'package:app/app.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:share_plus/share_plus.dart'; +import 'package:toly_ui/code/code.dart'; +import 'package:toly_ui/toly_ui.dart'; +import 'package:tolyui/tolyui.dart' hide TolyCollapse,CollapseController; + +// import 'package:flutter_highlight/flutter_highlight.dart'; +import 'collapse.dart'; + +class CodeDisplay extends StatefulWidget { + final Widget display; + final HighlighterStyle style; + final String code; + + const CodeDisplay({ + super.key, + required this.display, + required this.code, + required this.style, + }); + + @override + State createState() => _CodeDisplayState(); +} + +class _CodeDisplayState extends State { + String? codeRes; + + void _loadAssets() async { + codeRes = await codeData(); + setState(() {}); + } + + Future codeData() async { + if (widget.code.startsWith('assets')) { + return await rootBundle.loadString(widget.code); + } else { + return widget.code; + } + } + + @override + Widget build(BuildContext context) { + return Container( + width: double.maxFinite, + decoration: BoxDecoration( + color: Theme.of(context).listTileTheme.tileColor, + border: Border.all( + color: Theme.of(context).dividerTheme.color ?? Colors.grey, + width: Theme.of(context).dividerTheme.space ?? 1, + ), + boxShadow: [ + BoxShadow(color: Colors.grey.withOpacity(0.05),spreadRadius: 1,blurRadius: 4) + ], + borderRadius: BorderRadius.circular(4)), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 12), + child: widget.display, + ), + const Divider(), + TolyCollapse( + titleBuilder: _buildTitle, + sizeCurve: Curves.ease, + content: CodeWidget( + code: widget.code, + style: widget.style, + ), + duration: const Duration(milliseconds: 500), + ) + ], + ), + ); + } + _doShare() { + Share.share(widget.code); + } + Widget _buildTitle(BuildContext context, Animation anima, CollapseController ctrl) { + Color color = Theme.of(context).primaryColor; + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8.0), + child: Row( + children: [ + const Spacer(), + FeedbackWidget( + mode: FeedMode.fade, + a: 0.4, + onPressed: _doShare, + child: Padding( + padding: const EdgeInsets.only( + right: 10, + ), + child: Icon( + TolyIcon.icon_share, + size: 20, + color: color, + ), + ), + ), + FeedbackWidget( + mode: FeedMode.fade, + a: 0.4, + onPressed: _copyCode, + child: Padding( + padding: const EdgeInsets.only( + right: 10, + ), + child: Icon( + Icons.copy_rounded, + size: 20, + color: color, + ), + ), + ), + GestureDetector( + onTap: () => _toggleCode(ctrl), + child: AnimatedBuilder( + animation: anima, + builder: (_, child) { + return Transform.rotate( + angle: pi / 2 * Curves.ease.transform(anima.value), + child: Icon( + TolyIcon.icon_code, + color: color, + ), + ); + }, + )), + ], + )); + } + + + void _toggleCode(CollapseController ctrl) { + if (!ctrl.isOpen) { + _loadAssets(); + } else { + codeRes = null; + } + ctrl.toggle(); + } + + void _copyCode() async { + String code = await codeData(); + await Clipboard.setData(ClipboardData(text: code)); + $message.success(message: '代码复制成功!'); + } +} + +const githubTheme = { + 'root': TextStyle(color: Color(0xff333333), backgroundColor: Colors.transparent), + 'comment': TextStyle(color: Color(0xff999988), fontStyle: FontStyle.italic), + 'quote': TextStyle(color: Color(0xff999988), fontStyle: FontStyle.italic), + 'keyword': TextStyle(color: Color(0xff333333), fontWeight: FontWeight.bold), + 'selector-tag': TextStyle(color: Color(0xff333333), fontWeight: FontWeight.bold), + 'subst': TextStyle(color: Color(0xff333333), fontWeight: FontWeight.normal), + 'number': TextStyle(color: Color(0xff008080)), + 'literal': TextStyle(color: Color(0xff008080)), + 'variable': TextStyle(color: Color(0xff008080)), + 'template-variable': TextStyle(color: Color(0xff008080)), + 'string': TextStyle(color: Color(0xffdd1144)), + 'doctag': TextStyle(color: Color(0xffdd1144)), + 'title': TextStyle(color: Color(0xff990000), fontWeight: FontWeight.bold), + 'section': TextStyle(color: Color(0xff990000), fontWeight: FontWeight.bold), + 'selector-id': TextStyle(color: Color(0xff990000), fontWeight: FontWeight.bold), + 'type': TextStyle(color: Color(0xff445588), fontWeight: FontWeight.bold), + 'tag': TextStyle(color: Color(0xff000080), fontWeight: FontWeight.normal), + 'name': TextStyle(color: Color(0xff000080), fontWeight: FontWeight.normal), + 'attribute': TextStyle(color: Color(0xff000080), fontWeight: FontWeight.normal), + 'regexp': TextStyle(color: Color(0xff009926)), + 'link': TextStyle(color: Color(0xff009926)), + 'symbol': TextStyle(color: Color(0xff990073)), + 'bullet': TextStyle(color: Color(0xff990073)), + 'built_in': TextStyle(color: Color(0xff0086b3)), + 'builtin-name': TextStyle(color: Color(0xff0086b3)), + 'meta': TextStyle(color: Color(0xff999999), fontWeight: FontWeight.bold), + 'deletion': TextStyle(backgroundColor: Color(0xffffdddd)), + 'addition': TextStyle(backgroundColor: Color(0xffddffdd)), + 'emphasis': TextStyle(fontStyle: FontStyle.italic), + 'strong': TextStyle(fontWeight: FontWeight.bold), +}; diff --git a/modules/widget_system/widget_module/lib/views/mobile/widget_detail/node_display/collapse.dart b/modules/widget_system/widget_module/lib/views/mobile/widget_detail/node_display/collapse.dart new file mode 100644 index 000000000..963b1f5b9 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/widget_detail/node_display/collapse.dart @@ -0,0 +1,436 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:math'; + +import 'package:flutter/material.dart' hide CrossFadeState; +import 'package:flutter/rendering.dart'; + +typedef AnimatedTitleBuilder = Widget Function( + BuildContext context, + Animation anima, + CollapseController ctrl, +); + +/// A widget that cross-fades between two given children and animates itself +/// between their sizes. +/// +/// {@youtube 560 315 https://www.youtube.com/watch?v=PGK2UUAyE54} +/// +/// The animation is controlled through the [crossFadeState] parameter. +/// [firstCurve] and [opacityCurve] represent the opacity curves of the two +/// children. The [firstCurve] is inverted, i.e. it fades out when providing a +/// growing curve like [Curves.linear]. The [sizeCurve] is the curve used to +/// animate between the size of the fading-out child and the size of the +/// fading-in child. +/// +/// This widget is intended to be used to fade a pair of widgets with the same +/// width. In the case where the two children have different heights, the +/// animation crops overflowing children during the animation by aligning their +/// top edge, which means that the bottom will be clipped. +/// +/// The animation is automatically triggered when an existing +/// [TolyCollapse] is rebuilt with a different value for the +/// [crossFadeState] property. +/// +/// {@tool snippet} +/// +/// This code fades between two representations of the Flutter logo. It depends +/// on a boolean field `_first`; when `_first` is true, the first logo is shown, +/// otherwise the second logo is shown. When the field changes state, the +/// [TolyCollapse] widget cross-fades between the two forms of the logo +/// over three seconds. +/// +/// ```dart +/// AnimatedCrossFade( +/// duration: const Duration(seconds: 3), +/// firstChild: const FlutterLogo(style: FlutterLogoStyle.horizontal, size: 100.0), +/// secondChild: const FlutterLogo(style: FlutterLogoStyle.stacked, size: 100.0), +/// crossFadeState: _first ? CrossFadeState.showFirst : CrossFadeState.showSecond, +/// ) +/// ``` +/// {@end-tool} +/// +/// See also: +/// +/// * [AnimatedOpacity], which fades between nothing and a single child. +/// * [AnimatedSwitcher], which switches out a child for a new one with a +/// customizable transition, supporting multiple cross-fades at once. +/// * [AnimatedSize], the lower-level widget which [TolyCollapse] uses to +/// automatically change size. + +class TolyCollapse extends StatefulWidget { + /// Creates a cross-fade animation widget. + /// + /// The [duration] of the animation is the same for all components (fade in, + /// fade out, and size), and you can pass [Interval]s instead of [Curve]s in + /// order to have finer control, e.g., creating an overlap between the fades. + const TolyCollapse({ + super.key, + required this.content, + this.title, + this.opacityCurve = Curves.linear, + this.sizeCurve = Curves.linear, + this.alignment = Alignment.topCenter, + this.titlePadding = const EdgeInsets.symmetric(vertical: 12.0), + this.contentPadding = + const EdgeInsets.only(top: 0, right: 8, left: 8, bottom: 8), + required this.duration, + this.reverseDuration, + this.titleBuilder, + this.controller, + this.onOpen, + this.onClose, + this.excludeBottomFocus = true, + }) : assert(title == null && titleBuilder != null || + titleBuilder == null && title != null || + titleBuilder != null && title != null); + + final Widget content; + final Widget? title; + final VoidCallback? onOpen; + final VoidCallback? onClose; + final AnimatedTitleBuilder? titleBuilder; + final EdgeInsetsGeometry titlePadding; + final EdgeInsetsGeometry contentPadding; + final CollapseController? controller; + + /// The duration of the whole orchestrated animation. + final Duration duration; + + /// The duration of the whole orchestrated animation when running in reverse. + /// + /// If not supplied, this defaults to [duration]. + final Duration? reverseDuration; + + /// The fade curve of the second child. + /// + /// Defaults to [Curves.linear]. + final Curve opacityCurve; + + /// The curve of the animation between the two children's sizes. + /// + /// Defaults to [Curves.linear]. + final Curve sizeCurve; + + /// How the children should be aligned while the size is animating. + /// + /// Defaults to [Alignment.topCenter]. + /// + /// See also: + /// + /// * [Alignment], a class with convenient constants typically used to + /// specify an [AlignmentGeometry]. + /// * [AlignmentDirectional], like [Alignment] for specifying alignments + /// relative to text direction. + final AlignmentGeometry alignment; + + /// When true, this is equivalent to wrapping the bottom widget with an [ExcludeFocus] + /// widget while it is at the bottom of the cross-fade stack. + /// + /// Defaults to true. When it is false, the bottom widget in the cross-fade stack + /// can remain in focus until the top widget requests focus. This is useful for + /// animating between different [TextField]s so the keyboard remains open during the + /// cross-fade animation. + final bool excludeBottomFocus; + + @override + State createState() => _TolyCollapseState(); + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties.add(DiagnosticsProperty( + 'alignment', alignment, + defaultValue: Alignment.topCenter)); + properties + .add(IntProperty('duration', duration.inMilliseconds, unit: 'ms')); + properties.add(IntProperty( + 'reverseDuration', reverseDuration?.inMilliseconds, + unit: 'ms', defaultValue: null)); + } +} + +class _TolyCollapseState extends State + with TickerProviderStateMixin { + late AnimationController _controller; + late Animation _firstAnimation; + late Animation _secondAnimation; + + CollapseController? _internalController; + + CollapseController get _collapseCtrl => + widget.controller ?? _internalController!; + + @override + void initState() { + super.initState(); + if (widget.controller == null) { + _internalController = CollapseController(); + } + _collapseCtrl._attach(this); + _controller = AnimationController( + duration: widget.duration, + reverseDuration: widget.reverseDuration, + vsync: this, + ); + // if (widget.crossFadeState == CrossFadeState.showSecond) { + // _controller.value = 1.0; + // } + _firstAnimation = _initAnimation(Curves.linear, true); + _secondAnimation = _initAnimation(widget.opacityCurve, false); + _controller.addStatusListener((AnimationStatus status) { + setState(() { + // Trigger a rebuild because it depends on _isTransitioning, which + // changes its value together with animation status. + }); + }); + } + + Animation _initAnimation(Curve curve, bool inverted) { + Animation result = _controller.drive(CurveTween(curve: curve)); + if (inverted) { + result = result.drive(Tween(begin: 1.0, end: 0.0)); + } + return result; + } + + @override + void dispose() { + _controller.dispose(); + _collapseCtrl._detach(this); + super.dispose(); + } + + @override + void didUpdateWidget(TolyCollapse oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.duration != oldWidget.duration) { + _controller.duration = widget.duration; + } + if (widget.reverseDuration != oldWidget.reverseDuration) { + _controller.reverseDuration = widget.reverseDuration; + } + if (widget.opacityCurve != oldWidget.opacityCurve) { + _secondAnimation = _initAnimation(widget.opacityCurve, false); + } + } + + /// Whether we're in the middle of cross-fading this frame. + bool get _isTransitioning => + _controller.status == AnimationStatus.forward || + _controller.status == AnimationStatus.reverse; + + bool get _isOpen => _controller.value == 1.0; + + @override + Widget build(BuildContext context) { + const Key closeKey = ValueKey(false); + const Key openKey = ValueKey(true); + final bool transitioningForwards = + _controller.status == AnimationStatus.completed || + _controller.status == AnimationStatus.forward; + final Key topKey; + Widget topChild; + final Animation topAnimation; + final Key bottomKey; + Widget bottomChild; + final Animation bottomAnimation; + if (transitioningForwards) { + topKey = openKey; + topChild = Align( + alignment: Alignment.topLeft, + child: Padding(padding: widget.contentPadding, child: widget.content), + ); + topAnimation = _secondAnimation; + bottomKey = closeKey; + bottomChild = Container( + height: 0, + ); + bottomAnimation = _firstAnimation; + } else { + topKey = closeKey; + topChild = Container( + height: 0, + ); + topAnimation = _firstAnimation; + bottomKey = openKey; + bottomChild = Align( + alignment: Alignment.topLeft, + child: Padding( + padding: widget.contentPadding, + child: widget.content, + ), + ); + bottomAnimation = _secondAnimation; + } + + bottomChild = TickerMode( + key: bottomKey, + enabled: _isTransitioning, + child: IgnorePointer( + child: ExcludeSemantics( + // Always exclude the semantics of the widget that's fading out. + child: ExcludeFocus( + excluding: widget.excludeBottomFocus, + child: FadeTransition( + opacity: bottomAnimation, + child: bottomChild, + ), + ), + ), + ), + ); + topChild = TickerMode( + key: topKey, + enabled: true, // Top widget always has its animations enabled. + child: IgnorePointer( + ignoring: false, + child: ExcludeSemantics( + excluding: false, + // Always publish semantics for the widget that's fading in. + child: ExcludeFocus( + excluding: false, + child: FadeTransition( + opacity: topAnimation, + child: topChild, + ), + ), + ), + ), + ); + Widget title; + if (widget.titleBuilder != null) { + title = widget.titleBuilder!(context, _controller, _collapseCtrl); + } else { + title = GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + _toggleCodePanel(); + }, + child: Row( + children: [ + Expanded( + child: Padding( + padding: widget.titlePadding, + child: widget.title, + )), + Spacer(), + AnimatedBuilder( + animation: _controller, + builder: (_, child) => Transform.rotate( + angle: pi * _controller.value, + child: child, + ), + child: const Icon(Icons.expand_more)) + ], + )); + } + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + title, + ClipRect( + child: AnimatedSize( + alignment: widget.alignment, + duration: widget.duration, + reverseDuration: widget.reverseDuration, + curve: widget.sizeCurve, + child: + defaultLayoutBuilder(topChild, topKey, bottomChild, bottomKey), + ), + ), + ], + ); + } + + Widget defaultLayoutBuilder(Widget topChild, Key topChildKey, + Widget bottomChild, Key bottomChildKey) { + return Stack( + clipBehavior: Clip.none, + children: [ + Positioned( + key: bottomChildKey, + left: 0.0, + top: 0.0, + right: 0.0, + child: bottomChild, + ), + Positioned( + key: topChildKey, + child: topChild, + ), + ], + ); + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder description) { + super.debugFillProperties(description); + // description.add(EnumProperty('crossFadeState', widget.crossFadeState)); + description.add(DiagnosticsProperty( + 'controller', _controller, + showName: false)); + description.add(DiagnosticsProperty( + 'alignment', widget.alignment, + defaultValue: Alignment.topCenter)); + } + + void _close() { + widget.onClose?.call(); + _controller.reverse(); + } + + void _open() { + widget.onOpen?.call(); + _controller.forward(); + } + + // 折叠代码面板 + void _toggleCodePanel() { + if (_isOpen) { + _close(); + } else { + _open(); + } + } +} + +class CollapseController { + _TolyCollapseState? _state; + + bool get isOpen { + assert(_state != null); + return _state!._isOpen; + } + + void toggle() { + if (isOpen) { + close(); + } else { + open(); + } + } + + void close() { + assert(_state != null); + _state!._close(); + } + + void open() { + assert(_state != null); + _state!._open(); + } + + void _attach(_TolyCollapseState state) { + _state = state; + } + + void _detach(_TolyCollapseState state) { + if (_state == state) { + _state = null; + } + } +} diff --git a/modules/widget_system/widget_module/lib/views/mobile/widget_detail/node_display/node_display.dart b/modules/widget_system/widget_module/lib/views/mobile/widget_detail/node_display/node_display.dart new file mode 100644 index 000000000..e70bdf11f --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/widget_detail/node_display/node_display.dart @@ -0,0 +1,55 @@ +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:toly_ui/toly_ui.dart'; +import 'package:app/app.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:widget_repository/widget_repository.dart'; +import 'package:widget_module/views/mobile/widget_detail/node_display/code_display.dart'; + +import 'node_title.dart'; + +typedef NodeWidgetMapper = Widget Function(NodeModel model); + +class NodeDisplay extends StatelessWidget { + final NodeModel node; + final NodeWidgetMapper mapper; + final bool isDeath; + + const NodeDisplay({ + super.key, + required this.node, + required this.isDeath, + required this.mapper, + }); + + @override + Widget build(BuildContext context) { + Color primaryColor = Theme.of(context).primaryColor; + var style = context.select((AppConfigBloc bloc) => bloc.state.codeStyle); + bool isDesk = kIsWeb || Platform.isMacOS || Platform.isWindows || Platform.isLinux; + EdgeInsets pd = isDesk ? const EdgeInsets.symmetric(horizontal: 24, vertical: 8) : const EdgeInsets.all(8.0); + return Padding( + padding: pd, + child: Column( + children: [ + NodeTitle(text: node.name), + const SizedBox(height: 10), + CodeDisplay(display: mapper(node), code: node.code, style: style), + if (!isDeath) _buildNodeInfo(primaryColor), + ], + ), + ); + } + + Widget _buildNodeInfo(Color primaryColor) => Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Panel( + color: primaryColor.withOpacity(0.04), + child: Text( + node.subtitle, + style: const TextStyle(fontSize: 12), + )), + ); +} diff --git a/modules/widget_system/widget_module/lib/views/mobile/widget_detail/node_display/node_title.dart b/modules/widget_system/widget_module/lib/views/mobile/widget_detail/node_display/node_title.dart new file mode 100644 index 000000000..f285fb65f --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/widget_detail/node_display/node_title.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; +import 'package:toly_ui/toly_ui.dart'; + +class NodeTitle extends StatelessWidget { + final String text; + const NodeTitle({super.key, required this.text}); + + @override + Widget build(BuildContext context) { + + return Row( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Circle( + color: Theme.of(context).primaryColor, + radius: 5, + ), + ), + Expanded( + child: Text( + text, + style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 15), + ), + ), + ], + ); + } +} + +class LinkedButton extends StatelessWidget { + const LinkedButton({super.key}); + + @override + Widget build(BuildContext context) { + return const Placeholder(); + } +} + + diff --git a/modules/widget_system/widget_module/lib/views/mobile/widget_detail/widget_detail_bar.dart b/modules/widget_system/widget_module/lib/views/mobile/widget_detail/widget_detail_bar.dart new file mode 100644 index 000000000..b12089cc5 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/widget_detail/widget_detail_bar.dart @@ -0,0 +1,113 @@ +import 'dart:math'; + +import 'package:app/app.dart'; +import 'package:components/components.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:toly_ui/toly_ui.dart'; +import 'package:utils/utils.dart'; +import 'package:widget_module/blocs/action/widget_action.dart'; +import 'package:widget_module/blocs/blocs.dart'; +import 'package:widget_repository/widget_repository.dart'; +import 'package:widget_ui/widget_ui.dart'; + +class SliverWidgetDetailBar extends StatelessWidget { + final WidgetModel model; + + const SliverWidgetDetailBar({Key? key, required this.model}) + : super(key: key); + + final Color backgroundColor = const Color(0xffFAFAFA); + static const Color textColor = Color(0xff262626); + + @override + Widget build(BuildContext context) { + bool isDark = Theme.of(context).brightness == Brightness.dark; + Color? appBarColor = Theme.of(context).appBarTheme.backgroundColor; + Color? appBarTextColor = Theme.of(context).appBarTheme.titleTextStyle?.color; + + + return SliverAppBar( + pinned: true, + backgroundColor: isDark? appBarColor:backgroundColor, + titleTextStyle: TextStyle(color:isDark?appBarTextColor: textColor), + iconTheme: isDark? null:const IconThemeData(color: textColor), + centerTitle: false, + expandedHeight: 120.0, + scrolledUnderElevation: 0.5, + flexibleSpace: DiyFlexibleSpaceBar( + centerTitle: false, + expandedTitleScale: 2, + titleIconBuilder: (t) => WindmillWidget( + rotate: t * 2 * pi * 2, + radius: 15, + ), + fixedSubtitle: Text( + model.name, + style: TextStyle(color: isDark?appBarTextColor:Color(0xff696969), fontSize: 12), + ), + title: Padding( + padding: const EdgeInsets.only(bottom: 3), + child: Text( + model.nameCN, + style: TextStyle( fontSize: 16,color:isDark?appBarTextColor: textColor), + ), + ), + //伸展处布局 + titlePadding: const EdgeInsets.only(left: 20, bottom: 10), + //标题边距 + collapseMode: CollapseMode.parallax, + ), + elevation: 0, + actions: [ + _buildToHome(context), + FeedbackWidget( + onPressed: () => context.toggleLike(model.id), + child: BlocConsumer>( + listener: _listenLikeStateChange, + builder: _buildByLikeState, + ), + ) + ], + ); + } + + + // 监听 LikeWidgetBloc 伺机弹出 toast + void _listenLikeStateChange(BuildContext context, List state) { + bool collected = state.contains(model); + String msg = + collected ? "收藏【${model.name}】组件成功!" : "已取消【${model.name}】组件收藏!"; + Toast.toast( + context, + msg, + duration: Duration(milliseconds: collected ? 1500 : 600), + action: collected + ? SnackBarAction( + textColor: Colors.white, + label: '收藏夹管理', + onPressed: () => Scaffold.of(context).openEndDrawer()) + : null, + ); + } + + // 根据 [LikeWidgetState ] 构建图标 + Widget _buildByLikeState(BuildContext context, List state) { + bool liked = state.contains(model); + return Padding( + padding: const EdgeInsets.only(right: 20.0), + child: Icon( + liked ? TolyIcon.icon_star_ok : TolyIcon.icon_star_add, + size: 25, + ), + ); + } + + Widget _buildToHome(BuildContext context) => GestureDetector( + onLongPress: () => Scaffold.of(context).openEndDrawer(), + child: const Padding( + padding: EdgeInsets.all(15.0), + child: Icon(Icons.home), + ), + onTap: () => Navigator.of(context).pop()); +} diff --git a/modules/widget_system/widget_module/lib/views/mobile/widget_detail/widget_detail_page.dart b/modules/widget_system/widget_module/lib/views/mobile/widget_detail/widget_detail_page.dart new file mode 100644 index 000000000..34d1db6e4 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/widget_detail/widget_detail_page.dart @@ -0,0 +1,183 @@ +import 'dart:io'; + +import 'package:app/app.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:l10n/l10n.dart'; +import 'package:unit_widgets_display/unit_widgets_display.dart'; +import 'package:widget_repository/widget_repository.dart'; + +import '../../desk_ui/widget_detail/link_widget_buttons.dart'; +import 'node_display/node_display.dart'; +import 'package:widget_module/blocs/blocs.dart'; + +import 'category_end_drawer.dart'; +import 'widget_detail_bar.dart'; +import 'widget_detail_panel.dart'; +import 'widget_fields_sliver.dart'; +import 'package:tolyui/tolyui.dart'; + +// 用于组件详情不需要在一开始就加载 +// WidgetDetailBloc 可以在稍后提供 +class WidgetDetailPageScope extends StatelessWidget { + final WidgetModel model; + + const WidgetDetailPageScope({super.key, required this.model}); + + @override + Widget build(BuildContext context) { + WidgetRepository widgetRepository = context.read().repository; + NodeRepository nodeRepository = + kIsWeb ? MemoryNodeRepository() : const NodeDbRepository(); + + return BlocProvider( + create: (_) => WidgetDetailBloc( + widgetRepo: widgetRepository, + nodeRepo: nodeRepository, + )..push(model), + child: WidgetDetailPage(model: model), + ); + } +} + +class WidgetDetailPage extends StatelessWidget { + final WidgetModel model; + + const WidgetDetailPage({super.key, required this.model}); + + @override + Widget build(BuildContext context) { + WidgetDetailBloc bloc = context.watch(); + bool isDark = Theme.of(context).brightness == Brightness.dark; + + return Scaffold( + backgroundColor: isDark ? Colors.black : Colors.white, + endDrawer: CategoryEndDrawer(widget: bloc.currentWidget), + body: Builder(builder: (ctx) => _buildContent(ctx, bloc)), + ); + } + + Widget linkText(BuildContext context) => Row( + children: [ + const Padding( + padding: EdgeInsets.only(left: 15, right: 5), + child: Icon(Icons.link, color: Colors.blue), + ), + Text(context.l10n.relatedComponents, style: UnitTextStyle.labelBold), + ], + ); + + Widget _buildContent(BuildContext context, WidgetDetailBloc bloc) { + DetailState state = bloc.state; + return PopScope( + canPop: false, + onPopInvokedWithResult: (didPop, result) => + _onPop(context, didPop, result), + child: CustomScrollView( + slivers: [ + SliverWidgetDetailBar(model: bloc.currentWidget), + SliverToBoxAdapter( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + WidgetDetailPanel(model: bloc.currentWidget), + linkText(context), + if (state is DetailWithData) + LinkWidgetButtons(links: state.links, onSelect: bloc.push), + ], + ), + ), + if (state is DetailWithData) + state.nodes.isNotEmpty + ? SliverNodeList( + nodes: state.nodes, + model: state.widgetModel, + ) + : SliverWidgetFieldsList(widgetId: model.id), + ], + )); + } + + void _onPop(BuildContext context, bool didPop, dynamic result) { + if (didPop) { + return; + } + WidgetDetailBloc detailBloc = context.read(); + // 检查抽屉是否打开 + if (Scaffold.of(context).isEndDrawerOpen) { + if (context.mounted) { + Navigator.of(context).pop(); + } + return; + } + // 调用原来的 pop 逻辑 + detailBloc.pop().then((bool canPop) { + if (canPop && context.mounted) { + Navigator.of(context).pop(); + } + }); + } +} + +class SliverNodeList extends StatelessWidget { + final List nodes; + final WidgetModel model; + + const SliverNodeList({super.key, required this.nodes, required this.model}); + + @override + Widget build(BuildContext context) { + return SliverList( + delegate: SliverChildBuilderDelegate( + (_, i) => NodeDisplay( + mapper: _nodeMapper, + node: nodes[i], + isDeath: model.death, + ), + childCount: nodes.length, + )); + } + + Widget _nodeMapper(NodeModel node) { + NodeType type = node.type(model.name, node.priority); + Widget display = mapNodeDisplay(model.id, node.priority); + return switch (type) { + NodeType.display => display, + NodeType.newPage => newPageDisplay(display), + NodeType.description => display, + NodeType.deprecated => display, + }; + } +} + +Widget newPageDisplay(Widget page) { + return Builder( + builder: (ctx) => ElevatedButton( + onPressed: () { + Navigator.of(ctx).push(MaterialPageRoute(builder: (_) => page)); + }, + child: Wrap( + spacing: 6, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + Icon( + Icons.open_in_new, + size: 16, + color: Colors.white, + ), + Text('新界面打开'), + ], + ), + style: FillButtonPalette( + foregroundPalette: Palette.all(Colors.white), + borderRadius: BorderRadius.circular(6), + backgroundPalette: const Palette( + normal: Color(0xff1890ff), + hover: Color(0xff40a9ff), + pressed: Color(0xff096dd9), + ), + ).style, + ), + ); +} diff --git a/modules/widget_system/widget_module/lib/views/mobile/widget_detail/widget_detail_panel.dart b/modules/widget_system/widget_module/lib/views/mobile/widget_detail/widget_detail_panel.dart new file mode 100644 index 000000000..cfd417555 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/widget_detail/widget_detail_panel.dart @@ -0,0 +1,41 @@ +import 'package:toly_ui/toly_ui.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_star/flutter_star.dart'; +import 'package:widget_repository/widget_repository.dart'; +import 'package:widget_ui/widget_ui.dart'; + +class WidgetDetailPanel extends StatelessWidget { + final WidgetModel model; + + const WidgetDetailPanel({Key? key, required this.model}) : super(key: key); + + @override + Widget build(BuildContext context) { + Color color = Theme.of(context).primaryColor; + + return Row( + children: [ + _buildLeft(model, context), + Hero( + tag: "hero_widget_image_${model.id}", + child: WidgetDetailLogo( + model: model, + background: color, + widgetName: model.name, + ), + ) + ], + ); + } + + Widget _buildLeft(WidgetModel model, BuildContext context) => Expanded( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Panel( + constraints: BoxConstraints(minHeight: 110), + alignment: Alignment.topLeft, + color: Theme.of(context).scaffoldBackgroundColor, + child: Text(model.info)), + ), + ); +} diff --git a/modules/widget_system/widget_module/lib/views/mobile/widget_detail/widget_fields_sliver.dart b/modules/widget_system/widget_module/lib/views/mobile/widget_detail/widget_fields_sliver.dart new file mode 100644 index 000000000..17393aa59 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/widget_detail/widget_fields_sliver.dart @@ -0,0 +1,150 @@ +import 'package:flutter/material.dart'; +import 'package:widget_repository/widget_repository.dart'; + +class SliverWidgetFieldsList extends StatefulWidget { + final int widgetId; + + const SliverWidgetFieldsList({ + super.key, + required this.widgetId, + }); + + @override + State createState() => _SliverWidgetFieldsListState(); +} + +class _SliverWidgetFieldsListState extends State { + List? _fields; + bool _isLoading = true; + + @override + void initState() { + super.initState(); + _loadFields(); + } + + Future _loadFields() async { + try { + final repository = const WidgetDbRepository(); + final fields = await repository.loadWidgetFields(widget.widgetId); + setState(() { + _isLoading = false; + _fields = fields; + }); + } catch (e) { + setState(() { + _isLoading = false; + _fields = []; + }); + } + } + + @override + Widget build(BuildContext context) { + if (_isLoading) { + return const SliverToBoxAdapter( + child: Center( + child: Padding( + padding: EdgeInsets.all(32), + child: CircularProgressIndicator(), + ), + ), + ); + } + + if (_fields!.isEmpty) { + return const SliverToBoxAdapter( + child: Center( + child: Padding( + padding: EdgeInsets.all(32), + child: Text('暂无属性信息'), + ), + ), + ); + } + + return SliverPadding( + padding: const EdgeInsets.all(16), + sliver: SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) => _buildFieldItem(_fields![index]), + childCount: _fields!.length, + ), + ), + ); + } + + Widget _buildFieldItem(WidgetFieldModel field) { + return Padding( + padding: const EdgeInsets.all(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + width: 4, + height: 20, + decoration: BoxDecoration( + color: Theme.of(context).primaryColor, + borderRadius: BorderRadius.circular(2), + ), + ), + const SizedBox(width: 12), + Expanded( + child: Text( + field.fieldName, + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 14, + color: Colors.black87, + ), + ), + ), + // if (field.isRequired) _buildRequiredBadge(), + ], + ), + const SizedBox(height: 2), + Padding( + padding: const EdgeInsets.only(left: 16), + child: Text( + field.fieldType, + style: TextStyle( + color: Theme.of(context).primaryColor, + fontSize: 14, + fontWeight: FontWeight.w600, + ), + ), + ), + if (field.fieldDescZh != null) const SizedBox(height: 8), + Text( + field.fieldDescZh!, + style: const TextStyle( + fontSize: 13, + color: Colors.grey, + height: 1.4, + ), + ), + ], + ), + ); + } + + Widget _buildRequiredBadge() { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: Colors.red, + borderRadius: BorderRadius.circular(12), + ), + child: const Text( + '必需', + style: TextStyle( + color: Colors.white, + fontSize: 11, + fontWeight: FontWeight.w500, + ), + ), + ); + } +} diff --git a/modules/widget_system/widget_module/lib/views/mobile/widget_detail/widget_node_panel.dart b/modules/widget_system/widget_module/lib/views/mobile/widget_detail/widget_node_panel.dart new file mode 100644 index 000000000..c886ea4f6 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/widget_detail/widget_node_panel.dart @@ -0,0 +1,154 @@ +import 'package:app/app.dart'; +import 'package:toly_ui/toly_ui.dart'; +import 'package:flutter/material.dart'; +import 'package:share_plus/share_plus.dart'; +import 'package:toggle_rotate/toggle_rotate.dart'; + + +/// create by 张风捷特烈 on 2020-04-13 +/// contact me by email 1981462002@qq.com +/// 说明: 一个Widget的知识点对应的界面 + +class WidgetNodePanel extends StatefulWidget { + final String text; + final String subText; + final String code; + final Widget? show; + final HighlighterStyle? codeStyle; + final String? codeFamily; + final bool death; + + const WidgetNodePanel( + {Key? key, this.text='', + this.subText='', + this.code='', + this.death=false, + this.show, + required this.codeStyle, + this.codeFamily}) : super(key: key); + + @override + _WidgetNodePanelState createState() => _WidgetNodePanelState(); +} + +class _WidgetNodePanelState extends State { + CrossFadeState _crossFadeState = CrossFadeState.showFirst; + + bool get isFirst => _crossFadeState == CrossFadeState.showFirst; + + Color get themeColor => Theme.of(context).primaryColor; + + @override + Widget build(BuildContext context) { + return Container( + margin: const EdgeInsets.all(10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + buildNodeTitle(), + const SizedBox( + height: 20, + ), + _buildCode(context), + Padding( + padding: const EdgeInsets.only(top: 10, bottom: 20), + child: widget.show, + ), + if(!widget.death) + _buildNodeInfo(), + const Divider(), + ], + ), + ); + } + + Widget buildNodeTitle() => Row( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Circle( + color: themeColor, + radius: 5, + ), + ), + Expanded( + child: Text( + widget.text, + style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 15), + ), + ), + _buildShareButton(), + _buildCodeButton() + ], + ); + + Widget _buildNodeInfo() => SizedBox( + width: double.infinity, + child: Panel( + color: Theme.of(context).scaffoldBackgroundColor, + child: Text( + widget.subText, + style: const TextStyle(fontSize: 12), + )), + ); + + Widget _buildCodeButton() => Padding( + padding: const EdgeInsets.only(right: 10.0), + child: ToggleRotate( + durationMs: 300, + child: Icon( + TolyIcon.icon_code, + color: themeColor, + ), + onTap: _toggleCodePanel, + ), + ); + + + + Widget _buildShareButton() => FeedbackWidget( + mode: FeedMode.fade, + a: 0.4, + onPressed: _doShare, + child: Padding( + padding: const EdgeInsets.only( + right: 10, + ), + child: Icon( + TolyIcon.icon_share, + size: 20, + color: themeColor, + ), + ), + ); + + Widget _buildCode(BuildContext context) => AnimatedCrossFade( + firstCurve: Curves.easeInCirc, + secondCurve: Curves.easeInToLinear, + firstChild: const SizedBox(), + secondChild: SizedBox( + width: MediaQuery.of(context).size.width, + child: CodeWidget( + fontFamily: widget.codeFamily, + code: isFirst?'':widget.code, + style: widget.codeStyle ?? + HighlighterStyle.fromColors(HighlighterStyle.lightColor), + ), + ), + duration: const Duration(milliseconds: 200), + crossFadeState: _crossFadeState, + ); + + //执行分享 + _doShare() { + Share.share(widget.code); + } + + // 折叠代码面板 + _toggleCodePanel() { + setState(() { + _crossFadeState = + !isFirst ? CrossFadeState.showFirst : CrossFadeState.showSecond; + }); + } +} diff --git a/lib/app/navigation/home_drawer.dart b/modules/widget_system/widget_module/lib/views/mobile/widget_page/home_drawer.dart similarity index 84% rename from lib/app/navigation/home_drawer.dart rename to modules/widget_system/widget_module/lib/views/mobile/widget_page/home_drawer.dart index 36e7bab1f..a23c52c54 100644 --- a/lib/app/navigation/home_drawer.dart +++ b/modules/widget_system/widget_module/lib/views/mobile/widget_page/home_drawer.dart @@ -1,7 +1,7 @@ import 'package:app/app.dart'; import 'package:components/components.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; import 'unit_drawer_header.dart'; @@ -11,7 +11,7 @@ import 'unit_drawer_header.dart'; class HomeDrawer extends StatelessWidget { - const HomeDrawer({Key? key}):super(key: key); + const HomeDrawer({super.key}); @override Widget build(BuildContext context) { @@ -31,15 +31,15 @@ class HomeDrawer extends StatelessWidget { padding: EdgeInsets.zero, children: [ UnitDrawerHeader(color: color), - _buildItem(context, TolyIcon.icon_them, '应用设置', UnitRouter.setting), + _buildItem(context, TolyIcon.icon_them, '应用设置', '/settings'), _buildItem( - context, TolyIcon.icon_layout, '数据管理', UnitRouter.data_manage), + context, TolyIcon.icon_layout, '数据管理', '/data_manage'), const Divider(height: 1), _buildFlutterUnit(context), _buildItem(context, TolyIcon.icon_code, 'Dart 手册', ''), const Divider(height: 1), - _buildItem(context, Icons.info, '关于应用', UnitRouter.about_app), - _buildItem(context, TolyIcon.icon_kafei, '联系本王', UnitRouter.about_me), + _buildItem(context, Icons.info, '关于应用', '/about_app'), + _buildItem(context, TolyIcon.icon_kafei, '联系本王', '/about_me'), ], ), ); @@ -56,7 +56,7 @@ class HomeDrawer extends StatelessWidget { _buildItem(context, TolyIcon.icon_tag, '属性集录', ''), _buildItem(context, Icons.palette, '绘画集录', ''), _buildItem(context, Icons.widgets, '布局集录', ''), - _buildItem(context, TolyIcon.icon_bug, '要点集录', UnitRouter.issues_point), + _buildItem(context, TolyIcon.icon_bug, '要点集录', ''), ], ); @@ -73,7 +73,7 @@ class HomeDrawer extends StatelessWidget { Icon(Icons.chevron_right, color: Theme.of(context).primaryColor), onTap: () { if (linkTo.isNotEmpty) { - Navigator.of(context).pushNamed(linkTo); + context.push(linkTo); if (onTap != null) onTap(); } }, diff --git a/modules/widget_system/widget_module/lib/views/mobile/widget_page/phone_widget_content.dart b/modules/widget_system/widget_module/lib/views/mobile/widget_page/phone_widget_content.dart new file mode 100644 index 000000000..9b1ded9a1 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/widget_page/phone_widget_content.dart @@ -0,0 +1,33 @@ +import 'package:app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:widget_module/widget_module.dart'; + +import 'package:widget_repository/widget_repository.dart'; +import 'package:widget_ui/widget_ui.dart'; + +class PhoneWidgetContent extends StatelessWidget { + final List items; + + const PhoneWidgetContent({Key? key, required this.items}) : super(key: key); + + @override + Widget build(BuildContext context) { + return SliverList( + delegate: SliverChildBuilderDelegate( + (_, int index) => Padding( + padding: const EdgeInsets.only(left: 8.0, right: 8, top: 8), + child: WidgetItem( + model: items[index], + onWidget: context.handleWidgetAction, + ), + ), + childCount: items.length, + ), + ); + } + + void _toDetail(BuildContext context, WidgetModel model) { + context.push('/widget/detail/${model.name}', extra: model); + } +} diff --git a/modules/widget_system/widget_module/lib/views/mobile/widget_page/slider.dart b/modules/widget_system/widget_module/lib/views/mobile/widget_page/slider.dart new file mode 100644 index 000000000..e791a024d --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/widget_page/slider.dart @@ -0,0 +1,212 @@ +// import 'package:flutter/gestures.dart'; +// import 'package:flutter/material.dart'; +// import 'package:app/app.dart'; +// +// import 'widget_page.dart'; +// +// class Carousel extends StatefulWidget { +// final List data; +// +// const Carousel({Key? key, required this.data}) : super(key: key); +// +// @override +// _CarouselState createState() => _CarouselState(); +// } +// +// class _CarouselState extends State { +// final ValueNotifier factor = ValueNotifier(0); +// +// late PageController _ctrl; +// +// final int _firstOffset = 1000; //初始偏移 +// int _position = 0; //页面位置 +// +// @override +// void initState() { +// super.initState(); +// _position = _position + _firstOffset; +// +// double value = ((_position - _firstOffset + 1) % 5) / 5; +// factor.value = value == 0 ? 1 : value; +// _ctrl = PageController( +// viewportFraction: 0.9, +// initialPage: _position, +// )..addListener(() { +// if (_ctrl.page != null) { +// double value = (_ctrl.page! - _firstOffset + 1) % 5 / 5; +// factor.value = value == 0 ? 1 : value; +// } +// }); +// } +// +// @override +// void dispose() { +// _ctrl.dispose(); +// factor.dispose(); +// super.dispose(); +// } +// +// Color get color => Colors.blue; +// +// Color get nextColor => Colors.orangeAccent; +// +// bool get isDark => Theme.of(context).brightness == Brightness.dark; +// +// BoxDecoration get boxDecoration => BoxDecoration( +// color: isDark ? Colors.white.withAlpha(33) : Colors.white, +// borderRadius: const BorderRadius.only( +// topLeft: Radius.circular(40), topRight: Radius.circular(40)), +// ); +// +// @override +// Widget build(BuildContext context) { +// List data = widget.data; +// Widget child = PageView.builder( +// controller: _ctrl, // itemCount: 7, +// itemBuilder: (_, index) { +// return AnimatedBuilder( +// child: _buildByIndex(context, index, data), +// animation: _ctrl, +// builder: (context, child) => _buildAnimItemByIndex( +// context, +// child, +// index, +// ), +// ); +// }, +// onPageChanged: (index) { +// _position = index; +// setState(() {}); +// }, +// ); +// +// int realIndex = _fixPosition(_position, _firstOffset, data.length); +// +// child = Stack( +// alignment: Alignment.bottomCenter, +// children: [ +// child, +// Padding( +// padding: const EdgeInsets.only(bottom: 16.0), +// child: Wrap( +// spacing: 6, +// children: widget.data.asMap().keys.map((int index) { +// return GestureDetector( +// onTap: () { +// int deta = index - realIndex; +// _position += deta; +// print('$_position,$realIndex'); +// // _position = index; +// _ctrl.animateToPage(_position, +// duration: Duration(milliseconds: 500), +// curve: Curves.easeIn); +// }, +// child: Container( +// width: 8, +// height: 8, +// decoration: BoxDecoration( +// color: realIndex == index +// ? Colors.white +// : Colors.black.withValues(alpha: 0.4), +// shape: BoxShape.circle, +// boxShadow: [ +// BoxShadow( +// color: Colors.white.withValues(alpha: 0.3), +// spreadRadius: 1, +// blurRadius: 10, +// blurStyle: BlurStyle.outer) +// ]), +// ), +// ); +// }).toList(), +// ), +// ) +// ], +// ); +// +// if (!kIsDesk) { +// return child; +// } +// +// return MouseRegion( +// onEnter: _onEnter, +// onExit: _onExit, +// child: Stack( +// alignment: Alignment.center, +// children: [ +// Padding( +// padding: const EdgeInsets.symmetric(horizontal: 48.0), +// child: child), +// Positioned( +// right: 0, +// child: IconButton( +// onPressed: () { +// _position += 1; +// _ctrl.animateToPage(_position, +// duration: Duration(milliseconds: 500), +// curve: Curves.easeIn); +// }, +// icon: Icon(Icons.navigate_next_outlined))), +// Positioned( +// left: 0, +// child: IconButton( +// onPressed: () { +// _position -= 1; +// _ctrl.animateToPage(_position, +// duration: Duration(milliseconds: 500), +// curve: Curves.easeIn); +// }, +// icon: Icon(Icons.navigate_before))), +// ], +// ), +// ); +// } +// +// Widget? _buildByIndex( +// BuildContext context, int index, List data) { +// int realIndex = _fixPosition(index, _firstOffset, data.length); +// return WidgetDisplay( +// widget: data[realIndex], +// ); +// } +// +// Widget _buildAnimItemByIndex(BuildContext context, Widget? child, int index) { +// double value; +// if (_ctrl.position.haveDimensions && _ctrl.page != null) { +// value = _ctrl.page! - index; +// } else { +// value = (_position - index).toDouble(); +// } +// value = (1 - ((value.abs()) * .3)).clamp(0, 1).toDouble(); +// value = Curves.easeOut.transform(value); +// +// return Transform( +// transform: Matrix4.diagonal3Values(1.0, value, 1.0), +// alignment: Alignment.center, +// child: Padding( +// padding: const EdgeInsets.all(4.0), +// child: child, +// ), +// ); +// } +// +// int _fixPosition(int realPos, int initPos, int length) { +// final int offset = realPos - initPos; +// int result = offset % length; +// return result < 0 ? length + result : result; +// } +// +// bool _hover = false; +// +// void _onEnter(PointerEnterEvent event) { +// setState(() { +// _hover = true; +// }); +// } +// +// void _onExit(PointerExitEvent event) { +// setState(() { +// _hover = false; +// }); +// } +// } diff --git a/modules/widget_system/widget_module/lib/views/mobile/widget_page/standard_home_page.dart b/modules/widget_system/widget_module/lib/views/mobile/widget_page/standard_home_page.dart new file mode 100644 index 000000000..93f03eee7 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/widget_page/standard_home_page.dart @@ -0,0 +1,211 @@ +import 'package:l10n/ext.dart'; +import 'package:widget_module/blocs/action/widget_action.dart'; +import 'package:widget_repository/widget_repository.dart'; + +import 'package:toly_ui/toly_ui.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:widget_module/blocs/blocs.dart'; +import 'home_drawer.dart'; +import 'standard_home_search.dart'; +import 'widget_page.dart'; + +class StandardHomePage extends StatefulWidget { + final Widget? heard; + + const StandardHomePage({super.key, this.heard}); + + @override + State createState() => _StandardHomePageState(); +} + +class _StandardHomePageState extends State + with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin { + List get _tabs { + final provider = WidgetStatisticsProvider(); + final stats = provider.statistics; + + if (stats == null) { + return [ + context.l10n.stateless, + context.l10n.stateful, + context.l10n.single, + context.l10n.multi, + context.l10n.sliver, + context.l10n.proxy, + context.l10n.other, + ]; + } + + return [ + context.l10n.stateless, + context.l10n.stateful, + context.l10n.single, + context.l10n.multi, + context.l10n.sliver, + context.l10n.proxy, + context.l10n.other, + ]; + } + + ValueNotifier indexValue = ValueNotifier(0); + + List _buildTabWidgets() { + final provider = WidgetStatisticsProvider(); + final stats = provider.statistics; + final counts = [ + stats?.familyCount[WidgetFamily.stateless] ?? 0, + stats?.familyCount[WidgetFamily.stateful] ?? 0, + stats?.familyCount[WidgetFamily.singleChildRender] ?? 0, + stats?.familyCount[WidgetFamily.multiChildRender] ?? 0, + stats?.familyCount[WidgetFamily.sliver] ?? 0, + stats?.familyCount[WidgetFamily.proxy] ?? 0, + stats?.familyCount[WidgetFamily.other] ?? 0, + ]; + + return List.generate( + _tabs.length, + (index) => ValueListenableBuilder( + valueListenable: indexValue, + builder: (context, value, __) { + return Stack( + clipBehavior: Clip.none, + children: [ + Text(_tabs[index]), + if (value == index) + Positioned( + right: -10, + top: -6, + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 6, vertical: 3), + decoration: BoxDecoration( + color: Theme.of(context) + .primaryColor + .withValues(alpha: 0.6), + borderRadius: BorderRadius.circular(6), + ), + child: Text('${counts[index]}', + style: const TextStyle( + fontFamily: '黑体', + height: 1, + fontSize: 9, + color: Colors.white)), + ), + ), + ], + ); + })); + } + + late TabController tabController; + + @override + void initState() { + super.initState(); + tabController = TabController(length: 7, vsync: this); + tabController.addListener(_onChange); + } + + int maxCount = 60; + + @override + void dispose() { + tabController.removeListener(_onChange); + + tabController.dispose(); + super.dispose(); + } + + void _switchTab(int index) { + WidgetFamily widgetFamily = WidgetFamily.values[index]; + WidgetsBloc bloc = context.read(); + if (bloc.state.filter.family == widgetFamily) return; + PrimaryScrollController.of(context).jumpTo(0); + context.switchWidgetFamily(widgetFamily); + } + + @override + Widget build(BuildContext context) { + super.build(context); + final AppBarThemeData appBarTheme = AppBarTheme.of(context); + bool isDark = Theme.of(context).brightness == Brightness.dark; + double bottom = MediaQuery.of(context).padding.bottom; + return Scaffold( + extendBody: true, + drawer: const HomeDrawer(), + body: Column( + children: [ + AnnotatedRegion( + value: appBarTheme.systemOverlayStyle!, + child: Container( + color: isDark ? Colors.black : Colors.white, + height: MediaQuery.of(context).padding.top, + ), + ), + Expanded( + child: NestedScrollView( + floatHeaderSlivers: true, + headerSliverBuilder: _buildHeader, + body: const WidgetPage()), + ), + SizedBox(height: bottom) + ], + ), + ); + } + + List _buildHeader(BuildContext context, bool innerBoxIsScrolled) { + Color themeColor = Theme.of(context).primaryColor; + bool isDark = Theme.of(context).brightness == Brightness.dark; + + return [ + // const SliverSnapHeader(child: StandardHomeSearch()), + const SliverFloatingHeader( + snapMode: FloatingHeaderSnapMode.scroll, + child: StandardHomeSearch(), + ), + if (widget.heard != null) + SliverToBoxAdapter( + child: Container( + height: 168, + color: isDark ? Colors.black : Colors.white, + child: widget.heard, + ), + ), + SliverPinnedHeader( + color: isDark ? Colors.black : Colors.white, + child: TabBar( + onTap: _switchTab, + tabAlignment: TabAlignment.start, + indicatorSize: TabBarIndicatorSize.label, + isScrollable: true, + indicator: RoundRectTabIndicator( + borderSide: BorderSide(color: themeColor, width: 3), + ), + labelStyle: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + ), + controller: tabController, + labelColor: themeColor, + indicatorWeight: 3, + unselectedLabelColor: Colors.grey, + indicatorColor: themeColor, + tabs: _buildTabWidgets() + .map((Widget widget) => Tab(child: widget)) + .toList(), + )), + // handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), + // ), + ]; + } + + @override + bool get wantKeepAlive => true; + + void _onChange() { + indexValue.value = tabController.index; + } +} diff --git a/modules/widget_system/widget_module/lib/views/mobile/widget_page/standard_home_search.dart b/modules/widget_system/widget_module/lib/views/mobile/widget_page/standard_home_search.dart new file mode 100644 index 000000000..174f01dfc --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/widget_page/standard_home_search.dart @@ -0,0 +1,99 @@ +import 'package:app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:l10n/ext.dart'; +import 'package:toly_ui/toly_ui.dart'; + +import '../search_page/standard_search_page.dart'; + +class StandardHomeSearch extends StatelessWidget + implements PreferredSizeWidget { + const StandardHomeSearch({Key? key}) : super(key: key); + + @override + Size get preferredSize => const Size.fromHeight(35 + 8 * 2); + + @override + Widget build(BuildContext context) { + bool isDark = Theme.of(context).brightness == Brightness.dark; + + return ColoredBox( + color: isDark ? Colors.black : Colors.white, + child: Row( + children: [ + _buildHead(context), + Expanded( + child: GestureDetector( + onTap: () => _toSearchPage(context), + child: Container( + margin: const EdgeInsets.symmetric(vertical: 8), + height: 35, + child: TextField( + autofocus: false, + enabled: false, + cursorColor: Colors.blue, + maxLines: 1, + decoration: _topSearchInputDecoration(isDark, context), + )), + ), + ), + _buildCollectIcon(context), + ], + ), + ); + } + + InputDecoration _topSearchInputDecoration(bool isDark, BuildContext context) { + String hintText = context.l10n.searchWidget; + return InputDecoration( + filled: true, + fillColor: isDark ? const Color(0xff292929) : const Color(0xffF3F6F9), + prefixIcon: const Icon( + Icons.search, + color: Colors.grey, + size: 20, + ), + prefixIconConstraints: + const BoxConstraints(maxHeight: 24, minWidth: 36), + isCollapsed: true, + contentPadding: const EdgeInsets.only(top: 4, bottom: 4, right: 8), + border: const UnderlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.all(Radius.circular(6)), + ), + hintText: hintText, + hintStyle: const TextStyle(fontSize: 14)); + } + + Widget _buildHead(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 12.0), + child: FeedbackWidget( + onPressed: () => _openDrawer(context), + child: const CircleAvatar( + radius: 16, + backgroundImage: AssetImage('assets/images/icon_head.webp'), + ), + ), + ); + } + + Widget _buildCollectIcon(BuildContext context) { + return IconButton( + onPressed: () => context.push(AppRoute.collection.url), + icon: const Icon( + TolyIcon.icon_collect, + size: 22, + ), + ); + } + + void _toSearchPage(BuildContext context) { + Navigator.of(context) + .push(FadePageRoute(child: const StandardSearchPageProvider())); + } + + void _openDrawer(BuildContext context) { + // Scaffold.of(context).openDrawer(); + } +} diff --git a/modules/widget_system/widget_module/lib/views/mobile/widget_page/unit_drawer_header.dart b/modules/widget_system/widget_module/lib/views/mobile/widget_page/unit_drawer_header.dart new file mode 100644 index 000000000..ebe738afb --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/widget_page/unit_drawer_header.dart @@ -0,0 +1,74 @@ +import 'package:flutter/material.dart'; + +/// create by 张风捷特烈 on 2020-04-22 +/// contact me by email 1981462002@qq.com +/// 说明: + +class UnitDrawerHeader extends StatelessWidget { + final Color color; + + const UnitDrawerHeader({super.key, required this.color}); + + @override + Widget build(BuildContext context) { + return DrawerHeader( + padding: const EdgeInsets.only(top: 10, left: 15), + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/images/wy_300x200_filter.webp'), + fit: BoxFit.cover), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Wrap( + spacing: 10, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + FlutterLogo( + size: 35, + ), + Text( + 'Flutter Unit', + style: TextStyle(fontSize: 24, color: Colors.white, shadows: [ + Shadow( + color: Colors.black, offset: Offset(1, 1), blurRadius: 3) + ]), + ), + ], + ), + const SizedBox(height: 15), + Text( + 'The Unity Of Flutter, The Unity Of Coder.', + style: TextStyle(fontSize: 15, color: Colors.white, shadows: [ + Shadow(color: color, offset: const Offset(.5, .5), blurRadius: 1) + ]), + ), + const SizedBox(height: 5), + Text( + 'Flutter的联合,编程者的联合。', + style: TextStyle(fontSize: 15, color: Colors.white, shadows: [ + Shadow(color: color, offset: const Offset(.5, .5), blurRadius: 1) + ]), + ), + const SizedBox(height: 10), + const Row( + children: [ + Spacer(flex: 5), + Text( + '—— 张风捷特烈', + style: TextStyle(fontSize: 15, color: Colors.white, shadows: [ + Shadow( + color: Colors.orangeAccent, + offset: Offset(.5, .5), + blurRadius: 1) + ]), + ), + Spacer(flex: 1), + ], + ), + ], + ), + ); + } +} diff --git a/lib/widget_ui/mobile/widget_panel/widget_list_panel.dart b/modules/widget_system/widget_module/lib/views/mobile/widget_page/widget_list_panel.dart similarity index 100% rename from lib/widget_ui/mobile/widget_panel/widget_list_panel.dart rename to modules/widget_system/widget_module/lib/views/mobile/widget_page/widget_list_panel.dart diff --git a/lib/widget_ui/mobile/widget_panel/widget_page.dart b/modules/widget_system/widget_module/lib/views/mobile/widget_page/widget_page.dart similarity index 81% rename from lib/widget_ui/mobile/widget_panel/widget_page.dart rename to modules/widget_system/widget_module/lib/views/mobile/widget_page/widget_page.dart index b8a4561bc..4bb471810 100644 --- a/lib/widget_ui/mobile/widget_panel/widget_page.dart +++ b/modules/widget_system/widget_module/lib/views/mobile/widget_page/widget_page.dart @@ -1,11 +1,11 @@ -import 'package:artifact/artifact.dart'; +import 'package:components/components.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:refresh/refresh.dart'; import 'package:widget_module/blocs/blocs.dart'; - +import 'package:note/note.dart'; import 'widget_list_panel.dart'; +import 'package:tolyui_refresh/tolyui_refresh.dart'; class WidgetPage extends StatefulWidget { const WidgetPage({Key? key}) : super(key: key); @@ -15,10 +15,8 @@ class WidgetPage extends StatefulWidget { } class _WidgetPageState extends State { - final RefreshController _refreshController = - RefreshController(initialRefresh: false); - + RefreshController(initialRefresh: false); @override void dispose() { @@ -28,23 +26,19 @@ class _WidgetPageState extends State { @override Widget build(BuildContext context) { - const Color themeColor = Color(0xff007ACB); - - return BlocListener( + return BlocListener( listener: _listenStateChange, child: RefreshConfigWrapper( - child: SmartRefresher( + child: TolyRefresh( + physics: BouncingScrollPhysics(), controller: _refreshController, onRefresh: _onRefresh, enablePullUp: true, onLoading: _onLoadMore, child: CustomScrollView( - // key: PageStorageKey(name), slivers: [ - const WidgetListPanel(), - ], ), ), @@ -52,18 +46,17 @@ class _WidgetPageState extends State { ); } - void _onRefresh() async{ + void _onRefresh() async { context.read().add(EventRefresh()); + await context.read().refreshFromNet(); } void _onLoadMore() { context.read().add(EventLoadMore()); } - void _listenStateChange(BuildContext context, WidgetsState state) async{ + void _listenStateChange(BuildContext context, WidgetsState state) async { if (state is WidgetsLoaded) { - print('===_listenStateChange:${state.full}======'); - if (state.operate == LoadOperate.refresh) { _refreshController.refreshCompleted(); } diff --git a/modules/widget_system/widget_module/lib/views/widgets_bloc_provider.dart b/modules/widget_system/widget_module/lib/views/widgets_bloc_provider.dart new file mode 100644 index 000000000..3dc693d02 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/widgets_bloc_provider.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:fx_platform_adapter/fx_platform_adapter.dart'; +import 'package:widget_ui/widget_ui.dart'; +import '../blocs/blocs.dart'; +import 'package:widget_repository/widget_repository.dart'; + +class WidgetsBlocProvider extends StatefulWidget { + final Widget child; + + const WidgetsBlocProvider({super.key, required this.child}); + + @override + State createState() => _WidgetsBlocProviderState(); +} + +class _WidgetsBlocProviderState extends State { + late WidgetRepository repository; + final CategoryBloc categoryBloc = + CategoryBloc(repository: CategoryDbRepository()); + + @override + void initState() { + super.initState(); + if (kAppEnv.isWeb) { + repository = MemoryWidgetRepository(); + } else { + repository = const WidgetDbRepository(); + } + } + + @override + Widget build(BuildContext context) { + return MultiBlocProvider( + providers: [ + BlocProvider( + create: (_) => WidgetsBloc(repository: repository)), + BlocProvider(create: (_) => categoryBloc), + BlocProvider( + create: (_) => LikeWidgetBloc(repository: repository)), + BlocProvider( + create: (_) => CategoryWidgetBloc(categoryBloc: categoryBloc)), + ], + child: widget.child, + ); + } + + @override + void dispose() { + categoryBloc.close(); + super.dispose(); + } +} diff --git a/modules/widget_system/widget_module/lib/widget_module.dart b/modules/widget_system/widget_module/lib/widget_module.dart new file mode 100644 index 000000000..05012141f --- /dev/null +++ b/modules/widget_system/widget_module/lib/widget_module.dart @@ -0,0 +1,18 @@ +library widget_module; + +export 'views/desk_ui/desk_ui.dart'; +export 'views/mobile/mobile_ui.dart'; +export 'views/widgets_bloc_provider.dart'; +export 'event/widget_event.dart'; +export 'event/widget_statistics_event.dart'; +export 'blocs/action/widget_action.dart'; +export 'package:widget_ui/widget_ui.dart' show LikeWidgetBloc; +export 'package:widget_repository/widget_repository.dart' + show + WidgetFilter, + WidgetFamily, + CategoryModel, + WidgetModel, + CategoryRepository, + CategoryTo, + LikeDao,WidgetDao,NodeDao,CategoryDao; diff --git a/modules/widget_system/widget_module/pubspec.yaml b/modules/widget_system/widget_module/pubspec.yaml new file mode 100644 index 000000000..0add345f4 --- /dev/null +++ b/modules/widget_system/widget_module/pubspec.yaml @@ -0,0 +1,57 @@ +name: widget_module +description: A new Flutter package project. +version: 0.0.1 +homepage: +publish_to: none + +environment: + sdk: ">=3.5.0 <4.0.0" + flutter: ">=1.17.0" +resolution: workspace +dependencies: + flutter: + sdk: flutter + unit_widgets_display: ^0.0.1+2 + widget_ui: + path: ../widget_ui + widget_repository: + path: ../widget_repository + +#dependency_overrides: +# intl: 0.19.0 +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # To add assets to your package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # To add custom fonts to your package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/widget_module/test/widget_module_test.dart b/modules/widget_system/widget_module/test/widget_module_test.dart similarity index 100% rename from packages/widget_module/test/widget_module_test.dart rename to modules/widget_system/widget_module/test/widget_module_test.dart diff --git a/modules/widget_system/widget_repository/.gitignore b/modules/widget_system/widget_repository/.gitignore new file mode 100644 index 000000000..ac5aa9893 --- /dev/null +++ b/modules/widget_system/widget_repository/.gitignore @@ -0,0 +1,29 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +build/ diff --git a/modules/widget_system/widget_repository/.metadata b/modules/widget_system/widget_repository/.metadata new file mode 100644 index 000000000..24472f12f --- /dev/null +++ b/modules/widget_system/widget_repository/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "2663184aa79047d0a33a14a3b607954f8fdd8730" + channel: "stable" + +project_type: package diff --git a/modules/widget_system/widget_repository/CHANGELOG.md b/modules/widget_system/widget_repository/CHANGELOG.md new file mode 100644 index 000000000..41cc7d819 --- /dev/null +++ b/modules/widget_system/widget_repository/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/modules/widget_system/widget_repository/LICENSE b/modules/widget_system/widget_repository/LICENSE new file mode 100644 index 000000000..ba75c69f7 --- /dev/null +++ b/modules/widget_system/widget_repository/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/modules/widget_system/widget_repository/README.md b/modules/widget_system/widget_repository/README.md new file mode 100644 index 000000000..4a260d8d2 --- /dev/null +++ b/modules/widget_system/widget_repository/README.md @@ -0,0 +1,39 @@ + + +TODO: Put a short description of the package here that helps potential users +know whether this package might be useful for them. + +## Features + +TODO: List what your package can do. Maybe include images, gifs, or videos. + +## Getting started + +TODO: List prerequisites and provide or point to information on how to +start using the package. + +## Usage + +TODO: Include short and useful examples for package users. Add longer examples +to `/example` folder. + +```dart +const like = 'sample'; +``` + +## Additional information + +TODO: Tell users more about the package: where to find more information, how to +contribute to the package, how to file issues, what response they can expect +from the package authors, and more. diff --git a/modules/widget_system/widget_repository/analysis_options.yaml b/modules/widget_system/widget_repository/analysis_options.yaml new file mode 100644 index 000000000..a5744c1cf --- /dev/null +++ b/modules/widget_system/widget_repository/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/modules/widget_system/widget_repository/doc/tables_overview.md b/modules/widget_system/widget_repository/doc/tables_overview.md new file mode 100644 index 000000000..2770af6d5 --- /dev/null +++ b/modules/widget_system/widget_repository/doc/tables_overview.md @@ -0,0 +1,110 @@ +# 数据表结构总览 + +## 核心表 (6张) + +### 1. widget - Widget基本信息 +```sql +CREATE TABLE widget( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name VARCHAR(64) NOT NULL UNIQUE, + path TEXT NOT NULL, + is_abstract INTEGER NOT NULL DEFAULT 0, + is_private INTEGER NOT NULL DEFAULT 0, + deprecated INTEGER DEFAULT 0, + family INTEGER NOT NULL, + lever FLOAT(2) NOT NULL, + linkWidget TEXT DEFAULT '' +); +``` + +### 2. widget_inheritance - Widget继承关系 +```sql +CREATE TABLE widget_inheritance ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + widget_id INTEGER NOT NULL, + parent_name TEXT NOT NULL, + inheritance_order INTEGER NOT NULL +); +``` + +### 3. widget_fields - Widget字段信息 +```sql +CREATE TABLE widget_fields ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + widget_id INTEGER NOT NULL, + field_name TEXT NOT NULL, + field_type TEXT NOT NULL, + field_desc TEXT, + field_desc_zh TEXT, + field_order INTEGER NOT NULL, + is_required INTEGER NOT NULL DEFAULT 0 +); +``` + +### 4. widget_desc - Widget描述信息 +```sql +CREATE TABLE widget_desc( + id INTEGER PRIMARY KEY AUTOINCREMENT, + widget_id INTEGER NOT NULL, + name VARCHAR(128) NOT NULL, + info TEXT NOT NULL, + locale VARCHAR(16) DEFAULT 'zh-cn' +); +``` + +### 5. node - 示例代码节点 +```sql +CREATE TABLE node( + id INTEGER PRIMARY KEY AUTOINCREMENT, + widgetId INTEGER NOT NULL, + priority INTEGER DEFAULT 0, + code TEXT NOT NULL +); +``` + +### 6. node_desc - 节点描述信息 +```sql +CREATE TABLE node_desc( + id INTEGER PRIMARY KEY AUTOINCREMENT, + node_id INTEGER NOT NULL, + name VARCHAR(128) NOT NULL, + subtitle TEXT NOT NULL, + locale VARCHAR(16) DEFAULT 'zh-cn' +); +``` + +## 扩展表 (2张) + +### 7. category - Widget分类 +```sql +CREATE TABLE category( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name VARCHAR(64) NOT NULL UNIQUE, + type INTEGER DEFAULT 0, + color VARCHAR(9) DEFAULT '#FF2196F3', + info VARCHAR(256) DEFAULT '这里什么都没有...', + created DATETIME NOT NULL, + updated DATETIME NOT NULL, + priority INTEGER DEFAULT 0, + image VARCHAR(128) DEFAULT '' +); +``` + +### 8. category_widget - 分类Widget关联 +```sql +CREATE TABLE category_widget( + id INTEGER PRIMARY KEY AUTOINCREMENT, + categoryId INTEGER NOT NULL, + widgetId INTEGER NOT NULL +); +``` + +## 表关系图 +``` +widget (1) ←→ (N) widget_inheritance +widget (1) ←→ (N) widget_fields +widget (1) ←→ (N) widget_desc +widget (1) ←→ (N) node +node (1) ←→ (N) node_desc +widget (N) ←→ (N) category [through category_widget] +``` \ No newline at end of file diff --git a/modules/widget_system/widget_repository/lib/src/database/dao/category_dao.dart b/modules/widget_system/widget_repository/lib/src/database/dao/category_dao.dart new file mode 100644 index 000000000..9f4b6f7ce --- /dev/null +++ b/modules/widget_system/widget_repository/lib/src/database/dao/category_dao.dart @@ -0,0 +1,191 @@ +import 'package:fx_dao/fx_dao.dart'; + +import '../../../widget_repository.dart'; + +//""" +// CREATE TABLE IF NOT EXISTS category_widget( +// id INTEGER PRIMARY KEY AUTOINCREMENT, +// name VARCHAR(64) NOT NULL, +// color VARCHAR(9) DEFAULT '#FF2196F3', +// info VARCHAR(256) DEFAULT '这里什么都没有...', +// created DATETIME NOT NULL, +// updated DATETIME NOT NULL, +// priority INTEGER DEFAULT 0, +// image VARCHAR(128) NULL image DEFAULT '' +// ); +//"""; + +class CategoryDao extends Dao { + @override + String get createSql => ''; + + @override + String get name => 'category'; + + Future insert(CategoryPo category) async { + String addSql = //插入数据 + "INSERT INTO " + "category(id,name,color,info,priority,image,created,updated) " + "VALUES (?,?,?,?,?,?,?,?);"; + return await database + .transaction((tran) async => await tran.rawInsert(addSql, [ + category.id, + category.name, + category.color, + category.info, + category.priority, + category.image, + category.created?.toIso8601String(), + category.updated.toIso8601String(), + ])); + } + + Future update(CategoryPo widget) async { + String updateSql = //插入数据 + "UPDATE category SET name=? , color=? ,info=?, priority=?,image=?,updated=? " + "WHERE id = ?"; + + return await database + .transaction((tran) async => await tran.rawUpdate(updateSql, [ + widget.name, + widget.color, + widget.info, + widget.priority, + widget.image, + widget.updated.toIso8601String(), + widget.id, + ])); + } + + Future addWidget( + int categoryId, + int widgetId, + ) async { + String addSql = "INSERT INTO " + "category_widget(widgetId,categoryId) " + "VALUES (?,?);"; + return await database + .transaction((tran) async => await tran.rawInsert(addSql, [ + widgetId, + categoryId, + ])); + } + + Future addWidgets(int categoryId, List widgetIds) async { + String addSql = "INSERT INTO " + "category_widget(widgetId,categoryId) VALUES "; + String args = ''; + for (int i = 0; i < widgetIds.length; i++) { + args += "(${widgetIds[i]},$categoryId)"; + if (i == widgetIds.length - 1) { + args += ";"; + } else { + args += ","; + } + } + addSql += args; + return await database + .transaction((tran) async => await tran.rawInsert(addSql)); + } + + Future existByName(String name) async { + String sql = "SELECT COUNT(name) as count FROM category " + "WHERE name = ?"; + List> rawData = await database.rawQuery(sql, [name]); + if (rawData.isNotEmpty) { + return rawData[0]['count'] > 0; + } + return false; + } + + Future>> queryAll() async { + List> data = await database.rawQuery( + "SELECT c.id,c.name,c.info,c.color,c.image,c.created,c.updated,c.priority,COUNT(cw.categoryId) as `count`" + "FROM category AS c " + "LEFT JOIN category_widget AS cw " + "ON c.id = cw.categoryId GROUP BY c.id " + "ORDER BY priority DESC,created DESC", + []); + return data; + } + + Future> categoryWidgetIds(int id) async { + List> data = await database.rawQuery( + "SELECT categoryId FROM `category_widget`" + "WHERE widgetId = ?", + [id]); + return data.toList().map((e) => e["categoryId"]).toList(); + } + + Future deleteCollect(int id) async { + await database.execute( + "DELETE FROM category_widget " + "WHERE categoryId = ?", + [id]); + return await database.execute( + "DELETE FROM category " + "WHERE id = ?", + [id]); + } + + Future clear() async { + await database.execute("DELETE FROM category_widget " + "WHERE categoryId >0"); + return await database.execute("DELETE FROM category " + "WHERE id > 0"); + } + + Future removeWidget(int categoryId, int widgetId) async { + String deleteSql = "DELETE FROM " + "category_widget WHERE categoryId = ? AND widgetId = ? "; + return await database + .transaction((tran) async => await tran.rawInsert(deleteSql, [ + categoryId, + widgetId, + ])); + } + + Future existWidgetInCollect(int categoryId, int widgetId) async { + String sql = "SELECT COUNT(id) as count FROM category_widget " + "WHERE categoryId = ? AND widgetId = ?"; + List> rawData = + await database.rawQuery(sql, [categoryId, widgetId]); + if (rawData.isNotEmpty) { + return rawData[0]['count'] > 0; + } + return false; + } + + Future toggleCollect(int categoryId, int widgetId) async { + if (await existWidgetInCollect(categoryId, widgetId)) { + await removeWidget(categoryId, widgetId); + } else { + await addWidget(categoryId, widgetId); + } + } + + Future toggleCollectDefault(int widgetId) async { + await toggleCollect(1, widgetId); + } + + Future>> loadCollectWidgets(int categoryId, [String locale = 'zh-cn']) async { + String querySql = + "SELECT w.*, wd.name as localeName, wd.info as info " + "FROM widget w " + "LEFT JOIN widget_desc wd ON w.id = wd.widget_id AND wd.locale = ? " + "WHERE w.id IN (SELECT widgetId FROM category_widget WHERE categoryId = ?) " + "ORDER BY w.lever DESC"; + + return await database.rawQuery(querySql, [locale, categoryId]); + } + + Future> loadCollectWidgetIds(int categoryId) async { + String querySql = + "SELECT id FROM widget " + "WHERE id IN (SELECT widgetId FROM category_widget WHERE categoryId = ?) " + "ORDER BY lever DESC"; + + var data = await database.rawQuery(querySql, [categoryId]); + return data.map((e) => e["id"] as int).toList(); + } +} diff --git a/modules/widget_system/widget_repository/lib/src/database/dao/like_dao.dart b/modules/widget_system/widget_repository/lib/src/database/dao/like_dao.dart new file mode 100644 index 000000000..210a207f9 --- /dev/null +++ b/modules/widget_system/widget_repository/lib/src/database/dao/like_dao.dart @@ -0,0 +1,79 @@ +import 'package:fx_dao/fx_dao.dart'; + +class LikeDao extends Dao { + @override + String get createSql => ''; + + @override + String get name => 'like_widget'; + + Future> likeWidgetIds() async { + String sql = """ +SELECT cw.widgetId as widget_id +FROM category c +JOIN category_widget cw ON c.id = cw.categoryId +WHERE c.type = 1; + """; + var result = await database.rawQuery(sql); + var ids = result.map((e) => e['widget_id'] as int).toList(); + return ids; + } + + // 如果已喜欢,取消喜欢 + // 如果未喜欢,设为喜欢 + Future toggleCollect(int widgetId) async { + bool liked = await isLiked(widgetId); + + if (liked) { + await unlike(widgetId, check: false); + } else { + await like(widgetId, check: false); + } + } + + Future like(int widgetId, {bool check = true}) async { + if (check) { + // 如果 liked_widget_bloc ,直接取消,不执行 liked_widget_bloc 操作 + bool liked = await isLiked(widgetId); + if (liked) return 0; + } + String sql = """ +INSERT INTO category_widget (categoryId, widgetId) +SELECT id, ? FROM category WHERE type = 1; + """; + + return await database.rawInsert(sql, [widgetId]); + } + + Future unlike(int widgetId, {bool check = true}) async { + if (check) { + // 如果未 liked_widget_bloc ,直接取消,不执行 unlike 操作 + bool liked = await isLiked(widgetId); + if (!liked) return; + } + String sql = """ +DELETE FROM category_widget +WHERE categoryId IN (SELECT id FROM category WHERE type = 1) +AND widgetId = ?; + """; + await database.execute(sql, [widgetId]); + } + + // 判断组件是否已 liked + Future isLiked(int widgetId) async { + String sql = """ +SELECT EXISTS( + SELECT 1 + FROM category c + JOIN category_widget cw ON c.id = cw.categoryId + WHERE c.type = 1 AND cw.widgetId = ? +) as is_liked; + """; + var data = await database.rawQuery(sql, [widgetId]); + if (data.isNotEmpty) { + var result = data[0]; + return result['is_liked'] != 0; + } + return false; + } +} diff --git a/modules/widget_system/widget_repository/lib/src/database/dao/node_dao.dart b/modules/widget_system/widget_repository/lib/src/database/dao/node_dao.dart new file mode 100644 index 000000000..2e6c04b8b --- /dev/null +++ b/modules/widget_system/widget_repository/lib/src/database/dao/node_dao.dart @@ -0,0 +1,47 @@ +import 'package:fx_dao/fx_dao.dart'; + +import '../po/node_po.dart'; + +class NodeDao extends Dao { + @override + String get createSql => ''; + + @override + String get name => 'node'; + + Future insert(NodePo widget) async { + String addSql = "INSERT INTO " + "node(widgetId,name,priority,subtitle,code) " + "VALUES (?,?,?,?,?);"; + return await database.transaction((tran) async => await tran.rawInsert( + addSql, [ + widget.widgetId, + widget.name, + widget.priority, + widget.subtitle, + widget.code + ])); + } + + Future>> queryAll() async => + database.rawQuery("SELECT * FROM node"); + + //根据 id 查询组件 node + Future>> queryById(int id, {String? locale}) async { + String sql = """ +SELECT + node.priority, + node.code, + node_desc.name, + node_desc.subtitle +FROM node +INNER JOIN node_desc + ON node.id = node_desc.node_id +WHERE node_desc.locale = ? +AND node.widgetId = ? +ORDER BY priority +"""; + + return await database.rawQuery(sql, [locale ?? 'zh-cn', id]); + } +} diff --git a/modules/widget_system/widget_repository/lib/src/database/dao/widget_dao.dart b/modules/widget_system/widget_repository/lib/src/database/dao/widget_dao.dart new file mode 100644 index 000000000..337af7b4d --- /dev/null +++ b/modules/widget_system/widget_repository/lib/src/database/dao/widget_dao.dart @@ -0,0 +1,138 @@ +import 'package:fx_dao/fx_dao.dart'; +import '../../model/widget_filter.dart'; + +class WidgetDao extends Dao { + @override + String get createSql => ''; + + @override + String get name => 'widget'; + + Future>> queryByIds( + List ids, String? locale) async { + if (ids.isEmpty) { + return []; + } + String searchSql = """ +SELECT + widget.id, + widget.name, + widget.family, + widget.linkWidget, + widget.lever, + widget_desc.name AS nameCN, + widget_desc.info +FROM widget +INNER JOIN widget_desc + ON widget.id = widget_desc.widget_id +WHERE + widget_desc.locale = ? AND + widget.id in (${'?,' * (ids.length - 1)}?) +ORDER BY + lever DESC, widget.id ASC; + """; + + return database.rawQuery(searchSql, [(locale ?? 'zh-cn'), ...ids]); + } + + Future>> search(WidgetFilter arguments) async { + // 保证 name 参数为空时,不进行搜索 + if (arguments.name.isEmpty) { + return []; + } + + // * 表示 name 任意 + String name = arguments.name == '*' ? '' : arguments.name; + bool hasFamily = arguments.family != null; + String familySql = hasFamily ? ' AND family = ?' : ''; + List familyArg = hasFamily ? [arguments.family!.index] : []; + List starArg = arguments.stars; + if (arguments.stars.reduce((a, b) => a + b) == -5) { + starArg = [1, 2, 3, 4, 5]; + } + String searchSql = """ +SELECT + widget.id, + widget.name, + widget.family, + widget.linkWidget, + widget.lever, + widget_desc.name AS nameCN, + widget_desc.info +FROM widget +INNER JOIN widget_desc + ON widget.id = widget_desc.widget_id +WHERE +widget_desc.locale = ? +AND (widget.name LIKE ? OR widget_desc.info LIKE ? OR widget_desc.name LIKE ?) +$familySql +AND lever IN(?,?,?,?,?) +ORDER BY + lever DESC, widget.id ASC +LIMIT ? OFFSET ? +; + """; + + return database.rawQuery(searchSql, [ + arguments.locale ?? 'zh-cn', + "%$name%", + "%$name%", + "%$name%", + ...familyArg, + ...starArg, + arguments.pageSize, + arguments.offset + ]); + } + + Future total(WidgetFilter args) async { + bool hasFamily = args.family != null; + String familySql = hasFamily ? 'family = ?' : ''; + List familyArg = hasFamily ? [args.family!.index] : []; + + String sql = "SELECT count(id) as `count` FROM widget WHERE $familySql"; + + List> result = await database.rawQuery(sql, familyArg); + if (result.isNotEmpty) { + return result.first['count'] as int ?? 0; + } + return 0; + } + + Future?> queryWidgetByName(String name, + {String? locale}) async { + String querySql = """ +SELECT + widget.id, + widget.name, + widget.family, + widget.linkWidget, + widget.lever, + widget_desc.name AS nameCN, + widget_desc.info +FROM widget +INNER JOIN widget_desc + ON widget.id = widget_desc.widget_id +WHERE +widget.name = ? AND +widget_desc.locale = ? +; +"""; + List> result = + await database.rawQuery(querySql, [name, locale ?? 'zh-cn']); + if (result.isNotEmpty) { + return result.first; + } + return null; + } + + Future>> queryWidgetFields(int widgetId) async { + String sql = """ +SELECT * +FROM widget_fields +WHERE widget_id = ? +ORDER BY is_required DESC, field_order ASC +"""; + return database.rawQuery(sql, [widgetId]); + } +} diff --git a/modules/widget_system/widget_repository/lib/src/database/dao/widget_statistics_dao.dart b/modules/widget_system/widget_repository/lib/src/database/dao/widget_statistics_dao.dart new file mode 100644 index 000000000..0b9adfa69 --- /dev/null +++ b/modules/widget_system/widget_repository/lib/src/database/dao/widget_statistics_dao.dart @@ -0,0 +1,93 @@ +import 'package:fx_dao/fx_dao.dart'; + +class WidgetStatisticsDao extends Dao { + @override + String get createSql => ''; + + @override + String get name => 'widget_statistics'; + + /// 获取各Family对应的组件数量 + Future> getFamilyCount() async { + String sql = ''' + SELECT family, COUNT(*) as count + FROM widget + GROUP BY family + '''; + + List> result = await database.rawQuery(sql); + Map familyCount = {}; + + for (var row in result) { + familyCount[row['family'] as int] = row['count'] as int; + } + + return familyCount; + } + + /// 获取总组件数量 + Future getTotalWidgets() async { + String sql = 'SELECT COUNT(*) as count FROM widget'; + List> result = await database.rawQuery(sql); + return result.first['count'] as int; + } + + /// 获取总字段数量 + Future getTotalFields() async { + String sql = 'SELECT COUNT(*) as count FROM widget_fields'; + List> result = await database.rawQuery(sql); + return result.first['count'] as int; + } + + /// 获取星级分布 + Future> getLeverDistribution() async { + String sql = ''' + SELECT CAST(lever as INTEGER) as lever_int, COUNT(*) as count + FROM widget + GROUP BY CAST(lever as INTEGER) + '''; + + List> result = await database.rawQuery(sql); + Map leverDistribution = {}; + + for (var row in result) { + leverDistribution[row['lever_int'] as int] = row['count'] as int; + } + + return leverDistribution; + } + + /// 获取平均字段数量 + Future getAverageFields() async { + String sql = ''' + SELECT AVG(field_count) as avg_fields + FROM ( + SELECT widget_id, COUNT(*) as field_count + FROM widget_fields + GROUP BY widget_id + ) + '''; + + List> result = await database.rawQuery(sql); + var avgFields = result.first['avg_fields']; + return avgFields != null ? (avgFields as num).toDouble() : 0.0; + } + + /// 获取每个组件的属性个数 + Future> getWidgetFieldsCount() async { + String sql = ''' + SELECT widget_id, COUNT(*) as field_count + FROM widget_fields + GROUP BY widget_id + '''; + + List> result = await database.rawQuery(sql); + Map fieldsCount = {}; + + for (var row in result) { + fieldsCount[row['widget_id'] as int] = row['field_count'] as int; + } + + return fieldsCount; + } +} \ No newline at end of file diff --git a/modules/widget_system/widget_repository/lib/src/database/database.dart b/modules/widget_system/widget_repository/lib/src/database/database.dart new file mode 100644 index 000000000..13eea01a5 --- /dev/null +++ b/modules/widget_system/widget_repository/lib/src/database/database.dart @@ -0,0 +1,13 @@ +export 'po/category_po.dart'; +export 'po/node_po.dart'; +export 'po/widget_po.dart'; + +export 'dao/like_dao.dart'; +export 'dao/node_dao.dart'; +export 'dao/widget_dao.dart'; +export 'dao/category_dao.dart'; +export 'dao/widget_statistics_dao.dart'; + +export 'db_impl/category_db_repository.dart'; +export 'db_impl/node_db_repository.dart'; +export 'db_impl/widget_db_repository.dart'; \ No newline at end of file diff --git a/modules/widget_system/widget_repository/lib/src/database/db_impl/category_db_repository.dart b/modules/widget_system/widget_repository/lib/src/database/db_impl/category_db_repository.dart new file mode 100644 index 000000000..cb94f5f14 --- /dev/null +++ b/modules/widget_system/widget_repository/lib/src/database/db_impl/category_db_repository.dart @@ -0,0 +1,112 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:storage/storage.dart'; +import 'package:widget_repository/widget_repository.dart'; + +/// create by 张风捷特烈 on 2020-04-21 +/// contact me by email 1981462002@qq.com +/// 说明: + +class CategoryDbRepository implements CategoryRepository { + CategoryDao get categoryDao => AppStorage().flutter(); + + LikeDao get likeDao => AppStorage().flutter(); + + @override + Future addCategory(CategoryPo categoryPo) async { + int success = await categoryDao.insert(categoryPo); + return success != -1; + } + + @override + Future check(int categoryId, int widgetId) async { + return await categoryDao.existWidgetInCollect(categoryId, widgetId); + } + + @override + Future deleteCategory(int id) async { + await categoryDao.deleteCollect(id); + } + + @override + Future> loadCategories() async { + List> data = await categoryDao.queryAll(); + List collects = + data.map((e) => CategoryPo.fromJson(e)).toList(); + return collects.map(CategoryModel.fromPo).toList(); + } + + @override + Future> loadCategoryWidgets( + {int categoryId = 0, String locale = 'zh-cn'}) async { + List> rawData = + await categoryDao.loadCollectWidgets(categoryId, locale); + List widgets = rawData.map((e) => WidgetPo.fromJson(e)).toList(); + return widgets.map(WidgetModel.fromPo).toList(); + } + + @override + Future toggleCategory(int categoryId, int widgetId) async { + return await categoryDao.toggleCollect(categoryId, widgetId); + } + + @override + Future> getCategoryByWidget(int widgetId) async { + return await categoryDao.categoryWidgetIds(widgetId); + } + + @override + Future updateCategory(CategoryPo categoryPo) async { + int success = await categoryDao.update(categoryPo); + return success != -1; + } + + @override + Future> loadCategoryData() async { + List> data = await categoryDao.queryAll(); + + Completer> completer = Completer(); + List collects = []; + + if (data.isEmpty) { + completer.complete([]); + } + + for (int i = 0; i < data.length; i++) { + List ids = await categoryDao.loadCollectWidgetIds(data[i]['id']); + collects + .add(CategoryTo(widgetIds: ids, model: CategoryPo.fromJson(data[i]))); + if (i == data.length - 1) { + completer.complete(collects); + } + } + + return completer.future; + } + + @override + Future syncCategoryByData(String data, String likeData) async { + try { + await categoryDao.clear(); + List dataMap = json.decode(data); + for (int i = 0; i < dataMap.length; i++) { + CategoryPo po = CategoryPo.fromNetJson(dataMap[i]["model"]); + List widgetIds = dataMap[i]["widgetIds"]; + await addCategory(po); + if (widgetIds.isNotEmpty && po.id != null) { + await categoryDao.addWidgets(po.id!, widgetIds); + } + } + List likeWidgets = + (json.decode(likeData) as List).map((e) => e).toList(); + for (int i = 0; i < likeWidgets.length; i++) { + await likeDao.like(likeWidgets[i]); + } + return true; + } catch (e) { + print(e); + return false; + } + } +} diff --git a/modules/widget_system/widget_repository/lib/src/database/db_impl/node_db_repository.dart b/modules/widget_system/widget_repository/lib/src/database/db_impl/node_db_repository.dart new file mode 100644 index 000000000..617d8ee68 --- /dev/null +++ b/modules/widget_system/widget_repository/lib/src/database/db_impl/node_db_repository.dart @@ -0,0 +1,20 @@ +import 'package:storage/storage.dart'; +import 'package:widget_repository/widget_repository.dart'; + +class NodeDbRepository implements NodeRepository { + const NodeDbRepository(); + + NodeDao get nodeDao => AppStorage().flutter(); + + @override + Future> loadNode(int widgetId, {String? locale}) async { + List> data = + await nodeDao.queryById(widgetId, locale: locale); + List nodes = []; + for (int i = 0; i < data.length; i++) { + Map e = data[i]; + nodes.add(NodeModel.fromJson(e, i)); + } + return nodes; + } +} diff --git a/modules/widget_system/widget_repository/lib/src/database/db_impl/widget_db_repository.dart b/modules/widget_system/widget_repository/lib/src/database/db_impl/widget_db_repository.dart new file mode 100644 index 000000000..e132cb68d --- /dev/null +++ b/modules/widget_system/widget_repository/lib/src/database/db_impl/widget_db_repository.dart @@ -0,0 +1,73 @@ +import 'package:storage/storage.dart'; +import 'package:widget_repository/widget_repository.dart'; + +import '../../model/widget_filter.dart'; +import '../../repository/widget_repository.dart'; + + +/// create by 张风捷特烈 on 2020-03-03 +/// contact me by email 1981462002@qq.com +/// 说明 : Widget数据仓库 + +class WidgetDbRepository implements WidgetRepository { + const WidgetDbRepository(); + + WidgetDao get widgetDao => AppStorage().flutter(); + + LikeDao get likeDao => AppStorage().flutter(); + + @override + Future> loadLikeWidgets() async { + // return []; + List likeIds = await likeDao.likeWidgetIds(); + List> data = await widgetDao.queryByIds(likeIds, null); + List widgets = data.map((e) => WidgetPo.fromJson(e)).toList(); + return widgets.map(WidgetModel.fromPo).toList(); + } + + @override + Future> searchWidgets(WidgetFilter args) async { + List> data = await widgetDao.search(args); + List widgets = data.map((e) => WidgetPo.fromJson(e)).toList(); + return widgets.map(WidgetModel.fromPo).toList(); + } + + @override + Future> loadWidget(List id,String? locale) async { + List> data = await widgetDao.queryByIds(id,locale); + List widgets = data.map((e) => WidgetPo.fromJson(e)).toList(); + if (widgets.isNotEmpty) return widgets.map(WidgetModel.fromPo).toList(); + return []; + } + + @override + Future toggleLike( + int id, + ) { + return likeDao.toggleCollect(id); + } + + @override + Future collected(int id) async { + return await likeDao.like(id); + } + + @override + Future total(WidgetFilter args) => widgetDao.total(args); + + @override + Future queryWidgetByName(String? name) async { + if (name == null) return null; + Map? data = await widgetDao.queryWidgetByName(name); + if (data != null) { + return WidgetModel.fromPo(WidgetPo.fromJson(data)); + } + return null; + } + + @override + Future> loadWidgetFields(int widgetId) async { + List> data = await widgetDao.queryWidgetFields(widgetId); + return data.map((e) => WidgetFieldModel.fromJson(e)).toList(); + } +} diff --git a/packages/storage/lib/src/db_storage/models/category_po.dart b/modules/widget_system/widget_repository/lib/src/database/po/category_po.dart similarity index 82% rename from packages/storage/lib/src/db_storage/models/category_po.dart rename to modules/widget_system/widget_repository/lib/src/database/po/category_po.dart index 6046d5d8b..0a0177115 100644 --- a/packages/storage/lib/src/db_storage/models/category_po.dart +++ b/modules/widget_system/widget_repository/lib/src/database/po/category_po.dart @@ -1,19 +1,20 @@ + /// create by 张风捷特烈 on 2020-04-17 /// contact me by email 1981462002@qq.com /// 说明: 收藏夹数据库-数据模型 - -// """ -// CREATE TABLE IF NOT EXISTS category( -// id INTEGER PRIMARY KEY AUTOINCREMENT, -// name VARCHAR(64) NOT NULL, -// color VARCHAR(9) DEFAULT '#FF2196F3', -// info VARCHAR(256) DEFAULT '这里什么都没有...', -// created DATETIME NOT NULL, -// updated DATETIME NOT NULL, -// priority INTEGER DEFAULT 0, -// image VARCHAR(128) NULL image DEFAULT '' -// );"""; //建表语句 - +/// +/// """ +/// CREATE TABLE IF NOT EXISTS category( +/// id INTEGER PRIMARY KEY AUTOINCREMENT, +/// name VARCHAR(64) NOT NULL, +/// color VARCHAR(9) DEFAULT '#FF2196F3', +/// info VARCHAR(256) DEFAULT '这里什么都没有...', +/// created DATETIME NOT NULL, +/// updated DATETIME NOT NULL, +/// priority INTEGER DEFAULT 0, +/// image VARCHAR(128) NULL image DEFAULT '' +/// );"""; //建表语句 +/// class CategoryPo { final int? id; final String name; diff --git a/packages/storage/lib/src/db_storage/models/node_po.dart b/modules/widget_system/widget_repository/lib/src/database/po/node_po.dart similarity index 97% rename from packages/storage/lib/src/db_storage/models/node_po.dart rename to modules/widget_system/widget_repository/lib/src/database/po/node_po.dart index 16bfd8176..f5198a1b5 100644 --- a/packages/storage/lib/src/db_storage/models/node_po.dart +++ b/modules/widget_system/widget_repository/lib/src/database/po/node_po.dart @@ -22,7 +22,7 @@ class NodePo { factory NodePo.fromJson(Map map) { return NodePo( - id: map['id'], + id: map['id']??0, name: map['name'], widgetId: map["widgetId"], priority: map["priority"], diff --git a/modules/widget_system/widget_repository/lib/src/database/po/widget_po.dart b/modules/widget_system/widget_repository/lib/src/database/po/widget_po.dart new file mode 100644 index 000000000..cfea36dc1 --- /dev/null +++ b/modules/widget_system/widget_repository/lib/src/database/po/widget_po.dart @@ -0,0 +1,71 @@ +/// create by 张风捷特烈 on 2020-03-04 +/// contact me by email 1981462002@qq.com +/// 说明: 组件信息-数据库-数据模型 +/// +class WidgetPo { + final int id; + final String name; + final String nameCN; + final int deprecated; + final int family; + final double lever; + final String info; + final String linkWidget; + + const WidgetPo({ + required this.id, + required this.name, + required this.nameCN, + required this.deprecated, + required this.family, + required this.lever, + required this.linkWidget, + required this.info, + }); + + factory WidgetPo.fromJson(Map map) { + return WidgetPo( + id: map['id'], + name: map['name'], + nameCN: map["nameCN"] ?? map['localeName'] ?? '', + family: map["family"] ?? '', + deprecated: map["deprecated"] ?? 0, + lever: map["lever"].toDouble(), + linkWidget: map["linkWidget"] ?? '', + info: map["info"] ?? ''); + } + + factory WidgetPo.fromDesc(Map map) { + return WidgetPo( + id: map['id'], + name: map['name'], + nameCN: map["localName"], + family: map["family"], + lever: map["lever"].toDouble(), + linkWidget: map["linkIds"].join(','), + deprecated: map["deprecated"] ?? 0, + info: map["info"]); + } + + Map toJson() { + return { + "id": id, + "name": name, + "nameCN": nameCN, + "family": family, + "deprecated": deprecated, + "lever": lever, + "linkWidget": linkWidget, + "info": info + }; + } + + @override + String toString() { + return 'WidgetPo{id: $id, name: $name, nameCN: $nameCN, deprecated: $deprecated, family: $family, lever: $lever, info: $info}'; + } + + @override + List get props => + [id, name, nameCN, deprecated, family, linkWidget, lever, info]; +} diff --git a/modules/widget_system/widget_repository/lib/src/memory/memory_node_repository.dart b/modules/widget_system/widget_repository/lib/src/memory/memory_node_repository.dart new file mode 100644 index 000000000..02f24ba94 --- /dev/null +++ b/modules/widget_system/widget_repository/lib/src/memory/memory_node_repository.dart @@ -0,0 +1,39 @@ +import 'dart:convert'; +import 'package:flutter/services.dart'; +import 'package:widget_repository/widget_repository.dart'; + +import '../repository/node_repository.dart'; + +/// create by 张风捷特烈 on 2020-03-03 +/// contact me by email 1981462002@qq.com +/// 说明 : Node 数据仓库 + +class MemoryNodeRepository implements NodeRepository { + List? _nodeCache; + + MemoryNodeRepository(); + + Future initData() async { + if (_nodeCache != null) { + return; + } + ByteData byteData = await rootBundle.load('assets/data/web/node.json'); + String content = utf8.decode(byteData.buffer.asUint8List()); + var data = json.decode(content) as List; + _nodeCache = data.map((e) => NodePo.fromJson(e)).toList(); + } + + @override + Future> loadNode(int widgetId,{String? locale}) async { + await initData(); + return _nodeCache! + .where((element) => element.widgetId == widgetId) + .map((e) => NodeModel( + name: e.name, + subtitle: e.subtitle, + code: e.code, + priority: e.priority, + )) + .toList(); + } +} diff --git a/modules/widget_system/widget_repository/lib/src/memory/memory_widget_repository.dart b/modules/widget_system/widget_repository/lib/src/memory/memory_widget_repository.dart new file mode 100644 index 000000000..fa1993a34 --- /dev/null +++ b/modules/widget_system/widget_repository/lib/src/memory/memory_widget_repository.dart @@ -0,0 +1,91 @@ +import 'dart:convert'; +import 'package:flutter/services.dart'; +import 'package:widget_repository/widget_repository.dart'; + +import '../model/widget_filter.dart'; +import '../repository/widget_repository.dart'; + +/// create by 张风捷特烈 on 2020-03-03 +/// contact me by email 1981462002@qq.com +/// 说明 : Widget 数据仓库 + +class MemoryWidgetRepository implements WidgetRepository { + List? _widgetCache; + + List get widgets => _widgetCache!; + + MemoryWidgetRepository(); + + Future _initData() async { + if (_widgetCache != null) { + return; + } + ByteData byteData = await rootBundle.load('assets/data/web/widget.json'); + String content = utf8.decode(byteData.buffer.asUint8List()); + var data = json.decode(content) as List; + _widgetCache = data.map((e) => WidgetPo.fromJson(e)).toList(); + } + + @override + Future> searchWidgets(WidgetFilter args) async { + await _initData(); + var result = widgets + .map(WidgetModel.fromPo) + .where((e) => checkSearch(e, args)) + .toList(); + result.sort((a, b) => b.lever.compareTo(a.lever)); + return result; + } + + bool checkSearch(WidgetModel model, WidgetFilter args) { + bool nameMatch = model.name.toLowerCase().contains(args.name.toLowerCase()); + bool nameCNMatch = + model.nameCN.toLowerCase().contains(args.name.toLowerCase()); + bool familyMatch = model.family == args.family; + + return nameMatch || nameCNMatch || familyMatch; + } + + @override + Future> loadWidget(List id, String? locale) async { + await _initData(); + var data = widgets.where((element) => id.contains(element.id)); + return data.map(WidgetModel.fromPo).toList(); + } + + @override + Future> loadLikeWidgets() async { + return []; + } + + @override + Future queryWidgetByName(String? name) async { + await _initData(); + Iterable ret = widgets + .map(WidgetModel.fromPo) + .where((element) => element.name == name); + if (ret.isNotEmpty) { + return ret.first; + } + return null; + } + + @override + Future toggleLike(int id) async {} + + @override + Future total(WidgetFilter args) async { + return 0; + } + + @override + Future collected(int id) async { + return 0; + } + + @override + Future> loadWidgetFields(int widgetId) { + // TODO: implement loadWidgetFields + throw UnimplementedError(); + } +} diff --git a/packages/widget_repository/lib/src/model/category_model.dart b/modules/widget_system/widget_repository/lib/src/model/category_model.dart similarity index 91% rename from packages/widget_repository/lib/src/model/category_model.dart rename to modules/widget_system/widget_repository/lib/src/model/category_model.dart index 47ff50965..790267bb0 100644 --- a/packages/widget_repository/lib/src/model/category_model.dart +++ b/modules/widget_system/widget_repository/lib/src/model/category_model.dart @@ -1,11 +1,15 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; - - import 'package:intl/intl.dart'; import 'package:utils/utils.dart'; -import 'package:storage/storage.dart'; +import '../database/po/category_po.dart'; + + +// import 'package:intl/intl.dart'; +// import 'package:utils/utils.dart'; +// +// import 'package:storage/storage.dart'; /// create by 张风捷特烈 on 2020-04-21 diff --git a/modules/widget_system/widget_repository/lib/src/model/model.dart b/modules/widget_system/widget_repository/lib/src/model/model.dart new file mode 100644 index 000000000..900537b01 --- /dev/null +++ b/modules/widget_system/widget_repository/lib/src/model/model.dart @@ -0,0 +1,6 @@ +export 'node_model.dart'; +export 'widget_model.dart'; +export 'category_model.dart'; +export 'widget_filter.dart'; +export 'widget_statistics.dart'; +export 'widget_field_model.dart'; \ No newline at end of file diff --git a/modules/widget_system/widget_repository/lib/src/model/node_model.dart b/modules/widget_system/widget_repository/lib/src/model/node_model.dart new file mode 100644 index 000000000..4cee384a5 --- /dev/null +++ b/modules/widget_system/widget_repository/lib/src/model/node_model.dart @@ -0,0 +1,53 @@ +import 'package:equatable/equatable.dart'; + +/// create by 张风捷特烈 on 2020-03-04 +/// contact me by email 1981462002@qq.com +/// 说明: 详情页节点-展示-数据模型 +/// + +enum NodeType { + display, + newPage, + description, + deprecated, +} + +class NodeModel extends Equatable { + final String name; + final String subtitle; + final String code; + final int priority; + + const NodeModel({ + required this.name, + required this.subtitle, + required this.code, + required this.priority, + }); + + @override + List get props => [name, subtitle, code, priority]; + + NodeType type(String widget, int priority) { + if (widget == 'PinnedHeaderSliver') { + return NodeType.newPage; + } + if (widget == 'NavigationRail') { + return NodeType.newPage; + } + return NodeType.display; + } + + factory NodeModel.fromJson(Map map, int index) { + return NodeModel( + name: map['name'], + subtitle: map["subtitle"], + code: map["code"], + priority: index); + } + + @override + String toString() { + return 'Node{name: $name, subtitle: $subtitle, code: $code}'; + } +} diff --git a/modules/widget_system/widget_repository/lib/src/model/widget_field_model.dart b/modules/widget_system/widget_repository/lib/src/model/widget_field_model.dart new file mode 100644 index 000000000..6d41090ee --- /dev/null +++ b/modules/widget_system/widget_repository/lib/src/model/widget_field_model.dart @@ -0,0 +1,49 @@ +import 'package:equatable/equatable.dart'; + +/// Widget字段模型 +class WidgetFieldModel extends Equatable { + final int id; + final int widgetId; + final String fieldName; + final String fieldType; + final String? fieldDesc; + final String? fieldDescZh; + final int fieldOrder; + final bool isRequired; + + const WidgetFieldModel({ + required this.id, + required this.widgetId, + required this.fieldName, + required this.fieldType, + this.fieldDesc, + this.fieldDescZh, + required this.fieldOrder, + required this.isRequired, + }); + + @override + List get props => [ + id, + widgetId, + fieldName, + fieldType, + fieldDesc, + fieldDescZh, + fieldOrder, + isRequired, + ]; + + factory WidgetFieldModel.fromJson(Map json) { + return WidgetFieldModel( + id: json['id'], + widgetId: json['widget_id'], + fieldName: json['field_name'], + fieldType: json['field_type'], + fieldDesc: json['field_desc'], + fieldDescZh: json['field_desc_zh'], + fieldOrder: json['field_order'], + isRequired: json['is_required'] == 1, + ); + } +} \ No newline at end of file diff --git a/packages/widget_repository/lib/src/model/widget_filter.dart b/modules/widget_system/widget_repository/lib/src/model/widget_filter.dart similarity index 78% rename from packages/widget_repository/lib/src/model/widget_filter.dart rename to modules/widget_system/widget_repository/lib/src/model/widget_filter.dart index 71f36d132..3482da8e4 100644 --- a/packages/widget_repository/lib/src/model/widget_filter.dart +++ b/modules/widget_system/widget_repository/lib/src/model/widget_filter.dart @@ -1,9 +1,17 @@ -import 'enums.dart'; - +enum WidgetFamily { + stateless, + stateful, + singleChildRender, + multiChildRender, + sliver, + proxy, + other, +} class WidgetFilter { final String name; + final String? locale; final WidgetFamily? family; final List stars; final int page; @@ -11,6 +19,7 @@ class WidgetFilter { const WidgetFilter({ this.name = '', + this.locale = 'zh-cn', this.family, this.stars = const [-1, -1, -1, -1, -1], this.page = 1, @@ -23,6 +32,7 @@ class WidgetFilter { this.family, { this.name = '*', this.page = 1, + this.locale = 'zh-cn', this.pageSize = 20, this.stars = const [1, 2, 3, 4, 5], }); @@ -32,12 +42,14 @@ class WidgetFilter { WidgetFamily? family, List? stars, int? page, + String? locale, }) { return WidgetFilter( name: name ?? this.name, family: family ?? this.family, stars: stars ?? this.stars, page: page ?? this.page, + locale: locale ?? this.locale, ); } diff --git a/modules/widget_system/widget_repository/lib/src/model/widget_model.dart b/modules/widget_system/widget_repository/lib/src/model/widget_model.dart new file mode 100644 index 000000000..7c422fb16 --- /dev/null +++ b/modules/widget_system/widget_repository/lib/src/model/widget_model.dart @@ -0,0 +1,110 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter/cupertino.dart'; + +import '../../widget_repository.dart'; +import '../database/po/widget_po.dart'; + +/// create by 张风捷特烈 on 2020-03-04 +/// contact me by email 1981462002@qq.com +/// 说明: 组件信息-展示-数据模型 +/// + +const List kTabColors = [ + Color(0xff44D1FD), + Color(0xffFD4F43), + Color(0xffB375FF), + Color(0xFF4CAF50), + Color(0xFFFF9800), + Color(0xFF00F1F1), + Color(0xFFDBD83F), +]; + +class WidgetModel extends Equatable { + final int id; + final String name; + final String nameCN; + final WidgetFamily family; + final bool deprecated; + final bool death; + final List links; + final double lever; + final ImageProvider? image; + final String info; + + String get heroId => 'hero_widget_image_$id'; + + const WidgetModel({ + required this.id, + required this.name, + required this.nameCN, + required this.family, + this.deprecated = false, + this.death = false, + required this.links, + // required this.type, + required this.lever, + this.image, + required this.info, + }); + + @override + List get props => [id]; + + Color get color => kTabColors[family.index]; + + static WidgetModel fromPo(WidgetPo po) { + return WidgetModel( + id: po.id, + name: po.name, + nameCN: po.nameCN, + family: toFamily(po.family), + image: convertImage(po.name), + lever: po.lever, + deprecated: po.deprecated == 1, + death: po.deprecated == -1, + info: po.info, + links: formatLinkTo(po.linkWidget), + ); + } + + static convertImage(String name) { + // return image.isEmpty ? null : AssetImage(image); + return null; + } + + @override + String toString() { + return 'WidgetModel{id: $id, name: $name, nameCN: $nameCN, family: $family, deprecated: $deprecated, links: $links, lever: $lever, image: $image, info: $info}'; + } + + static List formatLinkTo(String links) { + if (links.isEmpty) { + return []; + } + if (!links.contains(',')) { + return [int.parse(links)]; + } + return links.split(',').map((e) => int.parse(e)).toList(); + } + + static WidgetFamily toFamily(int id) { + switch (id) { + case 0: + return WidgetFamily.stateless; + case 1: + return WidgetFamily.stateful; + case 2: + return WidgetFamily.singleChildRender; + case 3: + return WidgetFamily.multiChildRender; + case 4: + return WidgetFamily.sliver; + case 5: + return WidgetFamily.proxy; + case 6: + return WidgetFamily.other; + default: + return WidgetFamily.stateless; + } + } +} diff --git a/modules/widget_system/widget_repository/lib/src/model/widget_statistics.dart b/modules/widget_system/widget_repository/lib/src/model/widget_statistics.dart new file mode 100644 index 000000000..7b9d70b18 --- /dev/null +++ b/modules/widget_system/widget_repository/lib/src/model/widget_statistics.dart @@ -0,0 +1,52 @@ +import 'package:equatable/equatable.dart'; +import 'widget_filter.dart'; + +/// Widget统计数据模型 +class WidgetStatistics extends Equatable { + /// 各Family对应的组件数量 + final Map familyCount; + + /// 总组件数量 + final int totalWidgets; + + /// 总字段数量 + final int totalFields; + + /// 平均字段数量 + final double averageFields; + + /// 星级分布 (1-5星对应的组件数量) + final Map leverDistribution; + + /// 每个组件的属性个数 (widgetId -> 属性个数) + final Map widgetFieldsCount; + + const WidgetStatistics({ + required this.familyCount, + required this.totalWidgets, + required this.totalFields, + required this.averageFields, + required this.leverDistribution, + required this.widgetFieldsCount, + }); + + @override + List get props => [ + familyCount, + totalWidgets, + totalFields, + averageFields, + leverDistribution, + widgetFieldsCount, + ]; + + /// 获取指定组件的属性个数 + int getWidgetFieldCount(int widgetId) { + return widgetFieldsCount[widgetId] ?? 0; + } + + @override + String toString() { + return 'WidgetStatistics{familyCount: $familyCount, totalWidgets: $totalWidgets, totalFields: $totalFields, averageFields: $averageFields, leverDistribution: $leverDistribution, widgetFieldsCount: ${widgetFieldsCount.length} widgets}'; + } +} \ No newline at end of file diff --git a/packages/widget_repository/lib/src/category_repository.dart b/modules/widget_system/widget_repository/lib/src/repository/category_repository.dart similarity index 93% rename from packages/widget_repository/lib/src/category_repository.dart rename to modules/widget_system/widget_repository/lib/src/repository/category_repository.dart index 71f01bd99..473291905 100644 --- a/packages/widget_repository/lib/src/category_repository.dart +++ b/modules/widget_system/widget_repository/lib/src/repository/category_repository.dart @@ -1,6 +1,6 @@ -import 'model/model.dart'; -import 'package:storage/storage.dart'; +import '../database/po/category_po.dart'; +import '../model/model.dart'; /// create by 张风捷特烈 on 2020-04-21 /// contact me by email 1981462002@qq.com diff --git a/modules/widget_system/widget_repository/lib/src/repository/node_repository.dart b/modules/widget_system/widget_repository/lib/src/repository/node_repository.dart new file mode 100644 index 000000000..fe3ff0d07 --- /dev/null +++ b/modules/widget_system/widget_repository/lib/src/repository/node_repository.dart @@ -0,0 +1,12 @@ + +import '../model/model.dart'; + +abstract class NodeRepository{ + + Future> loadNode(int widgetId,{String? locale}); + +} + + + + diff --git a/modules/widget_system/widget_repository/lib/src/repository/repository.dart b/modules/widget_system/widget_repository/lib/src/repository/repository.dart new file mode 100644 index 000000000..e7464d8aa --- /dev/null +++ b/modules/widget_system/widget_repository/lib/src/repository/repository.dart @@ -0,0 +1,5 @@ +export 'category_repository.dart'; +export 'node_repository.dart'; +export 'widget_repository.dart'; +export 'widget_statistics_service.dart'; +export 'widget_statistics_provider.dart'; \ No newline at end of file diff --git a/modules/widget_system/widget_repository/lib/src/repository/widget_repository.dart b/modules/widget_system/widget_repository/lib/src/repository/widget_repository.dart new file mode 100644 index 000000000..9ffbfe3a9 --- /dev/null +++ b/modules/widget_system/widget_repository/lib/src/repository/widget_repository.dart @@ -0,0 +1,26 @@ + +import '../model/model.dart'; + +/// create by 张风捷特烈 on 2020-03-03 +/// contact me by email 1981462002@qq.com + +abstract class WidgetRepository { + // Future> loadWidgets(WidgetFamily family); + + Future> loadWidget(List ids,String? locale); + + Future queryWidgetByName(String? name); + + /// 根据 [WidgetFilter] 搜索 [WidgetModel] 列表 + Future> searchWidgets(WidgetFilter args); + + Future toggleLike(int id); + + Future total(WidgetFilter args); + + Future> loadLikeWidgets(); + + Future collected(int id); + + Future> loadWidgetFields(int widgetId); +} diff --git a/modules/widget_system/widget_repository/lib/src/repository/widget_statistics_provider.dart b/modules/widget_system/widget_repository/lib/src/repository/widget_statistics_provider.dart new file mode 100644 index 000000000..ac74cf978 --- /dev/null +++ b/modules/widget_system/widget_repository/lib/src/repository/widget_statistics_provider.dart @@ -0,0 +1,90 @@ +import 'package:flutter/foundation.dart'; +import '../../widget_repository.dart'; +import '../model/widget_statistics.dart'; +import 'widget_statistics_service.dart'; + +/// 全局统计数据提供器 +class WidgetStatisticsProvider extends ChangeNotifier { + static final WidgetStatisticsProvider _instance = + WidgetStatisticsProvider._internal(); + factory WidgetStatisticsProvider() => _instance; + WidgetStatisticsProvider._internal(); + + WidgetStatistics? _statistics; + bool _isLoaded = false; + + WidgetStatistics? get statistics => _statistics; + bool get isLoaded => _isLoaded; + + /// 加载统计数据 + Future loadStatistics(WidgetStatisticsDao dao) async { + if (_isLoaded) return; + + final stopwatch = Stopwatch()..start(); + try { + _statistics = await getStatistics(dao); + _isLoaded = true; + stopwatch.stop(); + debugPrint('统计数据加载耗时: ${stopwatch.elapsedMilliseconds} ms'); + notifyListeners(); + } catch (e) { + stopwatch.stop(); + debugPrint('加载统计数据失败: $e (耗时: ${stopwatch.elapsedMilliseconds} ms)'); + } + } + + /// 获取Widget统计数据 + Future getStatistics(WidgetStatisticsDao dao) async { + final results = await Future.wait([ + dao.getFamilyCount(), + dao.getTotalWidgets(), + dao.getTotalFields(), + dao.getAverageFields(), + dao.getLeverDistribution(), + dao.getWidgetFieldsCount(), + ]); + + final rawFamilyCount = results[0] as Map; + final totalWidgets = results[1] as int; + final totalFields = results[2] as int; + final averageFields = results[3] as double; + final leverDistribution = results[4] as Map; + final widgetFieldsCount = results[5] as Map; + + final familyCount = {}; + for (var entry in rawFamilyCount.entries) { + final family = _intToWidgetFamily(entry.key); + familyCount[family] = entry.value; + } + + return WidgetStatistics( + familyCount: familyCount, + totalWidgets: totalWidgets, + totalFields: totalFields, + averageFields: averageFields, + leverDistribution: leverDistribution, + widgetFieldsCount: widgetFieldsCount, + ); + } + + WidgetFamily _intToWidgetFamily(int index) { + switch (index) { + case 0: + return WidgetFamily.stateless; + case 1: + return WidgetFamily.stateful; + case 2: + return WidgetFamily.singleChildRender; + case 3: + return WidgetFamily.multiChildRender; + case 4: + return WidgetFamily.sliver; + case 5: + return WidgetFamily.proxy; + case 6: + return WidgetFamily.other; + default: + return WidgetFamily.stateless; + } + } +} diff --git a/modules/widget_system/widget_repository/lib/src/repository/widget_statistics_service.dart b/modules/widget_system/widget_repository/lib/src/repository/widget_statistics_service.dart new file mode 100644 index 000000000..f5812e18c --- /dev/null +++ b/modules/widget_system/widget_repository/lib/src/repository/widget_statistics_service.dart @@ -0,0 +1,57 @@ +import '../model/widget_statistics.dart'; +import '../database/dao/widget_statistics_dao.dart'; +import '../model/widget_filter.dart'; + +/// Widget统计服务 +class WidgetStatisticsService { + final WidgetStatisticsDao dao; + + const WidgetStatisticsService({required this.dao}); + + /// 获取Widget统计数据 + Future getStatistics() async { + final results = await Future.wait([ + dao.getFamilyCount(), + dao.getTotalWidgets(), + dao.getTotalFields(), + dao.getAverageFields(), + dao.getLeverDistribution(), + dao.getWidgetFieldsCount(), + ]); + + final rawFamilyCount = results[0] as Map; + final totalWidgets = results[1] as int; + final totalFields = results[2] as int; + final averageFields = results[3] as double; + final leverDistribution = results[4] as Map; + final widgetFieldsCount = results[5] as Map; + + final familyCount = {}; + for (var entry in rawFamilyCount.entries) { + final family = _intToWidgetFamily(entry.key); + familyCount[family] = entry.value; + } + + return WidgetStatistics( + familyCount: familyCount, + totalWidgets: totalWidgets, + totalFields: totalFields, + averageFields: averageFields, + leverDistribution: leverDistribution, + widgetFieldsCount: widgetFieldsCount, + ); + } + + WidgetFamily _intToWidgetFamily(int index) { + switch (index) { + case 0: return WidgetFamily.stateless; + case 1: return WidgetFamily.stateful; + case 2: return WidgetFamily.singleChildRender; + case 3: return WidgetFamily.multiChildRender; + case 4: return WidgetFamily.sliver; + case 5: return WidgetFamily.proxy; + case 6: return WidgetFamily.other; + default: return WidgetFamily.stateless; + } + } +} \ No newline at end of file diff --git a/modules/widget_system/widget_repository/lib/widget_repository.dart b/modules/widget_system/widget_repository/lib/widget_repository.dart new file mode 100644 index 000000000..c163d1319 --- /dev/null +++ b/modules/widget_system/widget_repository/lib/widget_repository.dart @@ -0,0 +1,7 @@ +library widget_repository; + +export 'src/repository/repository.dart'; +export 'src/memory/memory_node_repository.dart'; +export 'src/memory/memory_widget_repository.dart'; +export 'src/model/model.dart'; +export 'src/database/database.dart'; \ No newline at end of file diff --git a/modules/widget_system/widget_repository/pubspec.yaml b/modules/widget_system/widget_repository/pubspec.yaml new file mode 100644 index 000000000..895e78c90 --- /dev/null +++ b/modules/widget_system/widget_repository/pubspec.yaml @@ -0,0 +1,60 @@ +name: widget_repository +description: "A new Flutter package project." +version: 0.0.1 +homepage: + +environment: + sdk: ^3.5.0 + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + equatable: ^2.0.5 # 相等辅助 + intl: ^0.19.0 # 相等辅助 + fx_dao: 0.0.3+4 # 数据库 + storage: + path: ../../basic_system/storage + utils: + path: ../../basic_system/utils +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^4.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # To add assets to your package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/to/asset-from-package + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/to/resolution-aware-images + + # To add custom fonts to your package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/to/font-from-package diff --git a/modules/widget_system/widget_repository/test/widget_repository_test.dart b/modules/widget_system/widget_repository/test/widget_repository_test.dart new file mode 100644 index 000000000..e69de29bb diff --git a/modules/widget_system/widget_ui/.gitignore b/modules/widget_system/widget_ui/.gitignore new file mode 100644 index 000000000..eb6c05cd3 --- /dev/null +++ b/modules/widget_system/widget_ui/.gitignore @@ -0,0 +1,31 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +build/ diff --git a/modules/widget_system/widget_ui/.metadata b/modules/widget_system/widget_ui/.metadata new file mode 100644 index 000000000..ab15d7afa --- /dev/null +++ b/modules/widget_system/widget_ui/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "c519ee916eaeb88923e67befb89c0f1dabfa83e6" + channel: "stable" + +project_type: package diff --git a/modules/widget_system/widget_ui/CHANGELOG.md b/modules/widget_system/widget_ui/CHANGELOG.md new file mode 100644 index 000000000..41cc7d819 --- /dev/null +++ b/modules/widget_system/widget_ui/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/modules/widget_system/widget_ui/LICENSE b/modules/widget_system/widget_ui/LICENSE new file mode 100644 index 000000000..ba75c69f7 --- /dev/null +++ b/modules/widget_system/widget_ui/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/modules/widget_system/widget_ui/README.md b/modules/widget_system/widget_ui/README.md new file mode 100644 index 000000000..4a260d8d2 --- /dev/null +++ b/modules/widget_system/widget_ui/README.md @@ -0,0 +1,39 @@ + + +TODO: Put a short description of the package here that helps potential users +know whether this package might be useful for them. + +## Features + +TODO: List what your package can do. Maybe include images, gifs, or videos. + +## Getting started + +TODO: List prerequisites and provide or point to information on how to +start using the package. + +## Usage + +TODO: Include short and useful examples for package users. Add longer examples +to `/example` folder. + +```dart +const like = 'sample'; +``` + +## Additional information + +TODO: Tell users more about the package: where to find more information, how to +contribute to the package, how to file issues, what response they can expect +from the package authors, and more. diff --git a/modules/widget_system/widget_ui/analysis_options.yaml b/modules/widget_system/widget_ui/analysis_options.yaml new file mode 100644 index 000000000..a5744c1cf --- /dev/null +++ b/modules/widget_system/widget_ui/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/modules/widget_system/widget_ui/lib/src/bloc/bloc.dart b/modules/widget_system/widget_ui/lib/src/bloc/bloc.dart new file mode 100644 index 000000000..c9833f0be --- /dev/null +++ b/modules/widget_system/widget_ui/lib/src/bloc/bloc.dart @@ -0,0 +1 @@ +export 'liked_widget_bloc.dart'; diff --git a/modules/widget_system/widget_ui/lib/src/bloc/liked_widget_bloc.dart b/modules/widget_system/widget_ui/lib/src/bloc/liked_widget_bloc.dart new file mode 100644 index 000000000..5acdbc57c --- /dev/null +++ b/modules/widget_system/widget_ui/lib/src/bloc/liked_widget_bloc.dart @@ -0,0 +1,22 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:widget_repository/widget_repository.dart'; + +/// create by 张风捷特烈 on 2020-04-07 +/// contact me by email 1981462002@qq.com +/// 说明: + +class LikeWidgetBloc extends Cubit> { + final WidgetRepository repository; + + LikeWidgetBloc({required this.repository}) : super(const []); + + Future loadLikeData() async { + List widgets = await repository.loadLikeWidgets(); + emit(widgets); + } + + Future toggle(int widgetId) async { + await repository.toggleLike(widgetId); + return loadLikeData(); + } +} diff --git a/modules/widget_system/widget_ui/lib/src/view/field/filed.dart b/modules/widget_system/widget_ui/lib/src/view/field/filed.dart new file mode 100644 index 000000000..f94d92fd1 --- /dev/null +++ b/modules/widget_system/widget_ui/lib/src/view/field/filed.dart @@ -0,0 +1,2 @@ +export 'widget_fields_dialog.dart'; +export 'widget_fields_page.dart'; diff --git a/modules/widget_system/widget_ui/lib/src/view/field/widget_fields_dialog.dart b/modules/widget_system/widget_ui/lib/src/view/field/widget_fields_dialog.dart new file mode 100644 index 000000000..c8ea26d06 --- /dev/null +++ b/modules/widget_system/widget_ui/lib/src/view/field/widget_fields_dialog.dart @@ -0,0 +1,139 @@ +import 'package:flutter/material.dart'; +import 'package:widget_repository/widget_repository.dart'; + +class WidgetFieldsDialog extends StatefulWidget { + final int widgetId; + final String widgetName; + + const WidgetFieldsDialog({ + super.key, + required this.widgetId, + required this.widgetName, + }); + + @override + State createState() => _WidgetFieldsDialogState(); +} + +class _WidgetFieldsDialogState extends State { + List? _fields; + bool _isLoading = true; + + @override + void initState() { + super.initState(); + _loadFields(); + } + + Future _loadFields() async { + try { + final repository = const WidgetDbRepository(); + final fields = await repository.loadWidgetFields(widget.widgetId); + // SQL已经按必需属性排序,不需要再次排序 + setState(() { + _isLoading = false; + _fields = fields; + }); + } catch (e) { + setState(() { + _isLoading = false; + _fields = []; + }); + } + } + + @override + Widget build(BuildContext context) { + return Dialog( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + child: Container( + width: 500, + height: 600, + padding: const EdgeInsets.all(20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildHeader(context), + const Divider(), + const SizedBox(height: 16), + Expanded(child: _buildContent()), + ], + ), + ), + ); + } + + Widget _buildHeader(BuildContext context) { + return Row( + children: [ + Icon(Icons.widgets, color: Theme.of(context).primaryColor), + const SizedBox(width: 8), + Text( + '${widget.widgetName} 属性', + style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + const Spacer(), + IconButton( + onPressed: () => Navigator.of(context).pop(), + icon: const Icon(Icons.close), + ), + ], + ); + } + + Widget _buildContent() { + if (_isLoading) return const Center(child: CircularProgressIndicator()); + if (_fields!.isEmpty) return const Center(child: Text('暂无属性信息')); + + return ListView.builder( + itemCount: _fields!.length, + itemBuilder: (context, index) => _buildFieldItem(_fields![index]), + ); + } + + Widget _buildFieldItem(WidgetFieldModel field) { + return Container( + margin: const EdgeInsets.only(bottom: 8), + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: Theme.of(context).cardColor, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: Colors.grey.withOpacity(0.2)), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + RichText( + text: TextSpan( + children: [ + TextSpan( + text: '${field.fieldName} : ', + style: const TextStyle( + fontWeight: FontWeight.bold, color: Colors.black), + ), + TextSpan( + text: field.fieldType, + style: TextStyle( + fontWeight: FontWeight.bold, + color: Theme.of(context).primaryColor), + ), + ], + ), + ), + // if (field.isRequired) _buildRequiredBadge(), + ], + ), + if (field.fieldDescZh != null) ...[ + const SizedBox(height: 4), + Text( + field.fieldDescZh!, + style: const TextStyle(fontSize: 11, color: Colors.grey), + ), + ], + ], + ), + ); + } +} diff --git a/modules/widget_system/widget_ui/lib/src/view/field/widget_fields_page.dart b/modules/widget_system/widget_ui/lib/src/view/field/widget_fields_page.dart new file mode 100644 index 000000000..d8df9d6d8 --- /dev/null +++ b/modules/widget_system/widget_ui/lib/src/view/field/widget_fields_page.dart @@ -0,0 +1,140 @@ +import 'package:flutter/material.dart'; +import 'package:widget_repository/widget_repository.dart'; + +class WidgetFieldsPage extends StatefulWidget { + final int widgetId; + final String widgetName; + + const WidgetFieldsPage({ + super.key, + required this.widgetId, + required this.widgetName, + }); + + @override + State createState() => _WidgetFieldsPageState(); +} + +class _WidgetFieldsPageState extends State { + List? _fields; + bool _isLoading = true; + + @override + void initState() { + super.initState(); + _loadFields(); + } + + Future _loadFields() async { + try { + final repository = const WidgetDbRepository(); + final fields = await repository.loadWidgetFields(widget.widgetId); + // SQL已经按必需属性排序,不需要再次排序 + setState(() { + _isLoading = false; + _fields = fields; + }); + } catch (e) { + setState(() { + _isLoading = false; + _fields = []; + }); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + bottom: PreferredSize( + preferredSize: Size(0, 10), + child: Container( + height: 10, + color: Color(0xfff3f4f6), + )), + title: Text('${widget.widgetName} 属性'), + centerTitle: true, + ), + body: _buildContent(), + ); + } + + Widget _buildContent() { + if (_isLoading) return const Center(child: CircularProgressIndicator()); + if (_fields!.isEmpty) return const Center(child: Text('暂无属性信息')); + bool isZh = Localizations.localeOf(context).languageCode == 'zh'; + return ListView.separated( + separatorBuilder: (_, __) => Divider(), + itemCount: _fields!.length, + itemBuilder: (context, index) => _buildFieldItem(_fields![index], isZh), + ); + } + + Widget _buildFieldItem(WidgetFieldModel field, bool isZh) { + Color color = Theme.of(context).primaryColor; + return Container( + color: Colors.white, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + width: 4, + height: 20, + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(2), + ), + ), + const SizedBox(width: 12), + Expanded( + child: RichText( + text: TextSpan( + children: [ + TextSpan( + text: field.fieldName, + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 14, + color: Colors.black87, + ), + ), + ], + ), + ), + ), + // if (field.isRequired) _buildRequiredBadge(), + ], + ), + const SizedBox(height: 2), + Padding( + padding: const EdgeInsets.only(left: 16), + child: Text( + field.fieldType, + style: TextStyle( + color: Theme.of(context).primaryColor, + fontSize: 14, + fontWeight: FontWeight.w600, + ), + ), + ), + if (field.fieldDescZh != null) ...[ + const SizedBox(height: 8), + Text( + isZh ? field.fieldDescZh! : '${field.fieldDesc}', + style: const TextStyle( + fontSize: 13, + color: Colors.grey, + height: 1.4, + ), + ), + ], + ], + ), + ), + ); + } +} diff --git a/modules/widget_system/widget_ui/lib/src/view/node_tiled/node_tiled.dart b/modules/widget_system/widget_ui/lib/src/view/node_tiled/node_tiled.dart new file mode 100644 index 000000000..613744d49 --- /dev/null +++ b/modules/widget_system/widget_ui/lib/src/view/node_tiled/node_tiled.dart @@ -0,0 +1 @@ +// TODO Implement this library. diff --git a/modules/widget_system/widget_ui/lib/src/view/view.dart b/modules/widget_system/widget_ui/lib/src/view/view.dart new file mode 100644 index 000000000..3d2dcfe20 --- /dev/null +++ b/modules/widget_system/widget_ui/lib/src/view/view.dart @@ -0,0 +1,3 @@ +export 'widget_tiled/widget_tiled.dart'; +export 'node_tiled/node_tiled.dart'; +export 'field/filed.dart'; diff --git a/modules/widget_system/widget_ui/lib/src/view/widget_tiled/widget_detail_logo.dart b/modules/widget_system/widget_ui/lib/src/view/widget_tiled/widget_detail_logo.dart new file mode 100644 index 000000000..4a5cfbe3b --- /dev/null +++ b/modules/widget_system/widget_ui/lib/src/view/widget_tiled/widget_detail_logo.dart @@ -0,0 +1,135 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:flutter_star/star.dart'; +import 'package:flutter_star/star_score.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:widget_repository/widget_repository.dart'; +import 'package:fx_env/fx_env.dart'; +import 'package:widget_ui/widget_ui.dart'; + +class WidgetDetailLogo extends StatelessWidget { + final Color background; + final String widgetName; + final WidgetModel model; + + const WidgetDetailLogo({ + super.key, + required this.background, + required this.widgetName, + required this.model, + }); + + @override + Widget build(BuildContext context) { + if (!kApp.isDesktop) { + return Padding( + padding: const EdgeInsets.only(right: 8.0, top: 0), + child: WidgetLogo( + background: background, + widgetName: widgetName, + widgetId: model.id, + lever: model.lever, + inDetail: true, + ), + ); + } + + return Stack( + children: [ + Container( + width: 240, + height: 160, + alignment: Alignment.center, + decoration: BoxDecoration( + color: background, + gradient: LinearGradient( + transform: const GradientRotation(270 * 180 / pi), + colors: [ + background.withValues(alpha: 0.9), + background.withValues(alpha: 0.5) + ]), + borderRadius: BorderRadius.circular(6), + ), + child: SvgPicture.asset( + 'assets/images/widgets/${widgetLogo(widgetName)}', + width: 120, + ), + ), + Positioned( + top: 6, + right: 0, + child: StarScore( + score: model.lever, + star: Star(size: 15, fillColor: Colors.white), + ), + ), + Positioned( + bottom: 2, + left: 6, + child: Text( + "#${model.id}", + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 14, + color: Colors.white70, + ), + )), + _buildFieldCountBadge(context), + ], + ); + } + + Widget _buildFieldCountBadge(BuildContext context) { + final provider = WidgetStatisticsProvider(); + final stats = provider.statistics; + if (stats == null) return const SizedBox.shrink(); + + final fieldCount = stats.getWidgetFieldCount(model.id); + if (fieldCount == 0) return const SizedBox.shrink(); + + return Positioned( + right: 0, + bottom: 0, + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => _showFieldsDialog(context), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 6.0, vertical: 4), + child: Text( + '属性:$fieldCount 个', + style: const TextStyle( + fontSize: 14, + color: Colors.white, + decoration: TextDecoration.underline, + decorationColor: Colors.white, + ), + ), + ), + ), + ); + } + + void _showFieldsDialog(BuildContext context) { + if (kApp.isMobile) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => WidgetFieldsPage( + widgetId: model.id, + widgetName: widgetName, + ), + ), + ); + } else { + showDialog( + context: context, + builder: (context) => WidgetFieldsDialog( + widgetId: model.id, + widgetName: widgetName, + ), + ); + } + } +} diff --git a/modules/widget_system/widget_ui/lib/src/view/widget_tiled/widget_fields_dialog.dart b/modules/widget_system/widget_ui/lib/src/view/widget_tiled/widget_fields_dialog.dart new file mode 100644 index 000000000..83588aac3 --- /dev/null +++ b/modules/widget_system/widget_ui/lib/src/view/widget_tiled/widget_fields_dialog.dart @@ -0,0 +1,155 @@ +import 'package:flutter/material.dart'; +import 'package:widget_repository/widget_repository.dart'; +import 'package:storage/storage.dart'; + +class WidgetFieldsDialog extends StatefulWidget { + final int widgetId; + final String widgetName; + + const WidgetFieldsDialog({ + super.key, + required this.widgetId, + required this.widgetName, + }); + + @override + State createState() => _WidgetFieldsDialogState(); +} + +class _WidgetFieldsDialogState extends State { + List? _fields; + bool _isLoading = true; + + @override + void initState() { + super.initState(); + _loadFields(); + } + + Future _loadFields() async { + try { + final repository = const WidgetDbRepository(); + final fields = await repository.loadWidgetFields(widget.widgetId); + // SQL已经按必需属性排序,不需要再次排序 + setState(() { + _isLoading = false; + _fields = fields; + }); + } catch (e) { + setState(() { + _isLoading = false; + _fields = []; + }); + } + } + + @override + Widget build(BuildContext context) { + return Dialog( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + child: Container( + width: 500, + height: 600, + padding: const EdgeInsets.all(20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildHeader(context), + const Divider(), + const SizedBox(height: 16), + Expanded(child: _buildContent()), + ], + ), + ), + ); + } + + Widget _buildHeader(BuildContext context) { + return Row( + children: [ + Icon(Icons.widgets, color: Theme.of(context).primaryColor), + const SizedBox(width: 8), + Text( + '${widget.widgetName} 属性', + style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + const Spacer(), + IconButton( + onPressed: () => Navigator.of(context).pop(), + icon: const Icon(Icons.close), + ), + ], + ); + } + + Widget _buildContent() { + if (_isLoading) return const Center(child: CircularProgressIndicator()); + if (_fields!.isEmpty) return const Center(child: Text('暂无属性信息')); + + return ListView.builder( + itemCount: _fields!.length, + itemBuilder: (context, index) => _buildFieldItem(_fields![index]), + ); + } + + Widget _buildFieldItem(WidgetFieldModel field) { + return Container( + margin: const EdgeInsets.only(bottom: 8), + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: Theme.of(context).cardColor, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: Colors.grey.withOpacity(0.2)), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + RichText( + text: TextSpan( + children: [ + TextSpan( + text: '${field.fieldName} : ', + style: const TextStyle( + fontWeight: FontWeight.bold, color: Colors.black), + ), + TextSpan( + text: field.fieldType, + style: TextStyle( + fontWeight: FontWeight.bold, + color: Theme.of(context).primaryColor), + ), + ], + ), + ), + // if (field.isRequired) _buildRequiredBadge(), + ], + ), + if (field.fieldDescZh != null) ...[ + const SizedBox(height: 4), + Text( + field.fieldDescZh!, + style: const TextStyle(fontSize: 11, color: Colors.grey), + ), + ], + ], + ), + ); + } + + Widget _buildRequiredBadge() { + return Container( + margin: const EdgeInsets.only(left: 8), + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1), + decoration: BoxDecoration( + color: Colors.red, + borderRadius: BorderRadius.circular(4), + ), + child: const Text( + '必需', + style: TextStyle(color: Colors.white, fontSize: 10), + ), + ); + } +} diff --git a/modules/widget_system/widget_ui/lib/src/view/widget_tiled/widget_fields_page.dart b/modules/widget_system/widget_ui/lib/src/view/widget_tiled/widget_fields_page.dart new file mode 100644 index 000000000..99f7bd7d8 --- /dev/null +++ b/modules/widget_system/widget_ui/lib/src/view/widget_tiled/widget_fields_page.dart @@ -0,0 +1,160 @@ +import 'package:flutter/material.dart'; +import 'package:widget_repository/widget_repository.dart'; +import 'package:storage/storage.dart'; + +class WidgetFieldsPage extends StatefulWidget { + final int widgetId; + final String widgetName; + + const WidgetFieldsPage({ + super.key, + required this.widgetId, + required this.widgetName, + }); + + @override + State createState() => _WidgetFieldsPageState(); +} + +class _WidgetFieldsPageState extends State { + List? _fields; + bool _isLoading = true; + + @override + void initState() { + super.initState(); + _loadFields(); + } + + Future _loadFields() async { + try { + final repository = const WidgetDbRepository(); + final fields = await repository.loadWidgetFields(widget.widgetId); + // SQL已经按必需属性排序,不需要再次排序 + setState(() { + _isLoading = false; + _fields = fields; + }); + } catch (e) { + setState(() { + _isLoading = false; + _fields = []; + }); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + bottom: PreferredSize( + preferredSize: Size(0, 10), + child: Container( + height: 10, + color: Color(0xfff3f4f6), + )), + title: Text('${widget.widgetName} 属性'), + centerTitle: true, + ), + body: _buildContent(), + ); + } + + Widget _buildContent() { + if (_isLoading) return const Center(child: CircularProgressIndicator()); + if (_fields!.isEmpty) return const Center(child: Text('暂无属性信息')); + + return ListView.separated( + separatorBuilder: (_, __) => Divider(), + itemCount: _fields!.length, + itemBuilder: (context, index) => _buildFieldItem(_fields![index]), + ); + } + + Widget _buildFieldItem(WidgetFieldModel field) { + return Container( + color: Colors.white, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + width: 4, + height: 20, + decoration: BoxDecoration( + color: field.isRequired + ? Colors.red + : Theme.of(context).primaryColor, + borderRadius: BorderRadius.circular(2), + ), + ), + const SizedBox(width: 12), + Expanded( + child: RichText( + text: TextSpan( + children: [ + TextSpan( + text: field.fieldName, + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 14, + color: Colors.black87, + ), + ), + ], + ), + ), + ), + // if (field.isRequired) _buildRequiredBadge(), + ], + ), + const SizedBox(height: 2), + Padding( + padding: const EdgeInsets.only(left: 16), + child: Text( + field.fieldType, + style: TextStyle( + color: Theme.of(context).primaryColor, + fontSize: 14, + fontWeight: FontWeight.w600, + ), + ), + ), + if (field.fieldDescZh != null) ...[ + const SizedBox(height: 8), + Text( + field.fieldDescZh!, + style: const TextStyle( + fontSize: 13, + color: Colors.grey, + height: 1.4, + ), + ), + ], + ], + ), + ), + ); + } + + Widget _buildRequiredBadge() { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: Colors.red, + borderRadius: BorderRadius.circular(12), + ), + child: const Text( + '必需', + style: TextStyle( + color: Colors.white, + fontSize: 11, + fontWeight: FontWeight.w500, + ), + ), + ); + } +} diff --git a/modules/widget_system/widget_ui/lib/src/view/widget_tiled/widget_id_view.dart b/modules/widget_system/widget_ui/lib/src/view/widget_tiled/widget_id_view.dart new file mode 100644 index 000000000..bc73ced7b --- /dev/null +++ b/modules/widget_system/widget_ui/lib/src/view/widget_tiled/widget_id_view.dart @@ -0,0 +1,20 @@ +import 'package:flutter/material.dart'; + +class WidgetIdView extends StatelessWidget { + final int id; + + const WidgetIdView({super.key, required this.id}); + + @override + Widget build(BuildContext context) { + return Text( + "#$id", + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + fontSize: 10, + color: Colors.white70, + ), + ); + } +} diff --git a/modules/widget_system/widget_ui/lib/src/view/widget_tiled/widget_item.dart b/modules/widget_system/widget_ui/lib/src/view/widget_tiled/widget_item.dart new file mode 100644 index 000000000..b7e6468c4 --- /dev/null +++ b/modules/widget_system/widget_ui/lib/src/view/widget_tiled/widget_item.dart @@ -0,0 +1,207 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_star/star.dart'; +import 'package:flutter_star/star_score.dart'; +import 'package:tolyui_message/tolyui_message.dart'; +import 'package:tolyui_text/tolyui_text.dart'; +import 'package:widget_repository/widget_repository.dart'; +import 'package:wrapper/wrapper.dart'; + +import '../../../widget_ui.dart'; + +sealed class WidgetAction {} + +class JumpWidgetDetail extends WidgetAction { + final int? widgetId; + final String? widgetName; + final WidgetModel? model; + + JumpWidgetDetail({ + this.widgetId, + this.model, + this.widgetName, + }); +} + +class ToggleLikeWidget extends WidgetAction { + final int widgetId; + + ToggleLikeWidget(this.widgetId); +} + +/// 组价主页单体的样式 +class WidgetItem extends StatelessWidget { + final WidgetModel model; + final String? searchArgs; + + final ValueChanged onWidget; + + const WidgetItem({ + super.key, + required this.model, + this.searchArgs, + required this.onWidget, + }); + + @override + Widget build(BuildContext context) { + ThemeData theme = Theme.of(context); + ListTileThemeData data = theme.listTileTheme; + Color? tileColor = data.tileColor; + Color? textColor = data.textColor; + bool isDark = theme.brightness == Brightness.dark; + textColor = isDark ? textColor : const Color(0xff2F3032); + Color color = theme.primaryColor; + EdgeInsetsGeometry padding = const EdgeInsets.symmetric( + horizontal: 12, + vertical: 8, + ); + + return Stack( + children: [ + InkWell( + borderRadius: BorderRadius.circular(6), + onTap: () => onWidget.call(JumpWidgetDetail(model: model)), + child: Ink( + decoration: BoxDecoration( + color: tileColor, + borderRadius: BorderRadius.circular(6), + boxShadow: [ + BoxShadow( + color: color.withValues(alpha: 0.1), + blurRadius: 2, + ) + ], + ), + child: Row( + children: [ + GestureDetector( + onLongPress: () => onWidget.call(ToggleLikeWidget(model.id)), + child: Hero( + tag: model.heroId, + child: WidgetLogo( + lever: model.lever, + background: color, + widgetName: model.name, + widgetId: model.id)), + ), + Expanded( + child: Padding( + padding: padding, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + listTitle(textColor), + _buildContent(textColor), + _buildFoot(isDark) + ], + ), + ), + ), + ], + ), + ), + ), + LikeTag(model: model, onWidget: onWidget), + ], + ); + } + + Widget listTitle(Color? textColor) { + TextStyle style = TextStyle( + fontSize: 14, + color: textColor, + fontWeight: FontWeight.bold, + ); + + return GestureDetector( + child: HighlightText.withArg( + model.name, + arg: searchArgs, + caseSensitive: false, + highlightStyle: style.copyWith(color: Colors.red), + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: style, + ), + onLongPress: () async { + await Clipboard.setData(ClipboardData(text: model.name)); + $message.success(message: '名称复制成功!'); + }, + ); + } + + Widget _buildContent(Color? textColor) { + TextStyle style = TextStyle(fontSize: 13, color: textColor); + + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: HighlightText.withArg( + model.info, + arg: searchArgs, + caseSensitive: false, + highlightStyle: style.copyWith(color: Colors.red), + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: TextStyle(fontSize: 13, color: textColor), + ), + ); + } + + Widget _buildFoot(bool isDark) { + TextStyle style = + const TextStyle(fontSize: 12, height: 1, color: Color(0xff86909c)); + return Row( + children: [ + Container( + width: 4, + height: 4, + margin: const EdgeInsets.only(right: 6), + decoration: const BoxDecoration( + color: Color(0xff86909c), shape: BoxShape.circle), + ), + Expanded( + child: HighlightText.withArg( + model.nameCN, + arg: searchArgs, + caseSensitive: false, + highlightStyle: style.copyWith(color: Colors.red), + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: style, + ), + ), + Wrapper.just( + radius: 4, + color: isDark ? const Color(0xff292A2D) : const Color(0xffF3F3F5), + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + child: Text( + kWidgetFamilyLabelMap[model.family]!, + style: TextStyle( + color: + isDark ? const Color(0xffCCCCCC) : const Color(0xff878D96), + height: 1, + fontSize: 10, + shadows: [ + Shadow( + color: isDark ? Colors.black : Colors.white, + blurRadius: 2, + offset: const Offset(1, 1)) + ]), + ), + ), + ], + ); + } +} + +Map get kWidgetFamilyLabelMap => { + WidgetFamily.stateless: "Stateless", + WidgetFamily.stateful: "Stateful", + WidgetFamily.singleChildRender: "SingleChild", + WidgetFamily.multiChildRender: "MultiChild", + WidgetFamily.sliver: "Sliver", + WidgetFamily.proxy: "Proxy", + WidgetFamily.other: "Other", + }; diff --git a/modules/widget_system/widget_ui/lib/src/view/widget_tiled/widget_like_tag.dart b/modules/widget_system/widget_ui/lib/src/view/widget_tiled/widget_like_tag.dart new file mode 100644 index 000000000..384f24755 --- /dev/null +++ b/modules/widget_system/widget_ui/lib/src/view/widget_tiled/widget_like_tag.dart @@ -0,0 +1,43 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:widget_repository/widget_repository.dart'; +import 'package:widget_ui/widget_ui.dart'; + +class LikeTag extends StatelessWidget { + final WidgetModel model; + final ValueChanged onWidget; + + const LikeTag({ + super.key, + required this.model, + required this.onWidget, + }); + + @override + Widget build(BuildContext context) { + bool show = + context.select((LikeWidgetBloc bloc) => bloc.state.contains(model)); + if (show) { + return GestureDetector( + onTap: () => onWidget(ToggleLikeWidget(model.id)), + child: Container( + width: 24, + height: 24, + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.3), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(6), + bottomRight: Radius.circular(8), + )), + child: Icon( + Icons.star, + color: Colors.white, + size: 16, + ), + ), + ); + } + return const SizedBox(); + } +} diff --git a/modules/widget_system/widget_ui/lib/src/view/widget_tiled/widget_logo.dart b/modules/widget_system/widget_ui/lib/src/view/widget_tiled/widget_logo.dart new file mode 100644 index 000000000..ca82e7748 --- /dev/null +++ b/modules/widget_system/widget_ui/lib/src/view/widget_tiled/widget_logo.dart @@ -0,0 +1,246 @@ +import 'dart:math'; +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import 'package:flutter_star/star.dart'; +import 'package:flutter_star/star_score.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:widget_repository/widget_repository.dart'; +import 'package:fx_env/fx_env.dart'; +import 'package:widget_ui/widget_ui.dart'; + +class WidgetLogo extends StatelessWidget { + final Color background; + final String widgetName; + final int? widgetId; + final double lever; + final bool inDetail; + + const WidgetLogo({ + super.key, + required this.background, + required this.widgetName, + this.widgetId, + required this.lever, + this.inDetail = false, + }); + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + Container( + width: 110, + height: 110, + alignment: Alignment.center, + decoration: BoxDecoration( + color: background, + gradient: LinearGradient( + transform: const GradientRotation(270 * 180 / pi), + colors: [ + background.withValues(alpha: 0.9), + background.withValues(alpha: 0.5) + ]), + borderRadius: inDetail + ? BorderRadius.circular(6) + : const BorderRadius.only( + topLeft: Radius.circular(6), + bottomLeft: Radius.circular(6)), + ), + child: SvgPicture.asset( + 'assets/images/widgets/${widgetLogo(widgetName)}', + width: 80, + ), + ), + Positioned( + bottom: 4, + left: 6, + child: WidgetIdView( + id: widgetId ?? 0, + )), + Positioned( + top: 6, + right: -4, + child: StarScore( + score: lever, + star: Star(size: 10, fillColor: Colors.white), + ), + ), + if (widgetId != null) _buildFieldCountBadge(context), + ], + ); + } + + Widget _buildFieldCountBadge(BuildContext context) { + final provider = WidgetStatisticsProvider(); + final stats = provider.statistics; + if (stats == null) return const SizedBox.shrink(); + + final fieldCount = stats.getWidgetFieldCount(widgetId!); + if (fieldCount == 0) return const SizedBox.shrink(); + + return Positioned( + right: 0, + bottom: 0, + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => _showFieldsDialog(context), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 6.0, vertical: 4), + child: Text( + '属性:$fieldCount', + style: const TextStyle( + fontSize: 10, + color: Colors.white, + decoration: TextDecoration.underline, + decorationColor: Colors.white, + ), + ), + ), + ), + ); + } + + void _showFieldsDialog(BuildContext context) { + if (kApp.isMobile) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => WidgetFieldsPage( + widgetId: widgetId!, + widgetName: widgetName, + ), + ), + ); + } else { + showDialog( + context: context, + builder: (context) => WidgetFieldsDialog( + widgetId: widgetId!, + widgetName: widgetName, + ), + ); + } + } +} + +String widgetLogo(String widgetName) { + return switch (widgetName) { + 'Container' => 'Container.svg', + 'Text' => 'Text.svg', + 'GestureDetector' => 'GestureDetector.svg', + 'CircleAvatar' => 'CircleAvatar.svg', + 'Card' => 'Card.svg', + 'ListView' => 'ListView.svg', + 'GridView' => 'GridView.svg', + 'SingleChildScrollView' => 'SingleChildScrollView.svg', + 'PageView' => 'PageView.svg', + 'InputChip' => 'InputChip.svg', + 'Chip' => 'Chip.svg', + 'FilterChip' => 'FilterChip.svg', + 'MaterialButton' => 'MaterialButton.svg', + 'FlutterLogo' => 'FlutterLogo.svg', + 'RichText' => 'RichText.svg', + 'FloatingActionButton' => 'FloatingActionButton.svg', + 'Banner' => 'Banner.svg', + 'Icon' => 'Icon.svg', + _ => 'Widget.svg', + }; +} + +class GlassSquare extends StatelessWidget { + final Color color; // 主颜色 + final double size; // 边长 + final Widget? child; // 子组件 + + const GlassSquare({ + super.key, + required this.color, + this.size = 300, + this.child, + }); + + @override + Widget build(BuildContext context) { + return ClipRRect( + borderRadius: BorderRadius.circular(20), + child: Stack( + alignment: Alignment.center, + children: [ + // 背景渐变层 + Container( + width: size, + height: size, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + color, + Color.lerp(color, Colors.white, 0.4)!, + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.4), + blurRadius: 25, + offset: const Offset(0, 8), + ), + ], + ), + ), + + // 毛玻璃效果层 + BackdropFilter( + filter: ImageFilter.blur(sigmaX: 18, sigmaY: 18), + child: Container( + width: size, + height: size, + decoration: BoxDecoration( + border: Border.all(color: Colors.white.withOpacity(0.2)), + color: Colors.white.withOpacity(0.05), + ), + ), + ), + + // 高光层 + Container( + width: size, + height: size, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + gradient: RadialGradient( + colors: [ + Colors.white.withOpacity(0.45), + Colors.transparent, + ], + radius: 0.6, + center: const Alignment(-0.6, -0.6), + ), + ), + ), + + // 柔光层 + Container( + width: size, + height: size, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + gradient: const LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Color.fromRGBO(255, 255, 255, 0.15), + Color.fromRGBO(0, 0, 0, 0.25), + ], + ), + ), + ), + + // 子组件 + if (child != null) child!, + ], + ), + ); + } +} diff --git a/modules/widget_system/widget_ui/lib/src/view/widget_tiled/widget_tiled.dart b/modules/widget_system/widget_ui/lib/src/view/widget_tiled/widget_tiled.dart new file mode 100644 index 000000000..e16dd00c6 --- /dev/null +++ b/modules/widget_system/widget_ui/lib/src/view/widget_tiled/widget_tiled.dart @@ -0,0 +1,5 @@ +export 'widget_item.dart'; +export 'widget_logo.dart'; +export 'widget_id_view.dart'; +export 'widget_like_tag.dart'; +export 'widget_detail_logo.dart'; diff --git a/modules/widget_system/widget_ui/lib/widget_ui.dart b/modules/widget_system/widget_ui/lib/widget_ui.dart new file mode 100644 index 000000000..5c47bd8d0 --- /dev/null +++ b/modules/widget_system/widget_ui/lib/widget_ui.dart @@ -0,0 +1,4 @@ +library; + +export "src/view/view.dart"; +export 'src/bloc/bloc.dart'; diff --git a/modules/widget_system/widget_ui/pubspec.yaml b/modules/widget_system/widget_ui/pubspec.yaml new file mode 100644 index 000000000..b78ce0970 --- /dev/null +++ b/modules/widget_system/widget_ui/pubspec.yaml @@ -0,0 +1,65 @@ +name: widget_ui +description: "A new Flutter package project." +version: 0.0.1 +homepage: + +environment: + sdk: ^3.6.1 + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + flutter_bloc: ^8.1.6 # 状态管理 + equatable: ^2.0.5 # 相等辅助 + tolyui_message: ^0.2.6+1 + flutter_star: ^1.0.2 # 星星组件 + wrapper: ^1.0.2 # 气泡包裹 + tolyui_text: ^0.0.1+4 + + fx_platform_adapter: 0.0.3 # 平台适配器 + flutter_svg: ^2.0.17 # svg 展示 + widget_repository: + path: ../widget_repository + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # To add assets to your package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/to/asset-from-package + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/to/resolution-aware-images + + # To add custom fonts to your package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/to/font-from-package diff --git a/modules/widget_system/widget_ui/test/widget_ui_test.dart b/modules/widget_system/widget_ui/test/widget_ui_test.dart new file mode 100644 index 000000000..849487fbc --- /dev/null +++ b/modules/widget_system/widget_ui/test/widget_ui_test.dart @@ -0,0 +1,5 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:widget_ui/widget_ui.dart'; + +void main() {} diff --git a/packages/algorithm/lib/algorithm.dart b/packages/algorithm/lib/algorithm.dart deleted file mode 100644 index f9639cf2e..000000000 --- a/packages/algorithm/lib/algorithm.dart +++ /dev/null @@ -1,9 +0,0 @@ -library algorithm; - -export 'src/views/sort_parper.dart'; -export 'src/views/top_bar/sort_bar.dart'; -export 'src/views/top_bar/sort_button.dart'; -export 'src/views/sort_setting.dart'; -export 'src/views/sort_page.dart'; -export 'src/data_scope/sort_config.dart'; -export 'src/data_scope/state.dart'; \ No newline at end of file diff --git a/packages/algorithm/lib/src/data_scope/state.dart b/packages/algorithm/lib/src/data_scope/state.dart deleted file mode 100644 index a9ea9fc7f..000000000 --- a/packages/algorithm/lib/src/data_scope/state.dart +++ /dev/null @@ -1,70 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/cupertino.dart'; - -import '../algorithm/sort/functions.dart'; -import 'sort_config.dart'; - -enum SortStatus{ - none, // 未操作 - sorting, // 排序中 - sorted, // 排序完成 -} - -class SortState with ChangeNotifier{ - - SortState(){ - reset(); - } - - SortStatus status = SortStatus.none; - List data = []; - SortConfig _config = SortConfig(); - SortConfig get config => _config; - Random random = Random(); - - set config(SortConfig config){ - _config = config; - reset(); - notifyListeners(); - } - - void reset(){ - data.clear(); - status = SortStatus.none; - notifyListeners(); - int count = config.count; - if(config.seed!=-1){ - random = Random(config.seed); - } - for (int i = 0; i < count; i++) { - data.add(random.nextInt(1000)); - } - } - - void sort() async{ - status = SortStatus.sorting; - notifyListeners(); - SortFunction? sortFunction = sortFunctionMap[config.name]; - if(sortFunction!=null){ - await sortFunction(data,(arr) async { - await Future.delayed(config.duration); - notifyListeners(); - }); - } - status = SortStatus.sorted; - notifyListeners(); - } -} - -/// Provides the current [SortState] to descendant widgets in the tree. -class SortStateScope extends InheritedNotifier { - const SortStateScope({ - required super.notifier, - required super.child, - super.key, - }); - - static SortState of(BuildContext context) => - context.dependOnInheritedWidgetOfExactType()!.notifier!; -} \ No newline at end of file diff --git a/packages/algorithm/lib/src/views/top_bar/sort_bar.dart b/packages/algorithm/lib/src/views/top_bar/sort_bar.dart deleted file mode 100644 index 0a183be83..000000000 --- a/packages/algorithm/lib/src/views/top_bar/sort_bar.dart +++ /dev/null @@ -1,87 +0,0 @@ -import 'package:app/app.dart'; -import 'package:components/components.dart'; -import 'package:flutter/material.dart'; - - -import '../../data_scope/state.dart'; -import '../../algorithm/sort/functions.dart'; -import 'sort_button.dart'; - -class DeskSortBar extends StatelessWidget { - const DeskSortBar({super.key}); - - @override - Widget build(BuildContext context) { - - bool isDark = Theme.of(context).brightness == Brightness.dark; - - return DragToMoveAreaNoDouble( - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 20), - height: 64, - color: isDark? const Color(0xff2C3036):Colors.white, - child: const Row( - children: [ - Text('可视化排序',style: TextStyle(fontSize: 16,fontWeight: FontWeight.bold),), - Spacer(), - SizedBox( - width: 20, - ), - SortBar(), - Padding( - padding: EdgeInsets.symmetric(vertical: 18.0), - child: VerticalDivider( - width: 32, - ), - ), - WindowButtons(), - ], - ), - ), - ); - } -} - - -class SortBar extends StatelessWidget { - const SortBar({super.key}); - - @override - Widget build(BuildContext context) { - return Row( - children: [ - const SortButton(), - const SizedBox(width: 10,), - const SortSelector(), - const SizedBox(width: 10,), - GestureDetector( - onTap: (){ - Scaffold.of(context).openEndDrawer(); - }, - child: const Icon(Icons.settings)) - ], - ); - } -} - -class SortSelector extends StatelessWidget { - const SortSelector({super.key}); - - @override - Widget build(BuildContext context) { - return DropSelectableWidget( - fontSize: 12, - data: sortNameMap.values.toList(), - iconSize: 20, - height: 28, - width: 200, - disableColor: const Color(0xff1F425F), - onDropSelected: (int index) async { - SortState state = SortStateScope.of(context); - state.config =state.config.copyWith( - name: sortNameMap.keys.toList()[index] - ); - }, - ); - } -} diff --git a/packages/algorithm/pubspec.yaml b/packages/algorithm/pubspec.yaml deleted file mode 100644 index 7e7ad6ae7..000000000 --- a/packages/algorithm/pubspec.yaml +++ /dev/null @@ -1,57 +0,0 @@ -name: algorithm -description: algorithm -version: 0.0.1 -homepage: - -environment: - sdk: '>=2.18.1 <3.0.0' - flutter: ">=1.17.0" - -dependencies: - flutter: - sdk: flutter - components: - path: ../components - app: - path: ../app -dev_dependencies: - flutter_test: - sdk: flutter - flutter_lints: ^2.0.0 - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter packages. -flutter: - - # To add assets to your package, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - # - # For details regarding assets in packages, see - # https://flutter.dev/assets-and-images/#from-packages - # - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware - - # To add custom fonts to your package, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts in packages, see - # https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/app/lib/app.dart b/packages/app/lib/app.dart deleted file mode 100644 index 555144cfa..000000000 --- a/packages/app/lib/app.dart +++ /dev/null @@ -1,29 +0,0 @@ -library app; - -export 'bloc/global_bloc.dart'; -export 'model/global_state.dart'; -export 'model/app_style.dart'; -export 'repository/app_state_repository.dart'; -export 'app/cons/cons.dart'; -export 'app/cons/global_value.dart'; -export 'app/cons/path_unit.dart'; -export 'app/cons/sp.dart'; -export 'app/cons/str_unit.dart'; -export 'app/router/unit_router.dart'; - -export 'app/router/slide_page_route.dart'; -export 'app/router/fade_page_route.dart'; -export 'app/router/zero_page_route.dart'; - -export 'app/res/toly_icon.dart'; -export 'app/theme/size_unit.dart'; -export 'app/theme/app_theme.dart'; -export 'app/style/unit_text_style.dart'; -export 'app/style/unit_color.dart'; -export 'app/style/gap.dart'; -export 'app/style/shape/coupon_shape_border.dart'; -export 'app/style/shape/techno_shape.dart'; -export 'app/style/behavior/no_scroll_behavior.dart'; - -export 'package:app/platform_adapter/window/windows_adapter.dart'; -export 'package:app/platform_adapter/views/window_buttons.dart'; \ No newline at end of file diff --git a/packages/app/lib/app/cons/cons.dart b/packages/app/lib/app/cons/cons.dart deleted file mode 100644 index 43b8257ff..000000000 --- a/packages/app/lib/app/cons/cons.dart +++ /dev/null @@ -1,93 +0,0 @@ -import 'package:components/toly_ui/toly_ui.dart'; -import 'package:flutter/material.dart'; -import 'package:widget_repository/widget_repository.dart'; - -import '../../model/app_style.dart'; -import '../res/toly_icon.dart'; - - -class Cons { - - static const menuInfo = ["关于", "帮助", "问题反馈"]; //菜单栏 - - static const List tabColors = [ - Color(0xff44D1FD), - Color(0xffFD4F43), - Color(0xffB375FF), - Color(0xFF4CAF50), - Color(0xFFFF9800), - Color(0xFF00F1F1), - Color(0xFFDBD83F), - ]; - - static const tabs = [ - 'Stles', - 'Stful', - 'Scrow', - 'Mcrow', - 'Sliver', - 'Proxy', - 'Other' - ]; //标题列表 - - static const iconMap = { - //底栏图标 - "组件集录": TolyIcon.icon_layout, "收藏集录": TolyIcon.icon_star, - "绘制集录": Icons.palette, "布局集录": Icons.widgets, - "要点集录": TolyIcon.icon_bug, - }; - - static const List kFontFamilySupport = [ - 'local', - 'ComicNeue', - 'IndieFlower', - 'BalooBhai2', - 'Inconsolata', - 'Neucha' - ]; - - static const Map kWidgetFamilyLabelMap = { - WidgetFamily.statelessWidget: "Stateless", - WidgetFamily.statefulWidget: "Stateful", - WidgetFamily.singleChildRenderObjectWidget: "SingleChild", - WidgetFamily.multiChildRenderObjectWidget: "MultiChild", - WidgetFamily.sliver: "Sliver", - WidgetFamily.proxyWidget: "Proxy", - WidgetFamily.other: "Other", - }; - - static Map codeThemeSupport = { - HighlighterStyle.fromColors(HighlighterStyle.gitHub):"GitHub - Power By 张风捷特烈", - HighlighterStyle.fromColors(HighlighterStyle.darkColor):"捷特黑 - Power By 张风捷特烈", - HighlighterStyle.fromColors(HighlighterStyle.lightColor):"捷特白 - Power By 张风捷特烈", - HighlighterStyle.fromColors(HighlighterStyle.zenburn):"zenburn - Power By 张风捷特烈", - HighlighterStyle.fromColors(HighlighterStyle.mf):"mf - Power By MF", - HighlighterStyle.fromColors(HighlighterStyle.solarized):"cst - Power By cst", - }; - - - static final kThemeColorSupport = { - Colors.red: "毁灭之红", - Colors.orange: "愤怒之橙", - Colors.yellow: "警告之黄", - Colors.green: "伪装之绿", - Colors.blue: "冷漠之蓝", - Colors.indigo: "无限之靛", - Colors.purple: "神秘之紫", - - const MaterialColor(0xff2D2D2D, { - 50: Color(0xFF8A8A8A), - 100: Color(0xFF747474), - 200: Color(0xFF616161), - 300: Color(0xFF484848), - 400: Color(0xFF3D3D3D), - 500: Color(0xff2D2D2D), - 600: Color(0xFF252525), - 700: Color(0xFF141414), - 800: Color(0xFF050505), - 900: Color(0xff000000), - }): "归宿之黑" - }; - -} - diff --git a/packages/app/lib/app/cons/global_value.dart b/packages/app/lib/app/cons/global_value.dart deleted file mode 100644 index c3ef57316..000000000 --- a/packages/app/lib/app/cons/global_value.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'dart:io'; -import 'dart:ui'; - -import 'package:flutter/foundation.dart'; - -double px1 = 1/window.devicePixelRatio; - -bool isDesk = kIsWeb || Platform.isMacOS||Platform.isWindows||Platform.isLinux; \ No newline at end of file diff --git a/packages/app/lib/app/cons/path_unit.dart b/packages/app/lib/app/cons/path_unit.dart deleted file mode 100644 index e8d45cb3e..000000000 --- a/packages/app/lib/app/cons/path_unit.dart +++ /dev/null @@ -1,19 +0,0 @@ -/// create by 张风捷特烈 on 2021/1/17 -/// contact me by email 1981462002@qq.com -/// 说明: - -class PathUnit{ - - static const baseUrl='http://82.157.176.209:8080/api/v1'; - // static const baseUrl='http://192.168.0.100:8080/api/v1'; - - static const sendEmail = '/sendEmail/'; - static const register = '/register'; - - static const categoryDataSync = '/categoryData/sync'; - static const categoryData = '/categoryData'; - static const appInfo = '/appInfo/name'; - - static const login = '/login'; - -} \ No newline at end of file diff --git a/packages/app/lib/app/cons/str_unit.dart b/packages/app/lib/app/cons/str_unit.dart deleted file mode 100644 index 25686e497..000000000 --- a/packages/app/lib/app/cons/str_unit.dart +++ /dev/null @@ -1,42 +0,0 @@ -/// create by 张风捷特烈 on 2020/11/29 -/// contact me by email 1981462002@qq.com -/// 说明: - -class StrUnit { - - // 小文字大小 - static const String version = 'V1.5.1'; - static const String appName = 'Flutter Unit'; - - static const String galleryInfo = """ -[ - { - "image":"assets/images/anim_draw.webp", - "name":"基础绘制", - "info":"收录一些基础图形绘制案例,这些案例对初涉绘制的编程者会非常友好。通过这些案例,可以学会点、线、矩形、圆、圆弧、文字、图片等基本图形的绘制方法,了解 Canvas、Paint、Path 等绘制中核心对象的使用。" - }, - { - "image":"assets/images/draw_bg3.webp", - "name":"动画手势", - "info":"收录一些动画和手势的绘制案例,这些案例会让绘制更具有操作性。通过这些案例,可以学会动画和手势的使用,如滑动、旋转、缩放、移动等效果,让绘制不再只是静态展现。" - }, - { - "image":"assets/images/base_draw.webp", - "name":"粒子绘制", - "info":"收录一些粒子相关的绘制案例,这些案例将是绘制的顶级操作。通过这些案例,可以学会如何使用粒子来绘制惊艳的视觉效果,如粒子时钟、粒子爆炸、粒子背景等效果,让绘制拥有无限可能。" - }, - { - "image":"assets/images/draw_bg4.webp", - "name":"趣味绘制", - "info":"收录一些比较有趣的绘制案例,让我们一起在这里一起体验绘制的乐趣、编程的乐趣和智慧的乐趣吧。" - }, - { - "image":"assets/images/caver.webp", - "name":"艺术画廊", - "info":"收录一些殿堂级的绘制案例,这些案例将是绘制的巅峰作品,它们的没有任何的实用性,也不为任何需求而生,它们仅是因为存在而存在,是人类智慧和表达的媒介,称谓艺术。" - } -] -"""; - - -} \ No newline at end of file diff --git a/packages/app/lib/app/router/router_utils.dart b/packages/app/lib/app/router/router_utils.dart deleted file mode 100644 index f5516417f..000000000 --- a/packages/app/lib/app/router/router_utils.dart +++ /dev/null @@ -1,5 +0,0 @@ -import 'dart:io'; - -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - diff --git a/packages/app/lib/app/router/unit_router.dart b/packages/app/lib/app/router/unit_router.dart deleted file mode 100644 index 651ecd3b0..000000000 --- a/packages/app/lib/app/router/unit_router.dart +++ /dev/null @@ -1,33 +0,0 @@ -// ignore_for_file: constant_identifier_names -class UnitRouter { - static const String widget_detail = '/widget_detail'; - - static const String detail = 'detail'; - static const String search = 'search_bloc'; - static const String nav = 'nav'; - - static const String collect = 'CollectPage'; - static const String point = 'IssuesPointPage'; - static const String point_detail = 'IssuesDetailPage'; - - static const String setting = 'SettingPage'; - static const String font_setting = 'FountSettingPage'; - static const String theme_color_setting = 'ThemeColorSettingPage'; - static const String code_style_setting = 'CodeStyleSettingPage'; - static const String item_style_setting = 'ItemStyleSettingPage'; - static const String version_info = 'VersionInfo'; - static const String login = 'login'; - - static const String category_show = 'CategoryShow'; - static const String issues_point = 'IssuesPointPage'; - - static const String attr = 'AttrUnitPage'; - static const String bug = 'BugUnitPage'; - static const String layout = 'LayoutUnitPage'; - static const String about_me = 'AboutMePage'; - static const String about_app = 'AboutAppPage'; - static const String register = 'register'; - - static const String data_manage = 'DataManagePage'; - -} diff --git a/packages/app/lib/app/style/behavior/no_scroll_behavior.dart b/packages/app/lib/app/style/behavior/no_scroll_behavior.dart deleted file mode 100644 index 84c41938d..000000000 --- a/packages/app/lib/app/style/behavior/no_scroll_behavior.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/6/16 -/// contact me by email 1981462002@qq.com -/// 说明: - -class NoScrollBehavior extends ScrollBehavior { - @override - Widget buildViewportChrome( - BuildContext context, Widget child, AxisDirection axisDirection) => child; -} \ No newline at end of file diff --git a/packages/app/lib/app/theme/app_theme.dart b/packages/app/lib/app/theme/app_theme.dart deleted file mode 100644 index 0013f792b..000000000 --- a/packages/app/lib/app/theme/app_theme.dart +++ /dev/null @@ -1,99 +0,0 @@ -import 'dart:io'; - -import 'package:app/app.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -class AppTheme { - static ThemeData darkTheme(AppState state) { - Color scaffoldBackgroundColor = const Color(0xff010201); - - - SystemUiOverlayStyle overlayStyle = const SystemUiOverlayStyle( - statusBarColor: Colors.transparent, - statusBarBrightness: Brightness.dark, - statusBarIconBrightness: Brightness.light, - ); - bool useMaterial3 = (Platform.isAndroid || Platform.isIOS); - - return ThemeData( - scaffoldBackgroundColor: scaffoldBackgroundColor, - - tabBarTheme: TabBarTheme( - dividerColor: Colors.transparent, - ), - fontFamily: state.fontFamily, - useMaterial3:useMaterial3, - brightness: Brightness.dark, - primaryColor: const Color(0xff4699FB), - listTileTheme: ListTileThemeData( - tileColor: Color(0xff181818), - textColor: Color(0xffD6D6D6), - ), - appBarTheme: AppBarTheme( - systemOverlayStyle: overlayStyle, - elevation: 0, - centerTitle: true, - backgroundColor: Color(0xff181818), - - iconTheme: IconThemeData(color: Color(0xffCCCCCC)), - - titleTextStyle: TextStyle(fontSize: 16, fontWeight: FontWeight.bold,color: Color(0xffCCCCCC))), - floatingActionButtonTheme: const FloatingActionButtonThemeData( - foregroundColor: Colors.white, backgroundColor: Color(0xff4699FB)), - dividerTheme: DividerThemeData( - color: const Color(0xff2F2F2F), - space: px1, - thickness: px1, - ), - bottomNavigationBarTheme: const BottomNavigationBarThemeData( - backgroundColor: Color(0xff181818), - selectedItemColor: Color(0xff4699FB)), - ); - } - - static ThemeData lightTheme(AppState state) { - SystemUiOverlayStyle overlayStyle = const SystemUiOverlayStyle( - statusBarColor: Colors.transparent, - statusBarBrightness: Brightness.light, - statusBarIconBrightness: Brightness.dark, - ); - - bool useMaterial3 = (Platform.isAndroid || Platform.isIOS); - - return ThemeData( - fontFamily: state.fontFamily, - primaryColor: state.themeColor, - scaffoldBackgroundColor: Color(0xffF3F4F6), - useMaterial3: useMaterial3, - // Android 使用 Material3 - chipTheme: ChipThemeData(padding: EdgeInsets.symmetric(horizontal: 10)), - listTileTheme: ListTileThemeData( - tileColor: Colors.white, - ), - dividerTheme: DividerThemeData( - color: const Color(0xffDEE0E2), - space: px1, - thickness: px1, - ), - tabBarTheme: TabBarTheme( - dividerColor: Colors.transparent, - splashFactory: NoSplash.splashFactory, - overlayColor: MaterialStateProperty.resolveWith( - (Set states) { - return states.contains(MaterialState.focused) ? null : Colors.transparent; - }, - ), - ), - bottomNavigationBarTheme: const BottomNavigationBarThemeData( - backgroundColor: Colors.white), - appBarTheme: AppBarTheme( - systemOverlayStyle: overlayStyle, - elevation: 0, - centerTitle: true, - backgroundColor: Colors.white, - titleTextStyle: TextStyle(fontSize: 16, fontWeight: FontWeight.bold,color: Colors.black), - ), - ); - } -} diff --git a/packages/app/lib/bloc/global_bloc.dart b/packages/app/lib/bloc/global_bloc.dart deleted file mode 100644 index 45bf6a04b..000000000 --- a/packages/app/lib/bloc/global_bloc.dart +++ /dev/null @@ -1,100 +0,0 @@ -import 'dart:async'; - -import 'package:connectivity_plus/connectivity_plus.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:storage/storage.dart'; - -import '../model/app_style.dart'; -import '../repository/app_state_repository.dart'; -import '../model/global_state.dart'; - -/// create by 张风捷特烈 on 2020-03-22 -/// contact me by email 1981462002@qq.com -/// 说明: 全局信息的bloc - -class AppBloc extends Cubit { - - final Connectivity _connectivity = Connectivity(); - - - final AppStateRepository storage; - late StreamSubscription _subscription; - - AppBloc(this.storage) : super(const AppState()){ - _subscription = _connectivity.onConnectivityChanged.listen(_onNetConnectChange); - } - - void _onNetConnectChange(ConnectivityResult event) { - emit(state.copyWith(netConnect: event,)); - } - - - @override - Future close() async{ - _subscription.cancel(); - super.close(); - } - - - // 程序初始化事件处理: 使用 AppStorage 进行初始化 - void initApp() async { - emit(await storage.initApp()); - } - - AppConfigCao get cao => storage.sp.appConfig; - - // 切换字体事件处理 : 固化索引 + 产出新状态 - void switchFontFamily(String family) async { - AppState newState = state.copyWith(fontFamily: family); - cao.write(newState.toAppConfigPo()); - emit(newState); - } - - // 切换主题色事件处理 : 固化索引 + 产出新状态 - void switchThemeColor(MaterialColor color) async { - AppState newState = state.copyWith(themeColor: color); - cao.write(newState.toAppConfigPo()); - emit(newState); - } - - // 切换背景显示事件处理 : 固化数据 + 产出新状态 - void switchShowBg(bool show) async { - AppState newState = state.copyWith(showBackGround: show); - cao.write(newState.toAppConfigPo()); - emit(newState); - } - - // 切换背景显示事件处理 : 产出新状态 - void switchShowOver(bool show) async { - AppState newState = state.copyWith(showPerformanceOverlay: show); - cao.write(newState.toAppConfigPo()); - emit(newState); - } - - // 切换code样式事件处理 : 固化索引 + 产出新状态 - void switchCoderTheme(int codeStyleIndex) async { - AppState newState = state.copyWith(codeStyleIndex: codeStyleIndex); - cao.write(newState.toAppConfigPo()); - emit(newState); - } - - // 切换item样式事件处理 : 固化索引 + 产出新状态 - void changeItemStyle(int index) async { - AppState newState = state.copyWith(itemStyleIndex: index); - cao.write(newState.toAppConfigPo()); - emit(newState); - } - - void changeThemeMode(ThemeMode style) async{ - AppState newState = state.copyWith(themeMode: style); - cao.write(newState.toAppConfigPo()); - emit(newState); - } - - void switchShowTool(bool show) async{ - AppState newState = state.copyWith(showOverlayTool: show); - cao.write(newState.toAppConfigPo()); - emit(newState); - } -} diff --git a/packages/app/lib/bloc/update/bloc.dart b/packages/app/lib/bloc/update/bloc.dart deleted file mode 100644 index 5ee8657aa..000000000 --- a/packages/app/lib/bloc/update/bloc.dart +++ /dev/null @@ -1,71 +0,0 @@ -// import 'dart:async'; -// -// import 'package:flutter_bloc/flutter_bloc.dart'; -// import 'package:flutter_unit/app/utils/http_utils/task_result.dart'; -// import 'package:flutter_unit/point_system/api/app_info.dart'; -// import 'package:package_info_plus/package_info_plus.dart'; -// import 'package:r_upgrade/r_upgrade.dart'; -// -// import 'event.dart'; -// import 'state.dart'; -// -// class UpdateBloc extends Bloc { -// UpdateBloc() : super(const NoUpdateState()) { -// on(_onCheckUpdate); -// on(_onResetNoUpdate); -// on(_onDownloadEvent); -// on(_onDownloadingEvent); -// } -// -// void _onCheckUpdate(CheckUpdate event, Emitter emit) async { -// emit(const CheckLoadingState()); -// // await Future.delayed(Duration(seconds: 1)); -// // 检测更新逻辑 -// ResultBean result = await AppInfoApi.getAppVersion(appName: event.appName); -// PackageInfo packageInfo = await PackageInfo.fromPlatform(); -// -// if (result.status && result.data != null) { -// if (packageInfo.version == result.data!.appVersion) { -// emit(NoUpdateState( -// isChecked: true, -// checkTime: DateTime.now().millisecondsSinceEpoch, -// )); -// } else { -// if (result.data != null) { -// emit(ShouldUpdateState( -// oldVersion: packageInfo.version, info: result.data!)); -// } -// } -// } else { -// emit(CheckErrorState(error: result.msg)); -// } -// } -// -// void _onResetNoUpdate(ResetNoUpdate event, Emitter emit) { -// emit(const NoUpdateState()); -// } -// -// late int? id; -// late StreamSubscription? subscription; -// -// void _onDownloadEvent(DownloadEvent event, Emitter emit) async{ -// id = await RUpgrade.upgrade(event.appInfo.appUrl, -// fileName: '${event.appInfo.appName}.apk', isAutoRequestInstall: true); -// subscription = RUpgrade.stream.listen((DownloadInfo info) { -// double progress = (info.percent ?? 0) / 100; -// if (info.status! == DownloadStatus.STATUS_SUCCESSFUL) { -// progress = 1; -// subscription?.cancel(); -// add(const ResetNoUpdate()); -// } -// add(DownloadingEvent(state: DownloadingState( -// appSize: event.appInfo.appSize, -// progress: progress -// ))); -// }); -// } -// -// void _onDownloadingEvent(DownloadingEvent event, Emitter emit) { -// emit(event.state); -// } -// } diff --git a/packages/app/lib/bloc/update/event.dart b/packages/app/lib/bloc/update/event.dart deleted file mode 100644 index 0c75303c5..000000000 --- a/packages/app/lib/bloc/update/event.dart +++ /dev/null @@ -1,44 +0,0 @@ -// import 'package:equatable/equatable.dart'; -// import 'package:flutter_unit/point_system/api/app_info.dart'; -// -// import 'state.dart'; -// -// abstract class UpdateEvent extends Equatable { -// const UpdateEvent(); -// } -// -// // 检查更新 ---> 校验,转换状态 -// class CheckUpdate extends UpdateEvent { -// final String appName; -// -// const CheckUpdate({required this.appName}); -// -// @override -// List get props => [appName]; -// } -// -// class DownloadEvent extends UpdateEvent { -// final AppInfo appInfo; -// -// const DownloadEvent({required this.appInfo}); -// -// @override -// List get props => [appInfo]; -// } -// -// class DownloadingEvent extends UpdateEvent { -// final DownloadingState state; -// -// const DownloadingEvent({required this.state}); -// -// @override -// List get props => [state]; -// } -// -// // 将状态重置为 NoUpdateState -// class ResetNoUpdate extends UpdateEvent { -// const ResetNoUpdate(); -// -// @override -// List get props => []; -// } diff --git a/packages/app/lib/bloc/update/state.dart b/packages/app/lib/bloc/update/state.dart deleted file mode 100644 index b4804d69f..000000000 --- a/packages/app/lib/bloc/update/state.dart +++ /dev/null @@ -1,61 +0,0 @@ -// import 'package:equatable/equatable.dart'; -// import 'package:flutter_unit/point_system/api/app_info.dart'; -// -// abstract class UpdateState extends Equatable { -// const UpdateState(); -// } -// -// class NoUpdateState extends UpdateState { -// final bool isChecked; -// final int checkTime; -// -// const NoUpdateState({this.isChecked = false, this.checkTime = 0}); -// -// @override -// List get props => [isChecked, checkTime]; -// } -// -// class CheckLoadingState extends UpdateState { -// const CheckLoadingState(); -// @override -// List get props => []; -// } -// -// class DownloadingState extends UpdateState { -// final double progress; -// final int appSize; -// -// const DownloadingState({required this.progress, required this.appSize}); -// -// @override -// List get props => [progress, appSize]; -// } -// -// class CheckErrorState extends UpdateState { -// final String error; -// -// const CheckErrorState({required this.error}); -// -// @override -// List get props => [error]; -// -// @override -// String toString() { -// return 'CheckErrorState{error: $error}'; -// } -// } -// -// class ShouldUpdateState extends UpdateState { -// final String oldVersion; -// final AppInfo info; -// -// const ShouldUpdateState({required this.oldVersion, required this.info}); -// -// @override -// List get props => [oldVersion, info]; -// -// @override -// String toString() { -// return 'ShouldUpdateState{oldVersion: $oldVersion, info: $info}'; -// } -// } diff --git a/packages/app/lib/model/global_state.dart b/packages/app/lib/model/global_state.dart deleted file mode 100644 index 4faada5cf..000000000 --- a/packages/app/lib/model/global_state.dart +++ /dev/null @@ -1,133 +0,0 @@ -import 'package:app/app/cons/cons.dart'; -import 'package:components/toly_ui/toly_ui.dart'; -import 'package:connectivity_plus/connectivity_plus.dart'; -import 'package:equatable/equatable.dart'; -import 'package:flutter/material.dart'; -import 'package:storage/storage.dart'; - -import 'app_style.dart'; - -/// create by 张风捷特烈 on 2020-04-11 -/// contact me by email 1981462002@qq.com -/// 说明: 全局状态类 - -Map themeMode2Str = const { - ThemeMode.system: "跟随系统", - ThemeMode.light: "浅色模式", - ThemeMode.dark: "深色模式", -}; - -class AppState extends Equatable { - - /// [fontFamily] 文字字体 - final String fontFamily; - - /// [themeColor] 主题色 - final MaterialColor themeColor; - - /// [showBackGround] 是否显示主页背景图 - final bool showBackGround; - - /// [codeStyleIndex] 代码样式 索引 - final int codeStyleIndex; - - /// [itemStyleIndex] 主页item样式 索引 - final int itemStyleIndex; - - /// [showPerformanceOverlay] 是否显示性能浮层 - final bool showPerformanceOverlay; - - /// [showOverlayTool] 是否显示浮动工具 - final bool showOverlayTool; - - /// [appStyle] app 深色样式; - final ThemeMode themeMode; - final ConnectivityResult netConnect; - final String dbPath; - - const AppState({ - this.fontFamily = 'ComicNeue', - this.themeColor = Colors.blue, - this.themeMode = ThemeMode.system, - this.showBackGround = true, - this.codeStyleIndex = 0, - this.itemStyleIndex = 0, - this.showPerformanceOverlay = false, - this.showOverlayTool = true, - this.dbPath = '', - this.netConnect = ConnectivityResult.none, - }); - - @override - List get props => [ - fontFamily, - themeColor, - showBackGround, - codeStyleIndex, - itemStyleIndex, - themeMode, - showOverlayTool, - showPerformanceOverlay, - netConnect, - ]; - - AppState copyWith({ - String? fontFamily, - String? dbPath, - MaterialColor? themeColor, - bool? showBackGround, - int? codeStyleIndex, - int? itemStyleIndex, - bool? showPerformanceOverlay, - bool? showOverlayTool, - ThemeMode? themeMode, - ConnectivityResult? netConnect, - }) => - AppState( - fontFamily: fontFamily ?? this.fontFamily, - themeColor: themeColor ?? this.themeColor, - showBackGround: showBackGround ?? this.showBackGround, - codeStyleIndex: codeStyleIndex ?? this.codeStyleIndex, - showOverlayTool: showOverlayTool ?? this.showOverlayTool, - itemStyleIndex: itemStyleIndex ?? this.itemStyleIndex, - themeMode: themeMode ?? this.themeMode, - showPerformanceOverlay: showPerformanceOverlay ?? this.showPerformanceOverlay, - netConnect: netConnect ?? this.netConnect, - dbPath: dbPath ?? this.dbPath, - ); - - - // 将 AppState 状态数据转换为配置对象,以便存储 - AppConfigPo toAppConfigPo() => AppConfigPo( - showBackGround : showBackGround, - showOverlayTool : showOverlayTool, - showPerformanceOverlay : showPerformanceOverlay, - fontFamilyIndex : Cons.kFontFamilySupport.indexOf(fontFamily), - themeColorIndex : Cons.kThemeColorSupport.keys.toList().indexOf(themeColor), - codeStyleIndex : codeStyleIndex, - themeModeIndex : themeMode.index, - itemStyleIndex : itemStyleIndex, - ); - - // 根据存储的配置信息对象,形成 AppState 状态数据 - factory AppState.fromPo(AppConfigPo po) { - return AppState( - fontFamily: Cons.kFontFamilySupport[po.fontFamilyIndex], - themeColor: Cons.kThemeColorSupport.keys.toList()[po.themeColorIndex], - showBackGround: po.showBackGround, - codeStyleIndex: po.codeStyleIndex, - itemStyleIndex: po.itemStyleIndex, - showPerformanceOverlay: po.showPerformanceOverlay, - showOverlayTool: po.showOverlayTool, - themeMode: ThemeMode.values[po.themeModeIndex], - ); - } - - HighlighterStyle get codeStyle => Cons.codeThemeSupport.keys.toList()[codeStyleIndex]; - - - @override - String toString() { - return 'AppState{fontFamily: $fontFamily, themeColor: $themeColor, showBackGround: $showBackGround, codeStyleIndex: $codeStyleIndex, itemStyleIndex: $itemStyleIndex, showPerformanceOverlay: $showPerformanceOverlay}'; - } -} diff --git a/packages/app/lib/platform_adapter/views/window_buttons.dart b/packages/app/lib/platform_adapter/views/window_buttons.dart deleted file mode 100644 index 714687acf..000000000 --- a/packages/app/lib/platform_adapter/views/window_buttons.dart +++ /dev/null @@ -1,80 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:window_manager/window_manager.dart'; - -class WindowButtons extends StatefulWidget { - final List? actions; - const WindowButtons({Key? key, this.actions}) : super(key: key); - - @override - State createState() => _WindowButtonsState(); -} - -class _WindowButtonsState extends State { - @override - Widget build(BuildContext context) { - Brightness brightness = Theme.of(context).brightness; - return Align( - alignment:Alignment.topRight,child: Wrap( - spacing: 5, - children: [ - if(widget.actions!=null) - ...widget.actions!, - SizedBox( - width: 30, - height: 30, - child: WindowCaptionButton.minimize( - brightness:brightness, - onPressed: () async { - bool isMinimized = await windowManager.isMinimized(); - if (isMinimized) { - windowManager.restore(); - } else { - windowManager.minimize(); - } - }, - ), - ), - SizedBox( - width: 30, - height: 30, - child: FutureBuilder( - future: windowManager.isMaximized(), - builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.data == true) { - return WindowCaptionButton.unmaximize( - brightness: brightness, - onPressed: () async{ - await windowManager.unmaximize(); - setState(() { - - }); - }, - ); - } - return WindowCaptionButton.maximize( - brightness: brightness, - onPressed: () async{ - await windowManager.maximize(); - setState(() { - - }); - }, - ); - }, - ), - ), - SizedBox( - height: 30, - width: 30, - child: WindowCaptionButton.close( - brightness: brightness, - onPressed: () { - windowManager.close(); - }, - ), - ), - ], - ), - ); - } -} diff --git a/packages/app/lib/platform_adapter/window/windows_adapter.dart b/packages/app/lib/platform_adapter/window/windows_adapter.dart deleted file mode 100644 index e4f90cb96..000000000 --- a/packages/app/lib/platform_adapter/window/windows_adapter.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'dart:io'; - -import 'package:flutter/material.dart'; -import 'package:window_manager/window_manager.dart'; - -class WindowsAdapter { - - static Future setSize() async { - if (Platform.isMacOS || Platform.isWindows || Platform.isLinux) { - //仅对桌面端进行尺寸设置 - await windowManager.ensureInitialized(); - WindowOptions windowOptions = const WindowOptions( - size: Size(900,680), - minimumSize: Size(900,680), - center: true, - backgroundColor: Colors.transparent, - skipTaskbar: false, - titleBarStyle: TitleBarStyle.hidden, - ); - windowManager.waitUntilReadyToShow(windowOptions, () async { - await windowManager.setTitleBarStyle(TitleBarStyle.hidden,windowButtonVisibility: false); - await windowManager.show(); - await windowManager.focus(); - }); - } - } - -} - - -class DragToMoveAreaNoDouble extends StatelessWidget { - final Widget child; - - const DragToMoveAreaNoDouble({ - Key? key, - required this.child, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - if(Platform.isAndroid||Platform.isIOS) return child; - return GestureDetector( - behavior: HitTestBehavior.translucent, - onPanStart: (details) { - windowManager.startDragging(); - }, - child: child, - ); - } -} \ No newline at end of file diff --git a/packages/app/lib/repository/app_state_repository.dart b/packages/app/lib/repository/app_state_repository.dart deleted file mode 100644 index f8520697b..000000000 --- a/packages/app/lib/repository/app_state_repository.dart +++ /dev/null @@ -1,86 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:app/model/global_state.dart'; -import 'package:app/model/app_style.dart'; -import 'package:connectivity_plus/connectivity_plus.dart'; -import 'package:storage/storage.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:shared_preferences/shared_preferences.dart'; -import 'package:path/path.dart' as path; - -import '../app/cons/cons.dart'; -import '../app/cons/sp.dart'; - -class AppStateRepository { - SpStorage get sp => SpStorage.instance; - - Future initApp() async { - DbOpenHelper.setupDatabase(); - //数据库不存在,执行拷贝 - String databasesPath = await DbOpenHelper.getDbDirPath(); - String dbPath = path.join(databasesPath, "flutter.db"); - await SpStorage.instance.initSp(); - // 读取配置文件,初始化应用状态 - AppConfigPo po = await sp.appConfig.read(); - - bool shouldCopy = await _checkShouldCopy(dbPath, sp.spf); - - if (shouldCopy) { - await _doCopyAssetsDb(dbPath); - } else { - print("=====flutter.db 已存在===="); - } - - await FlutterDbStorage.instance.initDb(); - await AppDbStorage.instance.initDb(); - - final ConnectivityResult netConnect = - await (Connectivity().checkConnectivity()); - - return AppState.fromPo(po).copyWith( - netConnect: netConnect, - dbPath: dbPath, - ); - } - - Future _doCopyAssetsDb(String dbPath) async { - Directory dir = Directory(path.dirname(dbPath)); - if (!dir.existsSync()) { - await dir.create(recursive: true); - } - ByteData data = await rootBundle.load("assets/flutter.db"); - List bytes = - data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); - await File(dbPath).writeAsBytes(bytes, flush: true); - - print("=====flutter.db==== assets ======拷贝完成===="); - } - - Future _checkShouldCopy(String dbPath, SharedPreferences prefs) async { - bool shouldCopy = false; - String versionStr = await rootBundle.loadString('assets/version.json'); - int dbVersion = await json.decode(versionStr)['dbVersion']; - int versionInSP = prefs.getInt(SpKey.dbVersionKey) ?? -1; - - // 版本升级,执行拷贝 - if (dbVersion > versionInSP) { - shouldCopy = true; - await prefs.setInt(SpKey.dbVersionKey, dbVersion); - } - - //非 release模式,执行拷贝 - const isPro = bool.fromEnvironment('dart.vm.product'); - if (!isPro) { - shouldCopy = true; - } - - //数据库不存在,执行拷贝 - if (!File(dbPath).existsSync()) { - shouldCopy = true; - } - - return shouldCopy; - } -} diff --git a/packages/app/pubspec.yaml b/packages/app/pubspec.yaml deleted file mode 100644 index d1bf04658..000000000 --- a/packages/app/pubspec.yaml +++ /dev/null @@ -1,64 +0,0 @@ -name: app -description: A new Flutter package project. -version: 0.0.1 -homepage: - -environment: - sdk: '>=2.18.5 <3.0.0' - flutter: ">=1.17.0" - -dependencies: - flutter: - sdk: flutter - flutter_bloc: ^8.1.3 # 状态管理 - equatable: ^2.0.5 # 相等辅助 - shared_preferences: ^2.2.1 # xml 固化 - connectivity_plus: ^4.0.2 - window_manager: ^0.3.7 #桌面尺寸 - storage: - path: ../storage - widget_repository: - path: ../widget_repository - components: - path: ../components -dev_dependencies: - flutter_test: - sdk: flutter - flutter_lints: ^2.0.0 - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter packages. -flutter: - - # To add assets to your package, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - # - # For details regarding assets in packages, see - # https://flutter.dev/assets-and-images/#from-packages - # - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware - - # To add custom fonts to your package, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts in packages, see - # https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/app_update/lib/app_update.dart b/packages/app_update/lib/app_update.dart deleted file mode 100644 index 129f3ec6f..000000000 --- a/packages/app_update/lib/app_update.dart +++ /dev/null @@ -1,8 +0,0 @@ -library app_update; - -export 'bloc/bloc.dart'; -export 'bloc/state.dart'; -export 'bloc/event.dart'; -export 'model/app_info.dart'; -export 'views/app_update_panel.dart'; -export 'views/update_red_point.dart'; \ No newline at end of file diff --git a/packages/app_update/lib/bloc/bloc.dart b/packages/app_update/lib/bloc/bloc.dart deleted file mode 100644 index bb31221a5..000000000 --- a/packages/app_update/lib/bloc/bloc.dart +++ /dev/null @@ -1,72 +0,0 @@ -import 'dart:async'; - -import 'package:flutter_bloc/flutter_bloc.dart'; - -import 'package:package_info_plus/package_info_plus.dart'; -import 'package:r_upgrade/r_upgrade.dart'; -import 'package:utils/utils.dart'; - -import '../model/app_info.dart'; -import 'event.dart'; -import 'state.dart'; - -class UpdateBloc extends Bloc { - UpdateBloc() : super(const NoUpdateState()) { - on(_onCheckUpdate); - on(_onResetNoUpdate); - on(_onDownloadEvent); - on(_onDownloadingEvent); - } - - void _onCheckUpdate(CheckUpdate event, Emitter emit) async { - emit(const CheckLoadingState()); - // await Future.delayed(Duration(seconds: 1)); - // 检测更新逻辑 - TaskResult result = await AppInfoApi.getAppVersion(appName: event.appName); - PackageInfo packageInfo = await PackageInfo.fromPlatform(); - - if (result.success && result.data != null) { - if (packageInfo.version == result.data!.appVersion) { - emit(NoUpdateState( - isChecked: true, - checkTime: DateTime.now().millisecondsSinceEpoch, - )); - } else { - if (result.data != null) { - emit(ShouldUpdateState( - oldVersion: packageInfo.version, info: result.data!)); - } - } - } else { - emit(CheckErrorState(error: result.msg)); - } - } - - void _onResetNoUpdate(ResetNoUpdate event, Emitter emit) { - emit(const NoUpdateState()); - } - - late int? id; - late StreamSubscription? subscription; - - void _onDownloadEvent(DownloadEvent event, Emitter emit) async{ - id = await RUpgrade.upgrade(event.appInfo.appUrl, - fileName: '${event.appInfo.appName}.apk',); - subscription = RUpgrade.stream.listen((DownloadInfo info) { - double progress = (info.percent ?? 0) / 100; - if (info.status! == DownloadStatus.STATUS_SUCCESSFUL) { - progress = 1; - subscription?.cancel(); - add(const ResetNoUpdate()); - } - add(DownloadingEvent(state: DownloadingState( - appSize: event.appInfo.appSize, - progress: progress - ))); - }); - } - - void _onDownloadingEvent(DownloadingEvent event, Emitter emit) { - emit(event.state); - } -} diff --git a/packages/app_update/lib/bloc/event.dart b/packages/app_update/lib/bloc/event.dart deleted file mode 100644 index 111549454..000000000 --- a/packages/app_update/lib/bloc/event.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:equatable/equatable.dart'; - - -import '../model/app_info.dart'; -import 'state.dart'; - -abstract class UpdateEvent extends Equatable { - const UpdateEvent(); -} - -// 检查更新 ---> 校验,转换状态 -class CheckUpdate extends UpdateEvent { - final String appName; - - const CheckUpdate({required this.appName}); - - @override - List get props => [appName]; -} - -class DownloadEvent extends UpdateEvent { - final AppInfo appInfo; - - const DownloadEvent({required this.appInfo}); - - @override - List get props => [appInfo]; -} - -class DownloadingEvent extends UpdateEvent { - final DownloadingState state; - - const DownloadingEvent({required this.state}); - - @override - List get props => [state]; -} - -// 将状态重置为 NoUpdateState -class ResetNoUpdate extends UpdateEvent { - const ResetNoUpdate(); - - @override - List get props => []; -} diff --git a/packages/app_update/lib/bloc/state.dart b/packages/app_update/lib/bloc/state.dart deleted file mode 100644 index eb3c78bb7..000000000 --- a/packages/app_update/lib/bloc/state.dart +++ /dev/null @@ -1,63 +0,0 @@ -import 'package:equatable/equatable.dart'; - -import '../model/app_info.dart'; - - -abstract class UpdateState extends Equatable { - const UpdateState(); -} - -class NoUpdateState extends UpdateState { - final bool isChecked; - final int checkTime; - - const NoUpdateState({this.isChecked = false, this.checkTime = 0}); - - @override - List get props => [isChecked, checkTime]; -} - -class CheckLoadingState extends UpdateState { - const CheckLoadingState(); - @override - List get props => []; -} - -class DownloadingState extends UpdateState { - final double progress; - final int appSize; - - const DownloadingState({required this.progress, required this.appSize}); - - @override - List get props => [progress, appSize]; -} - -class CheckErrorState extends UpdateState { - final String error; - - const CheckErrorState({required this.error}); - - @override - List get props => [error]; - - @override - String toString() { - return 'CheckErrorState{error: $error}'; - } -} - -class ShouldUpdateState extends UpdateState { - final String oldVersion; - final AppInfo info; - - const ShouldUpdateState({required this.oldVersion, required this.info}); - - @override - List get props => [oldVersion, info]; - - @override - String toString() { - return 'ShouldUpdateState{oldVersion: $oldVersion, info: $info}'; - } -} diff --git a/packages/app_update/lib/model/app_info.dart b/packages/app_update/lib/model/app_info.dart deleted file mode 100644 index 245abd18a..000000000 --- a/packages/app_update/lib/model/app_info.dart +++ /dev/null @@ -1,56 +0,0 @@ -import 'package:app/app.dart'; -import 'package:equatable/equatable.dart'; -import 'package:utils/utils.dart'; - -class AppInfoApi { - static Future> getAppVersion( - {required String appName}) async { - String errorMsg = ""; - var result; - try { - result = - await HttpUtil.instance.client.get(PathUnit.appInfo + "/$appName"); - } catch (err) { - errorMsg = err.toString(); - } - print("=====${errorMsg}====="); - // 获取的数据非空且 status = true - if (result.data != null && result.data['status']) { - // 说明有数据 - if (result.data['data'] != null) { - return TaskResult.success( - data: AppInfo( - appName: result.data['data']['appName'], - appVersion: result.data['data']['appVersion'], - appUrl: result.data['data']['appUrl'], - appSize: result.data['data']['appSize'], - )); - } else { - return const TaskResult.success(data: null); - } - } - return TaskResult.error(msg: '请求错误: $errorMsg'); - } -} - -class AppInfo extends Equatable { - final String appName; - final String appVersion; - final String appUrl; - final int appSize; - - const AppInfo({ - required this.appName, - required this.appVersion, - required this.appUrl, - required this.appSize, - }); - - @override - List get props => [appName, appVersion, appUrl, appSize]; - - @override - String toString() { - return 'AppInfo{appName: $appName, appVersion: $appVersion, appUrl: $appUrl, appSize: $appSize}'; - } -} diff --git a/packages/app_update/lib/views/app_update_panel.dart b/packages/app_update/lib/views/app_update_panel.dart deleted file mode 100644 index 9220787c2..000000000 --- a/packages/app_update/lib/views/app_update_panel.dart +++ /dev/null @@ -1,129 +0,0 @@ -import 'dart:io'; - -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:r_upgrade/r_upgrade.dart'; -import 'package:utils/utils.dart'; - -import '../bloc/bloc.dart'; -import '../bloc/event.dart'; -import '../bloc/state.dart'; - -class AppUpdatePanel extends StatelessWidget { - const AppUpdatePanel({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return BlocConsumer( - builder: _buildByUpdateState, - listener: _listenerByUpdateState, - ); - } - - Widget _buildProgress(BuildContext context, double progress, int appSize) { - return Wrap( - alignment: WrapAlignment.center, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Column( - children: [ - Text( - '${(progress * 100).toStringAsFixed(2)} %', - style: const TextStyle(height: 1, fontSize: 12, color: Colors.grey), - ), - const SizedBox( - height: 5, - ), - Text( - '${convertFileSize((appSize * progress).floor())}/${convertFileSize(appSize)}', - style: const TextStyle(height: 1, fontSize: 10, color: Colors.grey), - ), - ], - ), - const SizedBox( - width: 15, - ), - SizedBox( - width: 20, - height: 20, - child: CircularProgressIndicator( - strokeWidth: 2, - backgroundColor: Colors.grey, - value: progress, - ), - ) - ]); - } - - Widget _buildByUpdateState(BuildContext context, UpdateState state) { - String info = '检查新版本'; - Widget trail = const SizedBox.shrink(); - if (state is ShouldUpdateState) { - info = "下载新版本"; - trail = Wrap( - alignment: WrapAlignment.center, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Text( - '${state.oldVersion} --> ${state.info.appVersion} ', - style: const TextStyle(height: 1, fontSize: 12, color: Colors.grey), - ), - const SizedBox(width: 5), - const Icon(Icons.update, color: Colors.green) - ]); - } - if (state is CheckLoadingState) { - trail = const CupertinoActivityIndicator(); - } - if (state is DownloadingState) { - info = "新版本下载中..."; - trail = _buildProgress(context, state.progress, state.appSize); - } - - return ListTile( - title: Text( - info, - style: const TextStyle(fontSize: 13), - ), - trailing: trail, - onTap: () => _tapByState(state, context), - ); - } - - void _tapByState(UpdateState state, BuildContext context) { - if (state is NoUpdateState) { - BlocProvider.of(context) - .add(const CheckUpdate(appName: 'FlutterUnit')); - } - if (state is ShouldUpdateState) { - if(Platform.isIOS){ - // ios 跳转应用商店 - RUpgrade.upgradeFromAppStore('6450545123', false); - return; - } - // 处理下载的事件 - BlocProvider.of(context) - .add(DownloadEvent(appInfo: state.info)); - } - } - - void _listenerByUpdateState(BuildContext context, UpdateState state) { - if (state is NoUpdateState) { - if (state.isChecked) { - Toast.success(context, '当前应用已是最新版本!'); - } - } - } - - String convertFileSize(int size){ - double result = size / 1024.0; - if(result<1024){ - return "${result.toStringAsFixed(2)} Kb"; - }else if(result>1024&&result<1024*1024){ - return "${(result/1024).toStringAsFixed(2)} Mb"; - }else{ - return "${(result/1024/1024).toStringAsFixed(2)} Gb"; - } - } -} diff --git a/packages/app_update/pubspec.yaml b/packages/app_update/pubspec.yaml deleted file mode 100644 index cc10eafe7..000000000 --- a/packages/app_update/pubspec.yaml +++ /dev/null @@ -1,62 +0,0 @@ -name: app_update -description: A new Flutter package project. -version: 0.0.1 -homepage: - -environment: - sdk: '>=2.18.6 <3.0.0' - flutter: ">=1.17.0" - -dependencies: - flutter: - sdk: flutter - flutter_bloc: ^8.1.3 # 状态管理 - equatable: ^2.0.5 # 相等辅助 - shared_preferences: ^2.2.1 # xml 固化 - r_upgrade: ^0.4.2 # 应用升级 - package_info_plus: ^4.0.2 - utils: - path: ../utils - app: - path: ../app -dev_dependencies: - flutter_test: - sdk: flutter - flutter_lints: ^2.0.0 - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter packages. -flutter: - - # To add assets to your package, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - # - # For details regarding assets in packages, see - # https://flutter.dev/assets-and-images/#from-packages - # - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware - - # To add custom fonts to your package, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts in packages, see - # https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/app_update/test/app_update_test.dart b/packages/app_update/test/app_update_test.dart deleted file mode 100644 index e68d1c97a..000000000 --- a/packages/app_update/test/app_update_test.dart +++ /dev/null @@ -1,12 +0,0 @@ -// import 'package:flutter_test/flutter_test.dart'; -// -// import 'package:app_update/app_update.dart'; -// -// void main() { -// test('adds one to input values', () { -// final calculator = Calculator(); -// expect(calculator.addOne(2), 3); -// expect(calculator.addOne(-7), -6); -// expect(calculator.addOne(0), 1); -// }); -// } diff --git a/packages/artifact/lib/artifact.dart b/packages/artifact/lib/artifact.dart deleted file mode 100644 index fd14d4614..000000000 --- a/packages/artifact/lib/artifact.dart +++ /dev/null @@ -1,7 +0,0 @@ -library artifact; - -export 'src/views/artifact_page.dart'; -export 'src/views/wrapper/refresh.dart'; - -export 'src/repositories/db/article_dao.dart'; -export 'src/repositories/db/columnize_dao.dart'; \ No newline at end of file diff --git a/packages/artifact/lib/src/blocs/article/bloc.dart b/packages/artifact/lib/src/blocs/article/bloc.dart deleted file mode 100644 index eefd759b0..000000000 --- a/packages/artifact/lib/src/blocs/article/bloc.dart +++ /dev/null @@ -1,86 +0,0 @@ -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:utils/utils.dart'; - -import '../../repositories/model/article.dart'; -import '../../repositories/repository/article_repository.dart'; - -class ArticleBloc extends Cubit { - ArticleBloc( - this.repository, { - this.pageSize = 20, - this.groupId, - }) : super(ArticleState.empty()); - - final int pageSize; - final int? groupId; - final ArticleRepository repository; - - /// 初始化时,加载 [pageSize] 条记录 - /// - void init() { - _loadDataFromDb(1, pageSize, filter: groupId?.toString(), requestNet: true); - } - - Future loadNextPageMore() async { - int curPage = state.data.length ~/ pageSize; - int nextPage = curPage + 1; - ArticleFilter filter = ArticleFilter( - page: nextPage, - pageSize: pageSize, - groupId: groupId, - ); - List
result = await repository.queryByDb(filter); - int count = await repository.total(filter); - emit(ArticleWithData( - data: state.data + result, - total: count, - )); - } - - Future _loadDataFromDb( - int page, - int pageSize, { - bool requestNet = false, - String? filter, - }) async { - ArticleFilter filter = ArticleFilter( - page: page, - pageSize: pageSize, - groupId: groupId, - ); - List
data = await repository.queryByDb(filter); - - // 没有内存缓存 并且数据库有数据 - if (data.isNotEmpty) { - emit(ArticleWithData(data: data, total: data.length)); - } - } - - @override - Future close() async { - super.close(); - print("=======close:${groupId}=================="); - } -} - -sealed class ArticleState { - final List
data; - - const ArticleState({this.data = const []}); - - factory ArticleState.empty() => const ArticleWithData(); -} - -class ArticleLoading extends ArticleState { - const ArticleLoading({super.data}); -} - -class ArticleWithData extends ArticleState { - final int total; - const ArticleWithData({super.data, this.total = 0}); -} - -class ArticleFailed extends ArticleState { - final String error; - const ArticleFailed(this.error, {super.data}); -} diff --git a/packages/artifact/lib/src/blocs/columnize/bloc.dart b/packages/artifact/lib/src/blocs/columnize/bloc.dart deleted file mode 100644 index ce9315715..000000000 --- a/packages/artifact/lib/src/blocs/columnize/bloc.dart +++ /dev/null @@ -1,41 +0,0 @@ -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:utils/utils.dart'; - -import '../../repositories/model/columnize.dart'; -import '../../repositories/repository/columnize_repository.dart'; - -class ColumnizeBloc extends Cubit { - final ColumnizeRepository repository; - - ColumnizeBloc(this.repository) : super(ColumnizeState.initial()); - - void init() { - _loadDataFromDb(requestNet: true); - } - - Future _loadDataFromDb({bool requestNet = false}) async { - /// - List data = await repository.queryByDb(); - - // - if (data.isNotEmpty) { - emit(ColumnizeState(data)); - } - } -} - -class ColumnizeState { - List data; - - ColumnizeState(this.data); - - factory ColumnizeState.initial() => ColumnizeState([ - Columnize(title: '-', url: '-'), - Columnize(title: '-', url: '-'), - Columnize(title: '-', url: '-'), - Columnize(title: '-', url: '-'), - Columnize(title: '-', url: '-'), - Columnize(title: '-', url: '-'), - Columnize(title: '-', url: '-'), - ]); -} diff --git a/packages/artifact/lib/src/repositories/db/article_dao.dart b/packages/artifact/lib/src/repositories/db/article_dao.dart deleted file mode 100644 index 10bf51327..000000000 --- a/packages/artifact/lib/src/repositories/db/article_dao.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'package:artifact/src/repositories/exp.dart'; -import 'package:sqflite/sqflite.dart'; - - - -class ArticleDao { - final Database database; - - ArticleDao(this.database); - - // 表名 - static const String kTableName = 'article'; - - Future insert(Article po) => database.insert( - kTableName, - po.toJson(), - conflictAlgorithm: ConflictAlgorithm.replace, - ); - - Future insertOrUpdate(Article po) async { - bool canUpdate = await shouldUpdate(po.id, po.update); - return database.insert( - kTableName, - po.toJson(), - conflictAlgorithm: - canUpdate ? ConflictAlgorithm.replace : ConflictAlgorithm.ignore, - ); - } - - /// 当前数据是否需要更新 - Future shouldUpdate(int id, int updateAt) async { - List> data = await database - .rawQuery("SELECT `update` FROM $kTableName WHERE id = ?", [id]); - // 没有数据,可以更新 - if (data.isEmpty) { - return true; - } - // 服务器中数据更新时间,大于本地数据库内容,可以更新 - return updateAt > data.first['update']; - } - - Future> query(ArticleFilter filter) async { - String queryArgs = ''; - List args = []; - if(filter.filter !=null){ - queryArgs+="AND filter = ? "; - args.add(filter.filter); - } - if(filter.groupId !=null){ - if(queryArgs.isEmpty){ - queryArgs +='WHERE groupId = ? '; - }else{ - queryArgs+="AND groupId = ? "; - } - args.add(filter.groupId); - } - queryArgs += 'LIMIT ? OFFSET ?'; - args.addAll([filter.pageSize,filter.offset]); - - List> data = await database.rawQuery( - "SELECT * FROM $kTableName $queryArgs", - args, - ); - - List
result = data.map(Article.fromDb).toList(); - return result; - } - - Future total(ArticleFilter filter) async{ - bool hasGroupId = filter.groupId != null; - String familySql = hasGroupId ? 'WHERE groupId = ?' : ''; - List familyArg = hasGroupId ? [filter.groupId!] : []; - - String sql = "SELECT count(id) as `count` FROM article $familySql"; - - List> result = await database.rawQuery(sql,familyArg); - if(result.isNotEmpty){ - return result.first['count'] as int ??0; - } - return 0; - } -} diff --git a/packages/artifact/lib/src/repositories/db/columnize_dao.dart b/packages/artifact/lib/src/repositories/db/columnize_dao.dart deleted file mode 100644 index 96d1a282e..000000000 --- a/packages/artifact/lib/src/repositories/db/columnize_dao.dart +++ /dev/null @@ -1,64 +0,0 @@ -import 'package:artifact/src/repositories/exp.dart'; -import 'package:sqflite/sqflite.dart'; - - - -class ColumnizeDao { - final Database database; - - ColumnizeDao(this.database); - - // 表名 - static const String kTableName = 'columnize'; - - Future insert(Columnize po) => database.insert( - kTableName, - po.toJson(), - conflictAlgorithm: ConflictAlgorithm.replace, - ); - - Future insertOrUpdate(Columnize po) async { - bool canUpdate = await shouldUpdate(po.id, po.update); - return database.insert( - kTableName, - po.toJson(), - conflictAlgorithm: - canUpdate ? ConflictAlgorithm.replace : ConflictAlgorithm.ignore, - ); - } - - /// 当前数据是否需要更新 - Future shouldUpdate(int id, int updateAt) async { - List> data = await database - .rawQuery("SELECT `update` FROM $kTableName WHERE id = ?", [id]); - // 没有数据,可以更新 - if (data.isEmpty) { - return true; - } - // 服务器中数据更新时间,大于本地数据库内容,可以更新 - return updateAt > data.first['update']; - } - - Future> query({ - int page = 1, - int pageSize = 20, - String? filter, - }) async { - String queryArgs = ''; - List args = []; - if(filter!=null){ - queryArgs+="AND filter = ? "; - args.add(filter); - } - queryArgs += 'LIMIT ? OFFSET ?'; - args.addAll([pageSize, (page - 1) * pageSize]); - - List> data = await database.rawQuery( - "SELECT * FROM $kTableName $queryArgs", - args, - ); - - List result = data.map(Columnize.fromDb).toList(); - return result; - } -} diff --git a/packages/artifact/lib/src/repositories/exp.dart b/packages/artifact/lib/src/repositories/exp.dart deleted file mode 100644 index 3ce49787e..000000000 --- a/packages/artifact/lib/src/repositories/exp.dart +++ /dev/null @@ -1,4 +0,0 @@ -export 'repository/article_repository.dart'; -export 'repository/columnize_repository.dart'; -export 'model/columnize.dart'; -export 'model/article.dart'; \ No newline at end of file diff --git a/packages/artifact/lib/src/repositories/model/article.dart b/packages/artifact/lib/src/repositories/model/article.dart deleted file mode 100644 index 99b0b2be0..000000000 --- a/packages/artifact/lib/src/repositories/model/article.dart +++ /dev/null @@ -1,103 +0,0 @@ - -class Article { - final String? username; - final String title; - final String? subtitle; - final String url; - final String? cover; - final int create; - final int update; - final int id; - final int userId; - final int groupId; - - Article({ - this.username = '', - required this.title, - this.subtitle = '', - required this.url, - this.cover = '', - this.update = 0, - this.create = 0, - required this.userId, - this.id = -1, - required this.groupId, - - }); - - Map toJson() => - { - "id": id, - "userId": userId, - "groupId": groupId, - "username": username, - "title": title, - "createAt": create, - "subtitle": subtitle, - "url": url, - "cover": cover, - "updateAt": update, - }; - - factory Article.fromMap(dynamic map)=> - Article( - id: map['articleId'] ?? '', - username: map['userName'] ?? '', - userId: map['userId'] ?? '', - title: map['title'] ?? '', - create: DateTime.parse(map['createAt']).millisecondsSinceEpoch, - update: DateTime.parse(map['updateAt']).millisecondsSinceEpoch, - subtitle: map['subtitle'] ?? '', - url: map['url'] ?? '', - groupId: map['groupId'] ?? 1, - cover: map['caver'] ?? '', - ); - - factory Article.fromDb(dynamic map)=> - Article( - id: map['id'] ?? '', - username: map['username'] ?? '', - userId: map['userId'] ?? '', - title: map['title'] ?? '', - create: map['createAt'] ?? 0 , - update: map['updateAt'] ?? 0, - subtitle: map['subtitle'] ?? '', - url: map['url'] ?? '', - groupId: map['groupId'] ?? 1, - cover: map['cover'] ?? '', - ); -} - -class ArticleFilter{ - final String? filter; - final int? groupId; - final int page; - final int pageSize; - - const ArticleFilter({ - this.filter, - this.groupId, - this.page = 1, - this.pageSize = 20, - }); - - int get offset =>pageSize*(page-1); - - ArticleFilter copyWith({ - String? filter, - int? groupId, - int? page, - }) { - return ArticleFilter( - filter: filter ?? this.filter, - groupId: groupId ?? this.groupId, - page: page ?? this.page, - pageSize: pageSize - ); - } - - @override - String toString() { - return 'ArticleFilter{filter: $filter, groupId: $groupId, page: $page, pageSize: $pageSize}'; - } -} \ No newline at end of file diff --git a/packages/artifact/lib/src/repositories/repository/article_repository.dart b/packages/artifact/lib/src/repositories/repository/article_repository.dart deleted file mode 100644 index 67cc2d83f..000000000 --- a/packages/artifact/lib/src/repositories/repository/article_repository.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:dio/dio.dart'; -import 'package:storage/storage.dart'; -import 'package:utils/utils.dart'; - -import '../../../artifact.dart'; -import '../exp.dart'; - -// 仓储: 提供数据 -class ArticleRepository { - const ArticleRepository(); - - ArticleDao get dao => FlutterDbStorage.instance.articleDao; - - // 从数据库加载资源 - Future> queryByDb(ArticleFilter filter) async { - List
caches = await dao.query(filter); - return caches; - } - - Future total(ArticleFilter filter) => dao.total(filter); -} diff --git a/packages/artifact/lib/src/repositories/repository/columnize_repository.dart b/packages/artifact/lib/src/repositories/repository/columnize_repository.dart deleted file mode 100644 index 266d6ad56..000000000 --- a/packages/artifact/lib/src/repositories/repository/columnize_repository.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:dio/dio.dart'; -import 'package:storage/storage.dart'; -import 'package:utils/utils.dart'; - -import '../../../artifact.dart'; -import '../model/columnize.dart'; - -// 仓储: 提供数据 -class ColumnizeRepository { - - const ColumnizeRepository(); - - ColumnizeDao get dao => FlutterDbStorage.instance.columnizeDao; - - - // 从数据库加载资源 - Future> queryByDb({ - int page = 1, - int pageSize = 20, - }) async { - List caches = await dao.query( - page: page, - pageSize: pageSize, - ); - return caches; - } - - -} diff --git a/packages/artifact/lib/src/views/article/sliver_article.dart b/packages/artifact/lib/src/views/article/sliver_article.dart deleted file mode 100644 index a76262a11..000000000 --- a/packages/artifact/lib/src/views/article/sliver_article.dart +++ /dev/null @@ -1,148 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -import '../../blocs/exp.dart'; -import '../../repositories/exp.dart'; -import 'article_detail_page.dart'; -import 'columnize_page_view.dart'; - -class SliverArticlePanel extends StatelessWidget { - const SliverArticlePanel({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - ArticleState state = context.watch().state; - return switch (state) { - ArticleLoading() => SliverToBoxAdapter( - child: const CupertinoActivityIndicator(), - ), - ArticleWithData() => SliverArticle( - data: state.data, - ), - ArticleFailed() => SliverToBoxAdapter( - child: Text('error'), - ), - }; - } -} - -class SliverArticle extends StatelessWidget { - final List
data; - const SliverArticle({Key? key, required this.data}) : super(key: key); - - @override - Widget build(BuildContext context) { - return SliverPadding( - padding: const EdgeInsets.only(bottom: 0), - sliver: SliverList( - delegate: SliverChildBuilderDelegate( - _buildItem, - childCount: data.length, - ))); - } - - Widget? _buildItem(BuildContext context, int index) { - return ArticlePanel(article: data[index]); - } -} - -class ArticlePanel extends StatelessWidget { - final Article article; - - const ArticlePanel({Key? key, required this.article}) : super(key: key); - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: () { - Navigator.of(context).push( - MaterialPageRoute( - builder: (_) => ArticleDetailPage(article: article), - ), - ); - }, - child: Container( - color: Colors.white, - padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), - margin: EdgeInsets.only(bottom: 6), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - CircleAvatar( - backgroundImage: AssetImage( - 'assets/images/icon_head.webp', - ), - backgroundColor: Colors.transparent, - radius: 10, - ), - const SizedBox( - width: 6, - ), - Expanded( - child: Text('${article.username}', - style: - TextStyle(color: Color(0xff6A6D76), fontSize: 12))), - Text( - '掘金', - style: TextStyle(fontSize: 12, color: Color(0xff6A6D76)), - ), - ], - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 4.0), - child: Text( - article.title, - maxLines: 2, - style: TextStyle(fontWeight: FontWeight.bold), - ), - ), - Row( - children: [ - Expanded( - child: Text( - '${article.subtitle}', - maxLines: 4, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: 12, - color: Color( - 0xffA3A3A3, - )), - )), - if (article.cover != null && article.cover!.isNotEmpty) - Padding( - padding: const EdgeInsets.only(left: 8.0), - child: ClipRRect( - borderRadius: BorderRadius.circular(8), - child: Image.network( - article.cover!, - width: 110, - )), - ) - ], - ), - const SizedBox( - height: 4, - ), - Row( - children: [ - Spacer(), - Text( - '更新时间: ${formatLong.format(DateTime.fromMillisecondsSinceEpoch(article.update, isUtc: true))}', - style: TextStyle( - color: Color( - 0xff6A6D76, - ), - fontSize: 12), - ), - ], - ), - ], - ), - ), - ); - } -} diff --git a/packages/artifact/lib/src/views/article/sliver_columnize.dart b/packages/artifact/lib/src/views/article/sliver_columnize.dart deleted file mode 100644 index 6f4ad2ea4..000000000 --- a/packages/artifact/lib/src/views/article/sliver_columnize.dart +++ /dev/null @@ -1,75 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:url_launcher/url_launcher.dart'; - -import 'columnize_page_view.dart'; - -class ColumnizeViewPage extends StatefulWidget { - const ColumnizeViewPage({Key? key}) : super(key: key); - - @override - State createState() => _ColumnizeViewPageState(); -} - -class _ColumnizeViewPageState extends State { - - late PageController _ctrl; - - @override - void initState() { - super.initState(); - _ctrl = PageController(viewportFraction: 0.9); - } - - @override - Widget build(BuildContext context) { - return ColoredBox( - color: Colors.white, - child: SizedBox( - height: 220, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: EdgeInsets.only(left: 16.0,right: 16,top: 12,bottom: 4), - child: Row( - children: [ - CircleAvatar( - backgroundImage: AssetImage('assets/images/icon_head.webp',), - backgroundColor: Colors.transparent, - radius: 10, - ), - SizedBox(width: 6,), - Text("捷特文章专栏",style: TextStyle(fontSize: 16,fontWeight: FontWeight.bold),), - Spacer(), - GestureDetector( - onTap: () async{ - Uri uri = Uri.parse('https://juejin.im/user/5b42c0656fb9a04fe727eb37'); - if (await canLaunchUrl(uri)) { - await launchUrl(uri,mode:LaunchMode.externalNonBrowserApplication ); - } else { - debugPrint('Could not launch ${uri.path}'); - } - }, - child: Wrap( - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Text("前往掘金",style: TextStyle(fontSize: 12,color: Colors.blue),), - Icon(Icons.navigate_next,size: 12,color: Colors.blue,) - ], - ), - ), - - ], - ), - ), - Expanded( - child: ColumnizePageView(), - ), - SizedBox(height: 10,) - ], - ), - ), - ); - } - -} diff --git a/packages/artifact/lib/src/views/building/building_panel.dart b/packages/artifact/lib/src/views/building/building_panel.dart deleted file mode 100644 index a8998916f..000000000 --- a/packages/artifact/lib/src/views/building/building_panel.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:flutter/material.dart'; - -class BuildingPanel extends StatelessWidget { - const BuildingPanel({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Icon(Icons.add_chart,size: 36,color: Colors.grey,), - const SizedBox(height: 8,), - Text('正在建设中',style: TextStyle(color: Colors.grey),), - ], - ), - ); - } -} diff --git a/packages/artifact/lib/src/views/wrapper/refresh.dart b/packages/artifact/lib/src/views/wrapper/refresh.dart deleted file mode 100644 index 64a6c2da9..000000000 --- a/packages/artifact/lib/src/views/wrapper/refresh.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:refresh/refresh.dart'; - -import '../toly_refresh_indicator.dart'; - -class RefreshConfigWrapper extends StatelessWidget { - final Widget child; - const RefreshConfigWrapper({Key? key, required this.child}) : super(key: key); - - @override - Widget build(BuildContext context) { - Color themeColor = Theme.of(context).primaryColor; - return RefreshConfiguration( - headerTriggerDistance: 60, - topHitBoundary: 20, - child: child, - headerBuilder: () => const TolyRefreshIndicator(), - footerBuilder: () => CustomFooter( - builder: (BuildContext context, LoadStatus? mode) { - Widget body; - if (mode == LoadStatus.idle) { - body = Wrap( - alignment: WrapAlignment.center, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Icon(Icons.arrow_upward, color: themeColor), - Text("上拉加载", - style: TextStyle(color: themeColor, height: 1)), - ], - ); - } else if (mode == LoadStatus.loading) { - body = CupertinoActivityIndicator(); - } else if (mode == LoadStatus.failed) { - body = Text("加载失败!点击重试!"); - } else if (mode == LoadStatus.canLoading) { - body = Text("松手,加载更多!", - style: TextStyle(color: themeColor, height: 1)); - } else { - body = Text("没有更多数据了!", - style: TextStyle( - color: Colors.grey, - )); - } - return Container( - height: 55.0, - child: Center(child: body), - ); - }, - ), - ); - } -} diff --git a/packages/artifact/pubspec.yaml b/packages/artifact/pubspec.yaml deleted file mode 100644 index 8eb6bd8b6..000000000 --- a/packages/artifact/pubspec.yaml +++ /dev/null @@ -1,66 +0,0 @@ -name: artifact -description: artifact -version: 0.0.1 -homepage: - -environment: - sdk: ">=3.0.0 <4.0.0" - flutter: ">=2.17.0" - -dependencies: - flutter: - sdk: flutter - flutter_bloc: ^8.1.3 # 状态管理 - dio: ^5.3.2 # 网络请求 - webview_flutter: ^4.2.4 - url_launcher: ^6.1.14 # url - refresh: ^1.0.1 - storage: - path: ../storage - algorithm: - path: ../algorithm - utils: - path: ../utils - app: - path: ../app -dev_dependencies: - flutter_test: - sdk: flutter - flutter_lints: ^2.0.0 - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter packages. -flutter: - - # To add assets to your package, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - # - # For details regarding assets in packages, see - # https://flutter.dev/assets-and-images/#from-packages - # - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware - - # To add custom fonts to your package, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts in packages, see - # https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/authentication/lib/blocs/user/bloc.dart b/packages/authentication/lib/blocs/user/bloc.dart deleted file mode 100644 index eb82c52c3..000000000 --- a/packages/authentication/lib/blocs/user/bloc.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:flutter_bloc/flutter_bloc.dart'; - -import 'state.dart'; - -class UserBloc extends Cubit { - UserBloc():super(UserPerformance.fromJson({})); - - - -} \ No newline at end of file diff --git a/packages/authentication/lib/views/mobile/user/page_item.dart b/packages/authentication/lib/views/mobile/user/page_item.dart deleted file mode 100644 index 5a8100e62..000000000 --- a/packages/authentication/lib/views/mobile/user/page_item.dart +++ /dev/null @@ -1,81 +0,0 @@ -import 'package:app/app.dart'; -import 'package:app_update/views/update_red_point.dart'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-26 -/// contact me by email 1981462002@qq.com -/// 说明: - -class MePageItem extends StatelessWidget { - final Color color; - - const MePageItem({Key? key, this.color = Colors.white}) : super(key: key); - - @override - Widget build(BuildContext context) { - return _buildChild(context); - } - - Widget _buildChild(BuildContext context) { - return ScrollConfiguration( - behavior: NoScrollBehavior(), - child: ListView( - padding: EdgeInsets.zero, - children: [ - const SizedBox( - height: 10, - ), - Gap.sfl10, - _buildItem(context, TolyIcon.icon_them, '应用设置', UnitRouter.setting), - const Divider(), - _buildItem( - context, TolyIcon.icon_layout, '数据管理', UnitRouter.data_manage), - const Divider(), - _buildItem( - context, TolyIcon.icon_collect, '我的收藏', UnitRouter.collect,), - Gap.sfl10, - - Stack( - children: [ - _buildItem( - context, - Icons.update, - '版本信息', - UnitRouter.version_info, - ), - const Positioned(left: 40, top: 10, child: UpdateRedPoint()) - ], - ), - const Divider(), - _buildItem(context, Icons.info, '关于应用', UnitRouter.about_app), - - Gap.sfl10, - _buildItem(context, TolyIcon.icon_kafei, '联系本王', UnitRouter.about_me), - ], - ), - ); - } - - Widget _buildItem( - BuildContext context, IconData icon, String title, String linkTo, - {VoidCallback? onTap}) => - ListTile( - leading: Icon( - icon, - color: Theme.of(context).primaryColor, - ), - title: Text(title, style: const TextStyle(fontSize: 16)), - trailing: - Icon(Icons.chevron_right, color: Theme.of(context).primaryColor), - onTap: () { - if (linkTo.isNotEmpty) { - Object? arg ; - if(linkTo==UnitRouter.collect){ - arg = true; - } - Navigator.of(context).pushNamed(linkTo,arguments: arg); - if (onTap != null) onTap(); - } - }, - ); -} diff --git a/packages/authentication/pubspec.yaml b/packages/authentication/pubspec.yaml deleted file mode 100644 index c55419820..000000000 --- a/packages/authentication/pubspec.yaml +++ /dev/null @@ -1,64 +0,0 @@ -name: authentication -description: A new Flutter package project. -version: 0.0.1 -homepage: - -environment: - sdk: '>=2.18.5 <3.0.0' - flutter: ">=1.17.0" - -dependencies: - flutter: - sdk: flutter - flutter_bloc: ^8.1.3 # 状态管理 - equatable: ^2.0.5 # 相等辅助 - shared_preferences: ^2.2.1 # xml 固化 - utils: - path: ../utils - app_update: - path: ../app_update - app: - path: ../app - components: - path: ../components -dev_dependencies: - flutter_test: - sdk: flutter - flutter_lints: ^2.0.0 - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter packages. -flutter: - - # To add assets to your package, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - # - # For details regarding assets in packages, see - # https://flutter.dev/assets-and-images/#from-packages - # - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware - - # To add custom fonts to your package, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts in packages, see - # https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/components/lib/components.dart b/packages/components/lib/components.dart deleted file mode 100644 index 5fba8b549..000000000 --- a/packages/components/lib/components.dart +++ /dev/null @@ -1,5 +0,0 @@ -library components; - -export 'flutter_ui/flutter_ui.dart'; -export 'project_ui/project_ui.dart'; -export 'toly_ui/toly_ui.dart'; \ No newline at end of file diff --git a/packages/components/lib/flutter_ui/toly_date_picker.dart b/packages/components/lib/flutter_ui/toly_date_picker.dart deleted file mode 100644 index fd3dfd2d1..000000000 --- a/packages/components/lib/flutter_ui/toly_date_picker.dart +++ /dev/null @@ -1,2910 +0,0 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:math' as math; - -import 'package:flutter/gestures.dart' show DragStartBehavior; -import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; - -const Size _calendarPortraitDialogSize = Size(330.0, 518.0); -const Size _calendarLandscapeDialogSize = Size(496.0, 346.0); -const Size _inputPortraitDialogSize = Size(330.0, 270.0); -const Size _inputLandscapeDialogSize = Size(496, 160.0); -const Size _inputRangeLandscapeDialogSize = Size(496, 164.0); -const Duration _dialogSizeAnimationDuration = Duration(milliseconds: 200); -const double _inputFormPortraitHeight = 98.0; -const double _inputFormLandscapeHeight = 108.0; - -/// Shows a dialog containing a Material Design date picker. -/// -/// The returned [Future] resolves to the date selected by the user when the -/// user confirms the dialog. If the user cancels the dialog, null is returned. -/// -/// When the date picker is first displayed, it will show the month of -/// [initialDate], with [initialDate] selected. -/// -/// The [firstDate] is the earliest allowable date. The [lastDate] is the latest -/// allowable date. [initialDate] must either fall between these dates, -/// or be equal to one of them. For each of these [DateTime] parameters, only -/// their dates are considered. Their time fields are ignored. They must all -/// be non-null. -/// -/// The [currentDate] represents the current day (i.e. today). This -/// date will be highlighted in the day grid. If null, the date of -/// `DateTime.now()` will be used. -/// -/// An optional [initialEntryMode] argument can be used to display the date -/// picker in the [DatePickerEntryMode.calendar] (a calendar month grid) -/// or [DatePickerEntryMode.input] (a text input field) mode. -/// It defaults to [DatePickerEntryMode.calendar] and must be non-null. -/// -/// An optional [selectableDayPredicate] function can be passed in to only allow -/// certain days for selection. If provided, only the days that -/// [selectableDayPredicate] returns true for will be selectable. For example, -/// this can be used to only allow weekdays for selection. If provided, it must -/// return true for [initialDate]. -/// -/// The following optional string parameters allow you to override the default -/// text used for various parts of the dialog: -/// -/// * [helpText], label displayed at the top of the dialog. -/// * [cancelText], label on the cancel button. -/// * [confirmText], label on the ok button. -/// * [errorFormatText], message used when the input text isn't in a proper date format. -/// * [errorInvalidText], message used when the input text isn't a selectable date. -/// * [fieldHintText], text used to prompt the user when no text has been entered in the field. -/// * [fieldLabelText], label for the date text input field. -/// -/// An optional [locale] argument can be used to set the locale for the date -/// picker. It defaults to the ambient locale provided by [Localizations]. -/// -/// An optional [textDirection] argument can be used to set the text direction -/// ([TextDirection.ltr] or [TextDirection.rtl]) for the date picker. It -/// defaults to the ambient text direction provided by [Directionality]. If both -/// [locale] and [textDirection] are non-null, [textDirection] overrides the -/// direction chosen for the [locale]. -/// -/// The [context], [useRootNavigator] and [routeSettings] arguments are passed to -/// [showDialog], the documentation for which discusses how it is used. [context] -/// and [useRootNavigator] must be non-null. -/// -/// The [builder] parameter can be used to wrap the dialog widget -/// to add inherited widgets like [Theme]. -/// -/// An optional [initialDatePickerMode] argument can be used to have the -/// calendar date picker initially appear in the [DatePickerMode.year] or -/// [DatePickerMode.day] mode. It defaults to [DatePickerMode.day], and -/// must be non-null. -/// -/// {@macro flutter.widgets.RawDialogRoute} -/// -/// ### State Restoration -/// -/// Using this method will not enable state restoration for the date picker. -/// In order to enable state restoration for a date picker, use -/// [Navigator.restorablePush] or [Navigator.restorablePushNamed] with -/// [DatePickerDialog]. -/// -/// For more information about state restoration, see [RestorationManager]. -/// -/// {@macro flutter.widgets.RestorationManager} -/// -/// {@tool dartpad} -/// This sample demonstrates how to create a restorable Material date picker. -/// This is accomplished by enabling state restoration by specifying -/// [MaterialApp.restorationScopeId] and using [Navigator.restorablePush] to -/// push [DatePickerDialog] when the button is tapped. -/// -/// ** See code in examples/api/lib/material/date_picker/show_date_picker.0.dart ** -/// {@end-tool} -/// -/// See also: -/// -/// * [showDateRangePicker], which shows a Material Design date range picker -/// used to select a range of dates. -/// * [CalendarDatePicker], which provides the calendar grid used by the date picker dialog. -/// * [InputDatePickerFormField], which provides a text input field for entering dates. -/// * [DisplayFeatureSubScreen], which documents the specifics of how -/// [DisplayFeature]s can split the screen into sub-screens. -/// * [showTimePicker], which shows a dialog that contains a Material Design time picker. -/// -Future showDatePicker({ - required BuildContext context, - required DateTime initialDate, - required DateTime firstDate, - required DateTime lastDate, - DateTime? currentDate, - DatePickerEntryMode initialEntryMode = DatePickerEntryMode.calendar, - SelectableDayPredicate? selectableDayPredicate, - String? helpText, - String? cancelText, - String? confirmText, - Locale? locale, - bool useRootNavigator = true, - RouteSettings? routeSettings, - TextDirection? textDirection, - TransitionBuilder? builder, - DatePickerMode initialDatePickerMode = DatePickerMode.day, - String? errorFormatText, - String? errorInvalidText, - String? fieldHintText, - String? fieldLabelText, - TextInputType? keyboardType, - Offset? anchorPoint, -}) async { - assert(context != null); - assert(initialDate != null); - assert(firstDate != null); - assert(lastDate != null); - initialDate = DateUtils.dateOnly(initialDate); - firstDate = DateUtils.dateOnly(firstDate); - lastDate = DateUtils.dateOnly(lastDate); - assert( - !lastDate.isBefore(firstDate), - 'lastDate $lastDate must be on or after firstDate $firstDate.', - ); - assert( - !initialDate.isBefore(firstDate), - 'initialDate $initialDate must be on or after firstDate $firstDate.', - ); - assert( - !initialDate.isAfter(lastDate), - 'initialDate $initialDate must be on or before lastDate $lastDate.', - ); - assert( - selectableDayPredicate == null || selectableDayPredicate(initialDate), - 'Provided initialDate $initialDate must satisfy provided selectableDayPredicate.', - ); - assert(initialEntryMode != null); - assert(useRootNavigator != null); - assert(initialDatePickerMode != null); - assert(debugCheckHasMaterialLocalizations(context)); - - Widget dialog = DatePickerDialog( - initialDate: initialDate, - firstDate: firstDate, - lastDate: lastDate, - currentDate: currentDate, - initialEntryMode: initialEntryMode, - selectableDayPredicate: selectableDayPredicate, - helpText: helpText, - cancelText: cancelText, - confirmText: confirmText, - initialCalendarMode: initialDatePickerMode, - errorFormatText: errorFormatText, - errorInvalidText: errorInvalidText, - fieldHintText: fieldHintText, - fieldLabelText: fieldLabelText, - keyboardType: keyboardType, - ); - - if (textDirection != null) { - dialog = Directionality( - textDirection: textDirection, - child: dialog, - ); - } - - if (locale != null) { - dialog = Localizations.override( - context: context, - locale: locale, - child: dialog, - ); - } - - return showDialog( - context: context, - useRootNavigator: useRootNavigator, - routeSettings: routeSettings, - builder: (BuildContext context) { - return builder == null ? dialog : builder(context, dialog); - }, - anchorPoint: anchorPoint, - ); -} - -/// A Material-style date picker dialog. -/// -/// It is used internally by [showDatePicker] or can be directly pushed -/// onto the [Navigator] stack to enable state restoration. See -/// [showDatePicker] for a state restoration app example. -/// -/// See also: -/// -/// * [showDatePicker], which is a way to display the date picker. -class DatePickerDialog extends StatefulWidget { - /// A Material-style date picker dialog. - DatePickerDialog({ - super.key, - required DateTime initialDate, - required DateTime firstDate, - required DateTime lastDate, - DateTime? currentDate, - this.initialEntryMode = DatePickerEntryMode.calendar, - this.selectableDayPredicate, - this.cancelText, - this.confirmText, - this.helpText, - this.initialCalendarMode = DatePickerMode.day, - this.errorFormatText, - this.errorInvalidText, - this.fieldHintText, - this.fieldLabelText, - this.keyboardType, - this.restorationId, - }) : assert(initialDate != null), - assert(firstDate != null), - assert(lastDate != null), - initialDate = DateUtils.dateOnly(initialDate), - firstDate = DateUtils.dateOnly(firstDate), - lastDate = DateUtils.dateOnly(lastDate), - currentDate = DateUtils.dateOnly(currentDate ?? DateTime.now()), - assert(initialEntryMode != null), - assert(initialCalendarMode != null) { - assert( - !this.lastDate.isBefore(this.firstDate), - 'lastDate ${this.lastDate} must be on or after firstDate ${this.firstDate}.', - ); - assert( - !this.initialDate.isBefore(this.firstDate), - 'initialDate ${this.initialDate} must be on or after firstDate ${this.firstDate}.', - ); - assert( - !this.initialDate.isAfter(this.lastDate), - 'initialDate ${this.initialDate} must be on or before lastDate ${this.lastDate}.', - ); - assert( - selectableDayPredicate == null || selectableDayPredicate!(this.initialDate), - 'Provided initialDate ${this.initialDate} must satisfy provided selectableDayPredicate', - ); - } - - /// The initially selected [DateTime] that the picker should display. - final DateTime initialDate; - - /// The earliest allowable [DateTime] that the user can select. - final DateTime firstDate; - - /// The latest allowable [DateTime] that the user can select. - final DateTime lastDate; - - /// The [DateTime] representing today. It will be highlighted in the day grid. - final DateTime currentDate; - - /// The initial mode of date entry method for the date picker dialog. - /// - /// See [DatePickerEntryMode] for more details on the different data entry - /// modes available. - final DatePickerEntryMode initialEntryMode; - - /// Function to provide full control over which [DateTime] can be selected. - final SelectableDayPredicate? selectableDayPredicate; - - /// The text that is displayed on the cancel button. - final String? cancelText; - - /// The text that is displayed on the confirm button. - final String? confirmText; - - /// The text that is displayed at the top of the header. - /// - /// This is used to indicate to the user what they are selecting a date for. - final String? helpText; - - /// The initial display of the calendar picker. - final DatePickerMode initialCalendarMode; - - /// The error text displayed if the entered date is not in the correct format. - final String? errorFormatText; - - /// The error text displayed if the date is not valid. - /// - /// A date is not valid if it is earlier than [firstDate], later than - /// [lastDate], or doesn't pass the [selectableDayPredicate]. - final String? errorInvalidText; - - /// The hint text displayed in the [TextField]. - /// - /// If this is null, it will default to the date format string. For example, - /// 'mm/dd/yyyy' for en_US. - final String? fieldHintText; - - /// The label text displayed in the [TextField]. - /// - /// If this is null, it will default to the words representing the date format - /// string. For example, 'Month, Day, Year' for en_US. - final String? fieldLabelText; - - /// The keyboard type of the [TextField]. - /// - /// If this is null, it will default to [TextInputType.datetime] - final TextInputType? keyboardType; - - /// Restoration ID to save and restore the state of the [DatePickerDialog]. - /// - /// If it is non-null, the date picker will persist and restore the - /// date selected on the dialog. - /// - /// The state of this widget is persisted in a [RestorationBucket] claimed - /// from the surrounding [RestorationScope] using the provided restoration ID. - /// - /// See also: - /// - /// * [RestorationManager], which explains how state restoration works in - /// Flutter. - final String? restorationId; - - @override - State createState() => _DatePickerDialogState(); -} - -class _DatePickerDialogState extends State with RestorationMixin { - late final RestorableDateTime _selectedDate = RestorableDateTime(widget.initialDate); - late final _RestorableDatePickerEntryMode _entryMode = _RestorableDatePickerEntryMode(widget.initialEntryMode); - final _RestorableAutovalidateMode _autovalidateMode = _RestorableAutovalidateMode(AutovalidateMode.disabled); - - @override - String? get restorationId => widget.restorationId; - - @override - void restoreState(RestorationBucket? oldBucket, bool initialRestore) { - registerForRestoration(_selectedDate, 'selected_date'); - registerForRestoration(_autovalidateMode, 'autovalidateMode'); - registerForRestoration(_entryMode, 'calendar_entry_mode'); - } - - final GlobalKey _calendarPickerKey = GlobalKey(); - final GlobalKey _formKey = GlobalKey(); - - void _handleOk() { - if (_entryMode.value == DatePickerEntryMode.input || _entryMode.value == DatePickerEntryMode.inputOnly) { - final FormState form = _formKey.currentState!; - if (!form.validate()) { - setState(() => _autovalidateMode.value = AutovalidateMode.always); - return; - } - form.save(); - } - Navigator.pop(context, _selectedDate.value); - } - - void _handleCancel() { - Navigator.pop(context); - } - - void _handleEntryModeToggle() { - setState(() { - switch (_entryMode.value) { - case DatePickerEntryMode.calendar: - _autovalidateMode.value = AutovalidateMode.disabled; - _entryMode.value = DatePickerEntryMode.input; - break; - case DatePickerEntryMode.input: - _formKey.currentState!.save(); - _entryMode.value = DatePickerEntryMode.calendar; - break; - case DatePickerEntryMode.calendarOnly: - case DatePickerEntryMode.inputOnly: - assert(false, 'Can not change entry mode from _entryMode'); - break; - } - }); - } - - void _handleDateChanged(DateTime date) { - setState(() { - _selectedDate.value = date; - }); - } - - Size _dialogSize(BuildContext context) { - final Orientation orientation = MediaQuery.of(context).orientation; - switch (_entryMode.value) { - case DatePickerEntryMode.calendar: - case DatePickerEntryMode.calendarOnly: - switch (orientation) { - case Orientation.portrait: - return _calendarPortraitDialogSize; - case Orientation.landscape: - return _calendarLandscapeDialogSize; - } - case DatePickerEntryMode.input: - case DatePickerEntryMode.inputOnly: - switch (orientation) { - case Orientation.portrait: - return _inputPortraitDialogSize; - case Orientation.landscape: - return _inputLandscapeDialogSize; - } - } - } - - static const Map _formShortcutMap = { - // Pressing enter on the field will move focus to the next field or control. - SingleActivator(LogicalKeyboardKey.enter): NextFocusIntent(), - }; - - @override - Widget build(BuildContext context) { - final ThemeData theme = Theme.of(context); - final ColorScheme colorScheme = theme.colorScheme; - final MaterialLocalizations localizations = MaterialLocalizations.of(context); - final Orientation orientation = MediaQuery.of(context).orientation; - final TextTheme textTheme = theme.textTheme; - // Constrain the textScaleFactor to the largest supported value to prevent - // layout issues. - final double textScaleFactor = math.min(MediaQuery.of(context).textScaleFactor, 1.3); - - final String dateText = localizations.formatMediumDate(_selectedDate.value); - final Color onPrimarySurface = colorScheme.brightness == Brightness.light - ? colorScheme.onPrimary - : colorScheme.onSurface; - final TextStyle? dateStyle = orientation == Orientation.landscape - ? textTheme.headline5?.copyWith(color: onPrimarySurface) - : textTheme.headline4?.copyWith(color: onPrimarySurface); - - final Widget actions = Container( - alignment: AlignmentDirectional.centerEnd, - constraints: const BoxConstraints(minHeight: 52.0), - padding: const EdgeInsets.symmetric(horizontal: 8), - child: OverflowBar( - spacing: 8, - children: [ - TextButton( - onPressed: _handleCancel, - child: Text(widget.cancelText ?? localizations.cancelButtonLabel), - ), - TextButton( - onPressed: _handleOk, - child: Text(widget.confirmText ?? localizations.okButtonLabel), - ), - ], - ), - ); - - CalendarDatePicker calendarDatePicker() { - return CalendarDatePicker( - key: _calendarPickerKey, - initialDate: _selectedDate.value, - firstDate: widget.firstDate, - lastDate: widget.lastDate, - currentDate: widget.currentDate, - onDateChanged: _handleDateChanged, - selectableDayPredicate: widget.selectableDayPredicate, - initialCalendarMode: widget.initialCalendarMode, - ); - } - - Form inputDatePicker() { - return Form( - key: _formKey, - autovalidateMode: _autovalidateMode.value, - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 24), - height: orientation == Orientation.portrait ? _inputFormPortraitHeight : _inputFormLandscapeHeight, - child: Shortcuts( - shortcuts: _formShortcutMap, - child: Column( - children: [ - const Spacer(), - InputDatePickerFormField( - initialDate: _selectedDate.value, - firstDate: widget.firstDate, - lastDate: widget.lastDate, - onDateSubmitted: _handleDateChanged, - onDateSaved: _handleDateChanged, - selectableDayPredicate: widget.selectableDayPredicate, - errorFormatText: widget.errorFormatText, - errorInvalidText: widget.errorInvalidText, - fieldHintText: widget.fieldHintText, - fieldLabelText: widget.fieldLabelText, - keyboardType: widget.keyboardType, - autofocus: true, - ), - const Spacer(), - ], - ), - ), - ), - ); - } - - final Widget picker; - final Widget? entryModeButton; - switch (_entryMode.value) { - case DatePickerEntryMode.calendar: - picker = calendarDatePicker(); - entryModeButton = IconButton( - icon: const Icon(Icons.edit), - color: onPrimarySurface, - tooltip: localizations.inputDateModeButtonLabel, - onPressed: _handleEntryModeToggle, - ); - break; - - case DatePickerEntryMode.calendarOnly: - picker = calendarDatePicker(); - entryModeButton = null; - break; - - case DatePickerEntryMode.input: - picker = inputDatePicker(); - entryModeButton = IconButton( - icon: const Icon(Icons.calendar_today), - color: onPrimarySurface, - tooltip: localizations.calendarModeButtonLabel, - onPressed: _handleEntryModeToggle, - ); - break; - - case DatePickerEntryMode.inputOnly: - picker = inputDatePicker(); - entryModeButton = null; - break; - } - - final Widget header = _DatePickerHeader( - helpText: widget.helpText ?? localizations.datePickerHelpText, - titleText: dateText, - titleStyle: dateStyle, - orientation: orientation, - isShort: orientation == Orientation.landscape, - entryModeButton: entryModeButton, - ); - - final Size dialogSize = _dialogSize(context) * textScaleFactor; - return Dialog( - insetPadding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 24.0), - clipBehavior: Clip.antiAlias, - child: AnimatedContainer( - width: dialogSize.width, - height: dialogSize.height, - duration: _dialogSizeAnimationDuration, - curve: Curves.easeIn, - child: MediaQuery( - data: MediaQuery.of(context).copyWith( - textScaleFactor: textScaleFactor, - ), - child: Builder(builder: (BuildContext context) { - switch (orientation) { - case Orientation.portrait: - return Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - header, - Expanded(child: picker), - actions, - ], - ); - case Orientation.landscape: - return Row( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - header, - Flexible( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Expanded(child: picker), - actions, - ], - ), - ), - ], - ); - } - }), - ), - ), - ); - } -} - -// A restorable [DatePickerEntryMode] value. -// -// This serializes each entry as a unique `int` value. -class _RestorableDatePickerEntryMode extends RestorableValue { - _RestorableDatePickerEntryMode( - DatePickerEntryMode defaultValue, - ) : _defaultValue = defaultValue; - - final DatePickerEntryMode _defaultValue; - - @override - DatePickerEntryMode createDefaultValue() => _defaultValue; - - @override - void didUpdateValue(DatePickerEntryMode? oldValue) { - assert(debugIsSerializableForRestoration(value.index)); - notifyListeners(); - } - - @override - DatePickerEntryMode fromPrimitives(Object? data) => DatePickerEntryMode.values[data! as int]; - - @override - Object? toPrimitives() => value.index; -} - -// A restorable [AutovalidateMode] value. -// -// This serializes each entry as a unique `int` value. -class _RestorableAutovalidateMode extends RestorableValue { - _RestorableAutovalidateMode( - AutovalidateMode defaultValue, - ) : _defaultValue = defaultValue; - - final AutovalidateMode _defaultValue; - - @override - AutovalidateMode createDefaultValue() => _defaultValue; - - @override - void didUpdateValue(AutovalidateMode? oldValue) { - assert(debugIsSerializableForRestoration(value.index)); - notifyListeners(); - } - - @override - AutovalidateMode fromPrimitives(Object? data) => AutovalidateMode.values[data! as int]; - - @override - Object? toPrimitives() => value.index; -} - -/// Re-usable widget that displays the selected date (in large font) and the -/// help text above it. -/// -/// These types include: -/// -/// * Single Date picker with calendar mode. -/// * Single Date picker with text input mode. -/// * Date Range picker with text input mode. -/// -/// [helpText], [orientation], [icon], [onIconPressed] are required and must be -/// non-null. -class _DatePickerHeader extends StatelessWidget { - - /// Creates a header for use in a date picker dialog. - const _DatePickerHeader({ - required this.helpText, - required this.titleText, - this.titleSemanticsLabel, - required this.titleStyle, - required this.orientation, - this.isShort = false, - this.entryModeButton, - }) : assert(helpText != null), - assert(orientation != null), - assert(isShort != null); - - static const double _datePickerHeaderLandscapeWidth = 152.0; - static const double _datePickerHeaderPortraitHeight = 120.0; - static const double _headerPaddingLandscape = 16.0; - - /// The text that is displayed at the top of the header. - /// - /// This is used to indicate to the user what they are selecting a date for. - final String helpText; - - /// The text that is displayed at the center of the header. - final String titleText; - - /// The semantic label associated with the [titleText]. - final String? titleSemanticsLabel; - - /// The [TextStyle] that the title text is displayed with. - final TextStyle? titleStyle; - - /// The orientation is used to decide how to layout its children. - final Orientation orientation; - - /// Indicates the header is being displayed in a shorter/narrower context. - /// - /// This will be used to tighten up the space between the help text and date - /// text if `true`. Additionally, it will use a smaller typography style if - /// `true`. - /// - /// This is necessary for displaying the manual input mode in - /// landscape orientation, in order to account for the keyboard height. - final bool isShort; - - final Widget? entryModeButton; - - @override - Widget build(BuildContext context) { - final ThemeData theme = Theme.of(context); - final ColorScheme colorScheme = theme.colorScheme; - final TextTheme textTheme = theme.textTheme; - - // The header should use the primary color in light themes and surface color in dark - final bool isDark = colorScheme.brightness == Brightness.dark; - final Color primarySurfaceColor = isDark ? colorScheme.surface : colorScheme.primary; - final Color onPrimarySurfaceColor = isDark ? colorScheme.onSurface : colorScheme.onPrimary; - - final TextStyle? helpStyle = textTheme.overline?.copyWith( - color: onPrimarySurfaceColor, - ); - - final Text help = Text( - helpText, - style: helpStyle, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ); - final Text title = Text( - titleText, - semanticsLabel: titleSemanticsLabel ?? titleText, - style: titleStyle, - maxLines: orientation == Orientation.portrait ? 1 : 2, - overflow: TextOverflow.ellipsis, - ); - - switch (orientation) { - case Orientation.portrait: - return SizedBox( - height: _datePickerHeaderPortraitHeight, - child: Material( - color: primarySurfaceColor, - child: Padding( - padding: const EdgeInsetsDirectional.only( - start: 24, - end: 12, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 16), - help, - const Flexible(child: SizedBox(height: 38)), - Row( - children: [ - Expanded(child: title), - if (entryModeButton != null) - entryModeButton!, - ], - ), - ], - ), - ), - ), - ); - case Orientation.landscape: - return SizedBox( - width: _datePickerHeaderLandscapeWidth, - child: Material( - color: primarySurfaceColor, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 16), - Padding( - padding: const EdgeInsets.symmetric( - horizontal: _headerPaddingLandscape, - ), - child: help, - ), - SizedBox(height: isShort ? 16 : 56), - Expanded( - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: _headerPaddingLandscape, - ), - child: title, - ), - ), - if (entryModeButton != null) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 4), - child: entryModeButton, - ), - ], - ), - ), - ); - } - } -} - -/// Shows a full screen modal dialog containing a Material Design date range -/// picker. -/// -/// The returned [Future] resolves to the [DateTimeRange] selected by the user -/// when the user saves their selection. If the user cancels the dialog, null is -/// returned. -/// -/// If [initialDateRange] is non-null, then it will be used as the initially -/// selected date range. If it is provided, `initialDateRange.start` must be -/// before or on `initialDateRange.end`. -/// -/// The [firstDate] is the earliest allowable date. The [lastDate] is the latest -/// allowable date. Both must be non-null. -/// -/// If an initial date range is provided, `initialDateRange.start` -/// and `initialDateRange.end` must both fall between or on [firstDate] and -/// [lastDate]. For all of these [DateTime] values, only their dates are -/// considered. Their time fields are ignored. -/// -/// The [currentDate] represents the current day (i.e. today). This -/// date will be highlighted in the day grid. If null, the date of -/// `DateTime.now()` will be used. -/// -/// An optional [initialEntryMode] argument can be used to display the date -/// picker in the [DatePickerEntryMode.calendar] (a scrollable calendar month -/// grid) or [DatePickerEntryMode.input] (two text input fields) mode. -/// It defaults to [DatePickerEntryMode.calendar] and must be non-null. -/// -/// The following optional string parameters allow you to override the default -/// text used for various parts of the dialog: -/// -/// * [helpText], the label displayed at the top of the dialog. -/// * [cancelText], the label on the cancel button for the text input mode. -/// * [confirmText],the label on the ok button for the text input mode. -/// * [saveText], the label on the save button for the fullscreen calendar -/// mode. -/// * [errorFormatText], the message used when an input text isn't in a proper -/// date format. -/// * [errorInvalidText], the message used when an input text isn't a -/// selectable date. -/// * [errorInvalidRangeText], the message used when the date range is -/// invalid (e.g. start date is after end date). -/// * [fieldStartHintText], the text used to prompt the user when no text has -/// been entered in the start field. -/// * [fieldEndHintText], the text used to prompt the user when no text has -/// been entered in the end field. -/// * [fieldStartLabelText], the label for the start date text input field. -/// * [fieldEndLabelText], the label for the end date text input field. -/// -/// An optional [locale] argument can be used to set the locale for the date -/// picker. It defaults to the ambient locale provided by [Localizations]. -/// -/// An optional [textDirection] argument can be used to set the text direction -/// ([TextDirection.ltr] or [TextDirection.rtl]) for the date picker. It -/// defaults to the ambient text direction provided by [Directionality]. If both -/// [locale] and [textDirection] are non-null, [textDirection] overrides the -/// direction chosen for the [locale]. -/// -/// The [context], [useRootNavigator] and [routeSettings] arguments are passed -/// to [showDialog], the documentation for which discusses how it is used. -/// [context] and [useRootNavigator] must be non-null. -/// -/// The [builder] parameter can be used to wrap the dialog widget -/// to add inherited widgets like [Theme]. -/// -/// {@macro flutter.widgets.RawDialogRoute} -/// -/// ### State Restoration -/// -/// Using this method will not enable state restoration for the date range picker. -/// In order to enable state restoration for a date range picker, use -/// [Navigator.restorablePush] or [Navigator.restorablePushNamed] with -/// [DateRangePickerDialog]. -/// -/// For more information about state restoration, see [RestorationManager]. -/// -/// {@macro flutter.widgets.RestorationManager} -/// -/// {@tool sample} -/// This sample demonstrates how to create a restorable Material date range picker. -/// This is accomplished by enabling state restoration by specifying -/// [MaterialApp.restorationScopeId] and using [Navigator.restorablePush] to -/// push [DateRangePickerDialog] when the button is tapped. -/// -/// ** See code in examples/api/lib/material/date_picker/show_date_range_picker.0.dart ** -/// {@end-tool} -/// -/// See also: -/// -/// * [showDatePicker], which shows a Material Design date picker used to -/// select a single date. -/// * [DateTimeRange], which is used to describe a date range. -/// * [DisplayFeatureSubScreen], which documents the specifics of how -/// [DisplayFeature]s can split the screen into sub-screens. -Future showDateRangePicker({ - required BuildContext context, - DateTimeRange? initialDateRange, - required DateTime firstDate, - required DateTime lastDate, - DateTime? currentDate, - DatePickerEntryMode initialEntryMode = DatePickerEntryMode.calendar, - String? helpText, - String? cancelText, - String? confirmText, - String? saveText, - String? errorFormatText, - String? errorInvalidText, - String? errorInvalidRangeText, - String? fieldStartHintText, - String? fieldEndHintText, - String? fieldStartLabelText, - String? fieldEndLabelText, - Locale? locale, - bool useRootNavigator = true, - RouteSettings? routeSettings, - TextDirection? textDirection, - TransitionBuilder? builder, - Offset? anchorPoint, -}) async { - assert(context != null); - assert( - initialDateRange == null || (initialDateRange.start != null && initialDateRange.end != null), - 'initialDateRange must be null or have non-null start and end dates.', - ); - assert( - initialDateRange == null || !initialDateRange.start.isAfter(initialDateRange.end), - "initialDateRange's start date must not be after it's end date.", - ); - initialDateRange = initialDateRange == null ? null : DateUtils.datesOnly(initialDateRange); - assert(firstDate != null); - firstDate = DateUtils.dateOnly(firstDate); - assert(lastDate != null); - lastDate = DateUtils.dateOnly(lastDate); - assert( - !lastDate.isBefore(firstDate), - 'lastDate $lastDate must be on or after firstDate $firstDate.', - ); - assert( - initialDateRange == null || !initialDateRange.start.isBefore(firstDate), - "initialDateRange's start date must be on or after firstDate $firstDate.", - ); - assert( - initialDateRange == null || !initialDateRange.end.isBefore(firstDate), - "initialDateRange's end date must be on or after firstDate $firstDate.", - ); - assert( - initialDateRange == null || !initialDateRange.start.isAfter(lastDate), - "initialDateRange's start date must be on or before lastDate $lastDate.", - ); - assert( - initialDateRange == null || !initialDateRange.end.isAfter(lastDate), - "initialDateRange's end date must be on or before lastDate $lastDate.", - ); - currentDate = DateUtils.dateOnly(currentDate ?? DateTime.now()); - assert(initialEntryMode != null); - assert(useRootNavigator != null); - assert(debugCheckHasMaterialLocalizations(context)); - - Widget dialog = DateRangePickerDialog( - initialDateRange: initialDateRange, - firstDate: firstDate, - lastDate: lastDate, - currentDate: currentDate, - initialEntryMode: initialEntryMode, - helpText: helpText, - cancelText: cancelText, - confirmText: confirmText, - saveText: saveText, - errorFormatText: errorFormatText, - errorInvalidText: errorInvalidText, - errorInvalidRangeText: errorInvalidRangeText, - fieldStartHintText: fieldStartHintText, - fieldEndHintText: fieldEndHintText, - fieldStartLabelText: fieldStartLabelText, - fieldEndLabelText: fieldEndLabelText, - ); - - if (textDirection != null) { - dialog = Directionality( - textDirection: textDirection, - child: dialog, - ); - } - - if (locale != null) { - dialog = Localizations.override( - context: context, - locale: locale, - child: dialog, - ); - } - - return showDialog( - context: context, - useRootNavigator: useRootNavigator, - routeSettings: routeSettings, - useSafeArea: false, - builder: (BuildContext context) { - return builder == null ? dialog : builder(context, dialog); - }, - anchorPoint: anchorPoint, - ); -} - -/// Returns a locale-appropriate string to describe the start of a date range. -/// -/// If `startDate` is null, then it defaults to 'Start Date', otherwise if it -/// is in the same year as the `endDate` then it will use the short month -/// day format (i.e. 'Jan 21'). Otherwise it will return the short date format -/// (i.e. 'Jan 21, 2020'). -String _formatRangeStartDate(MaterialLocalizations localizations, DateTime? startDate, DateTime? endDate) { - return startDate == null - ? localizations.dateRangeStartLabel - : (endDate == null || startDate.year == endDate.year) - ? localizations.formatShortMonthDay(startDate) - : localizations.formatShortDate(startDate); -} - -/// Returns an locale-appropriate string to describe the end of a date range. -/// -/// If `endDate` is null, then it defaults to 'End Date', otherwise if it -/// is in the same year as the `startDate` and the `currentDate` then it will -/// just use the short month day format (i.e. 'Jan 21'), otherwise it will -/// include the year (i.e. 'Jan 21, 2020'). -String _formatRangeEndDate(MaterialLocalizations localizations, DateTime? startDate, DateTime? endDate, DateTime currentDate) { - return endDate == null - ? localizations.dateRangeEndLabel - : (startDate != null && startDate.year == endDate.year && startDate.year == currentDate.year) - ? localizations.formatShortMonthDay(endDate) - : localizations.formatShortDate(endDate); -} - -/// A Material-style date range picker dialog. -/// -/// It is used internally by [showDateRangePicker] or can be directly pushed -/// onto the [Navigator] stack to enable state restoration. See -/// [showDateRangePicker] for a state restoration app example. -/// -/// See also: -/// -/// * [showDateRangePicker], which is a way to display the date picker. -class DateRangePickerDialog extends StatefulWidget { - /// A Material-style date range picker dialog. - const DateRangePickerDialog({ - super.key, - this.initialDateRange, - required this.firstDate, - required this.lastDate, - this.currentDate, - this.initialEntryMode = DatePickerEntryMode.calendar, - this.helpText, - this.cancelText, - this.confirmText, - this.saveText, - this.errorInvalidRangeText, - this.errorFormatText, - this.errorInvalidText, - this.fieldStartHintText, - this.fieldEndHintText, - this.fieldStartLabelText, - this.fieldEndLabelText, - this.restorationId, - }); - - /// The date range that the date range picker starts with when it opens. - /// - /// If an initial date range is provided, `initialDateRange.start` - /// and `initialDateRange.end` must both fall between or on [firstDate] and - /// [lastDate]. For all of these [DateTime] values, only their dates are - /// considered. Their time fields are ignored. - /// - /// If [initialDateRange] is non-null, then it will be used as the initially - /// selected date range. If it is provided, `initialDateRange.start` must be - /// before or on `initialDateRange.end`. - final DateTimeRange? initialDateRange; - - /// The earliest allowable date on the date range. - final DateTime firstDate; - - /// The latest allowable date on the date range. - final DateTime lastDate; - - /// The [currentDate] represents the current day (i.e. today). - /// - /// This date will be highlighted in the day grid. - /// - /// If `null`, the date of `DateTime.now()` will be used. - final DateTime? currentDate; - - /// The initial date range picker entry mode. - /// - /// The date range has two main modes: [DatePickerEntryMode.calendar] (a - /// scrollable calendar month grid) or [DatePickerEntryMode.input] (two text - /// input fields) mode. - /// - /// It defaults to [DatePickerEntryMode.calendar] and must be non-null. - final DatePickerEntryMode initialEntryMode; - - /// The label on the cancel button for the text input mode. - /// - /// If null, the localized value of - /// [MaterialLocalizations.cancelButtonLabel] is used. - final String? cancelText; - - /// The label on the "OK" button for the text input mode. - /// - /// If null, the localized value of - /// [MaterialLocalizations.okButtonLabel] is used. - final String? confirmText; - - /// The label on the save button for the fullscreen calendar mode. - /// - /// If null, the localized value of - /// [MaterialLocalizations.saveButtonLabel] is used. - final String? saveText; - - /// The label displayed at the top of the dialog. - /// - /// If null, the localized value of - /// [MaterialLocalizations.dateRangePickerHelpText] is used. - final String? helpText; - - /// The message used when the date range is invalid (e.g. start date is after - /// end date). - /// - /// If null, the localized value of - /// [MaterialLocalizations.invalidDateRangeLabel] is used. - final String? errorInvalidRangeText; - - /// The message used when an input text isn't in a proper date format. - /// - /// If null, the localized value of - /// [MaterialLocalizations.invalidDateFormatLabel] is used. - final String? errorFormatText; - - /// The message used when an input text isn't a selectable date. - /// - /// If null, the localized value of - /// [MaterialLocalizations.dateOutOfRangeLabel] is used. - final String? errorInvalidText; - - /// The text used to prompt the user when no text has been entered in the - /// start field. - /// - /// If null, the localized value of - /// [MaterialLocalizations.dateHelpText] is used. - final String? fieldStartHintText; - - /// The text used to prompt the user when no text has been entered in the - /// end field. - /// - /// If null, the localized value of [MaterialLocalizations.dateHelpText] is - /// used. - final String? fieldEndHintText; - - /// The label for the start date text input field. - /// - /// If null, the localized value of [MaterialLocalizations.dateRangeStartLabel] - /// is used. - final String? fieldStartLabelText; - - /// The label for the end date text input field. - /// - /// If null, the localized value of [MaterialLocalizations.dateRangeEndLabel] - /// is used. - final String? fieldEndLabelText; - - /// Restoration ID to save and restore the state of the [DateRangePickerDialog]. - /// - /// If it is non-null, the date range picker will persist and restore the - /// date range selected on the dialog. - /// - /// The state of this widget is persisted in a [RestorationBucket] claimed - /// from the surrounding [RestorationScope] using the provided restoration ID. - /// - /// See also: - /// - /// * [RestorationManager], which explains how state restoration works in - /// Flutter. - final String? restorationId; - - @override - State createState() => _DateRangePickerDialogState(); -} - -class _DateRangePickerDialogState extends State with RestorationMixin { - late final _RestorableDatePickerEntryMode _entryMode = _RestorableDatePickerEntryMode(widget.initialEntryMode); - late final RestorableDateTimeN _selectedStart = RestorableDateTimeN(widget.initialDateRange?.start); - late final RestorableDateTimeN _selectedEnd = RestorableDateTimeN(widget.initialDateRange?.end); - final RestorableBool _autoValidate = RestorableBool(false); - final GlobalKey _calendarPickerKey = GlobalKey(); - final GlobalKey<_InputDateRangePickerState> _inputPickerKey = GlobalKey<_InputDateRangePickerState>(); - - @override - String? get restorationId => widget.restorationId; - - @override - void restoreState(RestorationBucket? oldBucket, bool initialRestore) { - registerForRestoration(_entryMode, 'entry_mode'); - registerForRestoration(_selectedStart, 'selected_start'); - registerForRestoration(_selectedEnd, 'selected_end'); - registerForRestoration(_autoValidate, 'autovalidate'); - } - - void _handleOk() { - if (_entryMode.value == DatePickerEntryMode.input || _entryMode.value == DatePickerEntryMode.inputOnly) { - final _InputDateRangePickerState picker = _inputPickerKey.currentState!; - if (!picker.validate()) { - setState(() { - _autoValidate.value = true; - }); - return; - } - } - final DateTimeRange? selectedRange = _hasSelectedDateRange - ? DateTimeRange(start: _selectedStart.value!, end: _selectedEnd.value!) - : null; - - Navigator.pop(context, selectedRange); - } - - void _handleCancel() { - Navigator.pop(context); - } - - void _handleEntryModeToggle() { - setState(() { - switch (_entryMode.value) { - case DatePickerEntryMode.calendar: - _autoValidate.value = false; - _entryMode.value = DatePickerEntryMode.input; - break; - - case DatePickerEntryMode.input: - // Validate the range dates - if (_selectedStart.value != null && - (_selectedStart.value!.isBefore(widget.firstDate) || _selectedStart.value!.isAfter(widget.lastDate))) { - _selectedStart.value = null; - // With no valid start date, having an end date makes no sense for the UI. - _selectedEnd.value = null; - } - if (_selectedEnd.value != null && - (_selectedEnd.value!.isBefore(widget.firstDate) || _selectedEnd.value!.isAfter(widget.lastDate))) { - _selectedEnd.value = null; - } - // If invalid range (start after end), then just use the start date - if (_selectedStart.value != null && _selectedEnd.value != null && _selectedStart.value!.isAfter(_selectedEnd.value!)) { - _selectedEnd.value = null; - } - _entryMode.value = DatePickerEntryMode.calendar; - break; - - case DatePickerEntryMode.calendarOnly: - case DatePickerEntryMode.inputOnly: - assert(false, 'Can not change entry mode from $_entryMode'); - break; - } - }); - } - - void _handleStartDateChanged(DateTime? date) { - setState(() => _selectedStart.value = date); - } - - void _handleEndDateChanged(DateTime? date) { - setState(() => _selectedEnd.value = date); - } - - bool get _hasSelectedDateRange => _selectedStart.value != null && _selectedEnd.value != null; - - @override - Widget build(BuildContext context) { - final MediaQueryData mediaQuery = MediaQuery.of(context); - final Orientation orientation = mediaQuery.orientation; - final double textScaleFactor = math.min(mediaQuery.textScaleFactor, 1.3); - final MaterialLocalizations localizations = MaterialLocalizations.of(context); - final ColorScheme colors = Theme.of(context).colorScheme; - final Color onPrimarySurface = colors.brightness == Brightness.light - ? colors.onPrimary - : colors.onSurface; - - final Widget contents; - final Size size; - ShapeBorder? shape; - final double elevation; - final EdgeInsets insetPadding; - final bool showEntryModeButton = - _entryMode.value == DatePickerEntryMode.calendar || - _entryMode.value == DatePickerEntryMode.input; - switch (_entryMode.value) { - case DatePickerEntryMode.calendar: - case DatePickerEntryMode.calendarOnly: - contents = _CalendarRangePickerDialog( - key: _calendarPickerKey, - selectedStartDate: _selectedStart.value, - selectedEndDate: _selectedEnd.value, - firstDate: widget.firstDate, - lastDate: widget.lastDate, - currentDate: widget.currentDate, - onStartDateChanged: _handleStartDateChanged, - onEndDateChanged: _handleEndDateChanged, - onConfirm: _hasSelectedDateRange ? _handleOk : null, - onCancel: _handleCancel, - entryModeButton: showEntryModeButton - ? IconButton( - icon: const Icon(Icons.edit), - padding: EdgeInsets.zero, - color: onPrimarySurface, - tooltip: localizations.inputDateModeButtonLabel, - onPressed: _handleEntryModeToggle, - ) - : null, - confirmText: widget.saveText ?? localizations.saveButtonLabel, - helpText: widget.helpText ?? localizations.dateRangePickerHelpText, - ); - size = mediaQuery.size; - insetPadding = EdgeInsets.zero; - shape = const RoundedRectangleBorder(); - elevation = 0; - break; - - case DatePickerEntryMode.input: - case DatePickerEntryMode.inputOnly: - contents = _InputDateRangePickerDialog( - selectedStartDate: _selectedStart.value, - selectedEndDate: _selectedEnd.value, - currentDate: widget.currentDate, - picker: Container( - padding: const EdgeInsets.symmetric(horizontal: 24), - height: orientation == Orientation.portrait - ? _inputFormPortraitHeight - : _inputFormLandscapeHeight, - child: Column( - children: [ - const Spacer(), - _InputDateRangePicker( - key: _inputPickerKey, - initialStartDate: _selectedStart.value, - initialEndDate: _selectedEnd.value, - firstDate: widget.firstDate, - lastDate: widget.lastDate, - onStartDateChanged: _handleStartDateChanged, - onEndDateChanged: _handleEndDateChanged, - autofocus: true, - autovalidate: _autoValidate.value, - helpText: widget.helpText, - errorInvalidRangeText: widget.errorInvalidRangeText, - errorFormatText: widget.errorFormatText, - errorInvalidText: widget.errorInvalidText, - fieldStartHintText: widget.fieldStartHintText, - fieldEndHintText: widget.fieldEndHintText, - fieldStartLabelText: widget.fieldStartLabelText, - fieldEndLabelText: widget.fieldEndLabelText, - ), - const Spacer(), - ], - ), - ), - onConfirm: _handleOk, - onCancel: _handleCancel, - entryModeButton: showEntryModeButton - ? IconButton( - icon: const Icon(Icons.calendar_today), - padding: EdgeInsets.zero, - color: onPrimarySurface, - tooltip: localizations.calendarModeButtonLabel, - onPressed: _handleEntryModeToggle, - ) - : null, - confirmText: widget.confirmText ?? localizations.okButtonLabel, - cancelText: widget.cancelText ?? localizations.cancelButtonLabel, - helpText: widget.helpText ?? localizations.dateRangePickerHelpText, - ); - final DialogTheme dialogTheme = Theme.of(context).dialogTheme; - size = orientation == Orientation.portrait ? _inputPortraitDialogSize : _inputRangeLandscapeDialogSize; - insetPadding = const EdgeInsets.symmetric(horizontal: 16.0, vertical: 24.0); - shape = dialogTheme.shape; - elevation = dialogTheme.elevation ?? 24; - break; - } - - return Dialog( - insetPadding: insetPadding, - shape: shape, - elevation: elevation, - clipBehavior: Clip.antiAlias, - child: AnimatedContainer( - width: size.width, - height: size.height, - duration: _dialogSizeAnimationDuration, - curve: Curves.easeIn, - child: MediaQuery( - data: MediaQuery.of(context).copyWith( - textScaleFactor: textScaleFactor, - ), - child: Builder(builder: (BuildContext context) { - return contents; - }), - ), - ), - ); - } -} - -class _CalendarRangePickerDialog extends StatelessWidget { - const _CalendarRangePickerDialog({ - super.key, - required this.selectedStartDate, - required this.selectedEndDate, - required this.firstDate, - required this.lastDate, - required this.currentDate, - required this.onStartDateChanged, - required this.onEndDateChanged, - required this.onConfirm, - required this.onCancel, - required this.confirmText, - required this.helpText, - this.entryModeButton, - }); - - final DateTime? selectedStartDate; - final DateTime? selectedEndDate; - final DateTime firstDate; - final DateTime lastDate; - final DateTime? currentDate; - final ValueChanged onStartDateChanged; - final ValueChanged onEndDateChanged; - final VoidCallback? onConfirm; - final VoidCallback? onCancel; - final String confirmText; - final String helpText; - final Widget? entryModeButton; - - @override - Widget build(BuildContext context) { - final ThemeData theme = Theme.of(context); - final ColorScheme colorScheme = theme.colorScheme; - final MaterialLocalizations localizations = MaterialLocalizations.of(context); - final Orientation orientation = MediaQuery.of(context).orientation; - final TextTheme textTheme = theme.textTheme; - final Color headerForeground = colorScheme.brightness == Brightness.light - ? colorScheme.onPrimary - : colorScheme.onSurface; - final Color headerDisabledForeground = headerForeground.withOpacity(0.38); - final String startDateText = _formatRangeStartDate(localizations, selectedStartDate, selectedEndDate); - final String endDateText = _formatRangeEndDate(localizations, selectedStartDate, selectedEndDate, DateTime.now()); - final TextStyle? headlineStyle = textTheme.headline5; - final TextStyle? startDateStyle = headlineStyle?.apply( - color: selectedStartDate != null ? headerForeground : headerDisabledForeground, - ); - final TextStyle? endDateStyle = headlineStyle?.apply( - color: selectedEndDate != null ? headerForeground : headerDisabledForeground, - ); - final TextStyle saveButtonStyle = textTheme.button!.apply( - color: onConfirm != null ? headerForeground : headerDisabledForeground, - ); - - return SafeArea( - top: false, - left: false, - right: false, - child: Scaffold( - appBar: AppBar( - leading: CloseButton( - onPressed: onCancel, - ), - actions: [ - if (orientation == Orientation.landscape && entryModeButton != null) - entryModeButton!, - TextButton( - onPressed: onConfirm, - child: Text(confirmText, style: saveButtonStyle), - ), - const SizedBox(width: 8), - ], - bottom: PreferredSize( - preferredSize: const Size(double.infinity, 64), - child: Row(children: [ - SizedBox(width: MediaQuery.of(context).size.width < 360 ? 42 : 72), - Expanded( - child: Semantics( - label: '$helpText $startDateText to $endDateText', - excludeSemantics: true, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - helpText, - style: textTheme.overline!.apply( - color: headerForeground, - ), - ), - const SizedBox(height: 8), - Row( - children: [ - Text( - startDateText, - style: startDateStyle, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - Text(' – ', style: startDateStyle, - ), - Flexible( - child: Text( - endDateText, - style: endDateStyle, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), - ], - ), - const SizedBox(height: 16), - ], - ), - ), - ), - if (orientation == Orientation.portrait && entryModeButton != null) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: entryModeButton, - ), - ]), - ), - ), - body: _CalendarDateRangePicker( - initialStartDate: selectedStartDate, - initialEndDate: selectedEndDate, - firstDate: firstDate, - lastDate: lastDate, - currentDate: currentDate, - onStartDateChanged: onStartDateChanged, - onEndDateChanged: onEndDateChanged, - ), - ), - ); - } -} - -const Duration _monthScrollDuration = Duration(milliseconds: 200); - -const double _monthItemHeaderHeight = 58.0; -const double _monthItemFooterHeight = 12.0; -const double _monthItemRowHeight = 42.0; -const double _monthItemSpaceBetweenRows = 8.0; -const double _horizontalPadding = 8.0; -const double _maxCalendarWidthLandscape = 384.0; -const double _maxCalendarWidthPortrait = 480.0; - -/// Displays a scrollable calendar grid that allows a user to select a range -/// of dates. -class _CalendarDateRangePicker extends StatefulWidget { - /// Creates a scrollable calendar grid for picking date ranges. - _CalendarDateRangePicker({ - DateTime? initialStartDate, - DateTime? initialEndDate, - required DateTime firstDate, - required DateTime lastDate, - DateTime? currentDate, - required this.onStartDateChanged, - required this.onEndDateChanged, - }) : initialStartDate = initialStartDate != null ? DateUtils.dateOnly(initialStartDate) : null, - initialEndDate = initialEndDate != null ? DateUtils.dateOnly(initialEndDate) : null, - assert(firstDate != null), - assert(lastDate != null), - firstDate = DateUtils.dateOnly(firstDate), - lastDate = DateUtils.dateOnly(lastDate), - currentDate = DateUtils.dateOnly(currentDate ?? DateTime.now()) { - assert( - this.initialStartDate == null || this.initialEndDate == null || !this.initialStartDate!.isAfter(initialEndDate!), - 'initialStartDate must be on or before initialEndDate.', - ); - assert( - !this.lastDate.isBefore(this.firstDate), - 'firstDate must be on or before lastDate.', - ); - } - - /// The [DateTime] that represents the start of the initial date range selection. - final DateTime? initialStartDate; - - /// The [DateTime] that represents the end of the initial date range selection. - final DateTime? initialEndDate; - - /// The earliest allowable [DateTime] that the user can select. - final DateTime firstDate; - - /// The latest allowable [DateTime] that the user can select. - final DateTime lastDate; - - /// The [DateTime] representing today. It will be highlighted in the day grid. - final DateTime currentDate; - - /// Called when the user changes the start date of the selected range. - final ValueChanged? onStartDateChanged; - - /// Called when the user changes the end date of the selected range. - final ValueChanged? onEndDateChanged; - - @override - _CalendarDateRangePickerState createState() => _CalendarDateRangePickerState(); -} - -class _CalendarDateRangePickerState extends State<_CalendarDateRangePicker> { - final GlobalKey _scrollViewKey = GlobalKey(); - DateTime? _startDate; - DateTime? _endDate; - int _initialMonthIndex = 0; - late ScrollController _controller; - late bool _showWeekBottomDivider; - - @override - void initState() { - super.initState(); - _controller = ScrollController(); - _controller.addListener(_scrollListener); - - _startDate = widget.initialStartDate; - _endDate = widget.initialEndDate; - - // Calculate the index for the initially displayed month. This is needed to - // divide the list of months into two `SliverList`s. - final DateTime initialDate = widget.initialStartDate ?? widget.currentDate; - if (!initialDate.isBefore(widget.firstDate) && - !initialDate.isAfter(widget.lastDate)) { - _initialMonthIndex = DateUtils.monthDelta(widget.firstDate, initialDate); - } - - _showWeekBottomDivider = _initialMonthIndex != 0; - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - void _scrollListener() { - if (_controller.offset <= _controller.position.minScrollExtent) { - setState(() { - _showWeekBottomDivider = false; - }); - } else if (!_showWeekBottomDivider) { - setState(() { - _showWeekBottomDivider = true; - }); - } - } - - int get _numberOfMonths => DateUtils.monthDelta(widget.firstDate, widget.lastDate) + 1; - - void _vibrate() { - switch (Theme.of(context).platform) { - case TargetPlatform.android: - case TargetPlatform.fuchsia: - HapticFeedback.vibrate(); - break; - case TargetPlatform.iOS: - case TargetPlatform.linux: - case TargetPlatform.macOS: - case TargetPlatform.windows: - break; - } - } - - // This updates the selected date range using this logic: - // - // * From the unselected state, selecting one date creates the start date. - // * If the next selection is before the start date, reset date range and - // set the start date to that selection. - // * If the next selection is on or after the start date, set the end date - // to that selection. - // * After both start and end dates are selected, any subsequent selection - // resets the date range and sets start date to that selection. - void _updateSelection(DateTime date) { - _vibrate(); - setState(() { - if (_startDate != null && _endDate == null && !date.isBefore(_startDate!)) { - _endDate = date; - widget.onEndDateChanged?.call(_endDate); - } else { - _startDate = date; - widget.onStartDateChanged?.call(_startDate!); - if (_endDate != null) { - _endDate = null; - widget.onEndDateChanged?.call(_endDate); - } - } - }); - } - - Widget _buildMonthItem(BuildContext context, int index, bool beforeInitialMonth) { - final int monthIndex = beforeInitialMonth - ? _initialMonthIndex - index - 1 - : _initialMonthIndex + index; - final DateTime month = DateUtils.addMonthsToMonthDate(widget.firstDate, monthIndex); - return Stack( - alignment: Alignment.center, - children: [ - Text("${month.month}",style: TextStyle(fontSize: 200,color: Colors.grey.withOpacity(0.1)),), - _MonthItem( - selectedDateStart: _startDate, - selectedDateEnd: _endDate, - currentDate: widget.currentDate, - firstDate: widget.firstDate, - lastDate: widget.lastDate, - displayedMonth: month, - onChanged: _updateSelection, - ), - ], - ); - } - - @override - Widget build(BuildContext context) { - const Key sliverAfterKey = Key('sliverAfterKey'); - - return Column( - children: [ - const _DayHeaders(), - if (_showWeekBottomDivider) const Divider(height: 0), - Expanded( - child: _CalendarKeyboardNavigator( - firstDate: widget.firstDate, - lastDate: widget.lastDate, - initialFocusedDay: _startDate ?? widget.initialStartDate ?? widget.currentDate, - // In order to prevent performance issues when displaying the - // correct initial month, 2 `SliverList`s are used to split the - // months. The first item in the second SliverList is the initial - // month to be displayed. - child: CustomScrollView( - key: _scrollViewKey, - controller: _controller, - center: sliverAfterKey, - slivers: [ - SliverList( - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) => _buildMonthItem(context, index, true), - childCount: _initialMonthIndex, - ), - ), - SliverList( - key: sliverAfterKey, - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) => _buildMonthItem(context, index, false), - childCount: _numberOfMonths - _initialMonthIndex, - ), - ), - ], - ), - ), - ), - ], - ); - } -} - -class _CalendarKeyboardNavigator extends StatefulWidget { - const _CalendarKeyboardNavigator({ - required this.child, - required this.firstDate, - required this.lastDate, - required this.initialFocusedDay, - }); - - final Widget child; - final DateTime firstDate; - final DateTime lastDate; - final DateTime initialFocusedDay; - - @override - _CalendarKeyboardNavigatorState createState() => _CalendarKeyboardNavigatorState(); -} - -class _CalendarKeyboardNavigatorState extends State<_CalendarKeyboardNavigator> { - - final Map _shortcutMap = const { - SingleActivator(LogicalKeyboardKey.arrowLeft): DirectionalFocusIntent(TraversalDirection.left), - SingleActivator(LogicalKeyboardKey.arrowRight): DirectionalFocusIntent(TraversalDirection.right), - SingleActivator(LogicalKeyboardKey.arrowDown): DirectionalFocusIntent(TraversalDirection.down), - SingleActivator(LogicalKeyboardKey.arrowUp): DirectionalFocusIntent(TraversalDirection.up), - }; - late Map> _actionMap; - late FocusNode _dayGridFocus; - TraversalDirection? _dayTraversalDirection; - DateTime? _focusedDay; - - @override - void initState() { - super.initState(); - - _actionMap = >{ - NextFocusIntent: CallbackAction(onInvoke: _handleGridNextFocus), - PreviousFocusIntent: CallbackAction(onInvoke: _handleGridPreviousFocus), - DirectionalFocusIntent: CallbackAction(onInvoke: _handleDirectionFocus), - }; - _dayGridFocus = FocusNode(debugLabel: 'Day Grid'); - } - - @override - void dispose() { - _dayGridFocus.dispose(); - super.dispose(); - } - - void _handleGridFocusChange(bool focused) { - setState(() { - if (focused) { - _focusedDay ??= widget.initialFocusedDay; - } - }); - } - - /// Move focus to the next element after the day grid. - void _handleGridNextFocus(NextFocusIntent intent) { - _dayGridFocus.requestFocus(); - _dayGridFocus.nextFocus(); - } - - /// Move focus to the previous element before the day grid. - void _handleGridPreviousFocus(PreviousFocusIntent intent) { - _dayGridFocus.requestFocus(); - _dayGridFocus.previousFocus(); - } - - /// Move the internal focus date in the direction of the given intent. - /// - /// This will attempt to move the focused day to the next selectable day in - /// the given direction. If the new date is not in the current month, then - /// the page view will be scrolled to show the new date's month. - /// - /// For horizontal directions, it will move forward or backward a day (depending - /// on the current [TextDirection]). For vertical directions it will move up and - /// down a week at a time. - void _handleDirectionFocus(DirectionalFocusIntent intent) { - assert(_focusedDay != null); - setState(() { - final DateTime? nextDate = _nextDateInDirection(_focusedDay!, intent.direction); - if (nextDate != null) { - _focusedDay = nextDate; - _dayTraversalDirection = intent.direction; - } - }); - } - - static const Map _directionOffset = { - TraversalDirection.up: -DateTime.daysPerWeek, - TraversalDirection.right: 1, - TraversalDirection.down: DateTime.daysPerWeek, - TraversalDirection.left: -1, - }; - - int _dayDirectionOffset(TraversalDirection traversalDirection, TextDirection textDirection) { - // Swap left and right if the text direction if RTL - if (textDirection == TextDirection.rtl) { - if (traversalDirection == TraversalDirection.left) { - traversalDirection = TraversalDirection.right; - } else if (traversalDirection == TraversalDirection.right) { - traversalDirection = TraversalDirection.left; - } - } - return _directionOffset[traversalDirection]!; - } - - DateTime? _nextDateInDirection(DateTime date, TraversalDirection direction) { - final TextDirection textDirection = Directionality.of(context); - final DateTime nextDate = DateUtils.addDaysToDate(date, _dayDirectionOffset(direction, textDirection)); - if (!nextDate.isBefore(widget.firstDate) && !nextDate.isAfter(widget.lastDate)) { - return nextDate; - } - return null; - } - - @override - Widget build(BuildContext context) { - return FocusableActionDetector( - shortcuts: _shortcutMap, - actions: _actionMap, - focusNode: _dayGridFocus, - onFocusChange: _handleGridFocusChange, - child: _FocusedDate( - date: _dayGridFocus.hasFocus ? _focusedDay : null, - scrollDirection: _dayGridFocus.hasFocus ? _dayTraversalDirection : null, - child: widget.child, - ), - ); - } -} - -/// InheritedWidget indicating what the current focused date is for its children. -/// -/// This is used by the [_MonthPicker] to let its children [_DayPicker]s know -/// what the currently focused date (if any) should be. -class _FocusedDate extends InheritedWidget { - const _FocusedDate({ - required super.child, - this.date, - this.scrollDirection, - }); - - final DateTime? date; - final TraversalDirection? scrollDirection; - - @override - bool updateShouldNotify(_FocusedDate oldWidget) { - return !DateUtils.isSameDay(date, oldWidget.date) || scrollDirection != oldWidget.scrollDirection; - } - - static _FocusedDate? of(BuildContext context) { - return context.dependOnInheritedWidgetOfExactType<_FocusedDate>(); - } -} - - -class _DayHeaders extends StatelessWidget { - const _DayHeaders(); - - /// Builds widgets showing abbreviated days of week. The first widget in the - /// returned list corresponds to the first day of week for the current locale. - /// - /// Examples: - /// - /// ``` - /// ┌ Sunday is the first day of week in the US (en_US) - /// | - /// S M T W T F S <-- the returned list contains these widgets - /// _ _ _ _ _ 1 2 - /// 3 4 5 6 7 8 9 - /// - /// ┌ But it's Monday in the UK (en_GB) - /// | - /// M T W T F S S <-- the returned list contains these widgets - /// _ _ _ _ 1 2 3 - /// 4 5 6 7 8 9 10 - /// ``` - List _getDayHeaders(TextStyle headerStyle, MaterialLocalizations localizations) { - final List result = []; - for (int i = localizations.firstDayOfWeekIndex; true; i = (i + 1) % 7) { - final String weekday = localizations.narrowWeekdays[i]; - result.add(ExcludeSemantics( - child: Center(child: Text(weekday, style: headerStyle)), - )); - if (i == (localizations.firstDayOfWeekIndex - 1) % 7) { - break; - } - } - return result; - } - - @override - Widget build(BuildContext context) { - final ThemeData themeData = Theme.of(context); - final ColorScheme colorScheme = themeData.colorScheme; - final TextStyle textStyle = themeData.textTheme.subtitle2!.apply(color: colorScheme.onSurface); - final MaterialLocalizations localizations = MaterialLocalizations.of(context); - final List labels = _getDayHeaders(textStyle, localizations); - - // Add leading and trailing containers for edges of the custom grid layout. - labels.insert(0, Container()); - labels.add(Container()); - - return Container( - constraints: BoxConstraints( - maxWidth: MediaQuery.of(context).orientation == Orientation.landscape - ? _maxCalendarWidthLandscape - : _maxCalendarWidthPortrait, - maxHeight: _monthItemRowHeight, - ), - child: GridView.custom( - shrinkWrap: true, - gridDelegate: _monthItemGridDelegate, - childrenDelegate: SliverChildListDelegate( - labels, - addRepaintBoundaries: false, - ), - ), - ); - } -} - -class _MonthItemGridDelegate extends SliverGridDelegate { - const _MonthItemGridDelegate(); - - @override - SliverGridLayout getLayout(SliverConstraints constraints) { - final double tileWidth = (constraints.crossAxisExtent - 2 * _horizontalPadding) / DateTime.daysPerWeek; - return _MonthSliverGridLayout( - crossAxisCount: DateTime.daysPerWeek + 2, - dayChildWidth: tileWidth, - edgeChildWidth: _horizontalPadding, - reverseCrossAxis: axisDirectionIsReversed(constraints.crossAxisDirection), - ); - } - - @override - bool shouldRelayout(_MonthItemGridDelegate oldDelegate) => false; -} - -const _MonthItemGridDelegate _monthItemGridDelegate = _MonthItemGridDelegate(); - -class _MonthSliverGridLayout extends SliverGridLayout { - /// Creates a layout that uses equally sized and spaced tiles for each day of - /// the week and an additional edge tile for padding at the start and end of - /// each row. - /// - /// This is necessary to facilitate the painting of the range highlight - /// correctly. - const _MonthSliverGridLayout({ - required this.crossAxisCount, - required this.dayChildWidth, - required this.edgeChildWidth, - required this.reverseCrossAxis, - }) : assert(crossAxisCount != null && crossAxisCount > 0), - assert(dayChildWidth != null && dayChildWidth >= 0), - assert(edgeChildWidth != null && edgeChildWidth >= 0), - assert(reverseCrossAxis != null); - - /// The number of children in the cross axis. - final int crossAxisCount; - - /// The width in logical pixels of the day child widgets. - final double dayChildWidth; - - /// The width in logical pixels of the edge child widgets. - final double edgeChildWidth; - - /// Whether the children should be placed in the opposite order of increasing - /// coordinates in the cross axis. - /// - /// For example, if the cross axis is horizontal, the children are placed from - /// left to right when [reverseCrossAxis] is false and from right to left when - /// [reverseCrossAxis] is true. - /// - /// Typically set to the return value of [axisDirectionIsReversed] applied to - /// the [SliverConstraints.crossAxisDirection]. - final bool reverseCrossAxis; - - /// The number of logical pixels from the leading edge of one row to the - /// leading edge of the next row. - double get _rowHeight { - return _monthItemRowHeight + _monthItemSpaceBetweenRows; - } - - /// The height in logical pixels of the children widgets. - double get _childHeight { - return _monthItemRowHeight; - } - - @override - int getMinChildIndexForScrollOffset(double scrollOffset) { - return crossAxisCount * (scrollOffset ~/ _rowHeight); - } - - @override - int getMaxChildIndexForScrollOffset(double scrollOffset) { - final int mainAxisCount = (scrollOffset / _rowHeight).ceil(); - return math.max(0, crossAxisCount * mainAxisCount - 1); - } - - double _getCrossAxisOffset(double crossAxisStart, bool isPadding) { - if (reverseCrossAxis) { - return - ((crossAxisCount - 2) * dayChildWidth + 2 * edgeChildWidth) - - crossAxisStart - - (isPadding ? edgeChildWidth : dayChildWidth); - } - return crossAxisStart; - } - - @override - SliverGridGeometry getGeometryForChildIndex(int index) { - final int adjustedIndex = index % crossAxisCount; - final bool isEdge = adjustedIndex == 0 || adjustedIndex == crossAxisCount - 1; - final double crossAxisStart = math.max(0, (adjustedIndex - 1) * dayChildWidth + edgeChildWidth); - - return SliverGridGeometry( - scrollOffset: (index ~/ crossAxisCount) * _rowHeight, - crossAxisOffset: _getCrossAxisOffset(crossAxisStart, isEdge), - mainAxisExtent: _childHeight, - crossAxisExtent: isEdge ? edgeChildWidth : dayChildWidth, - ); - } - - @override - double computeMaxScrollOffset(int childCount) { - assert(childCount >= 0); - final int mainAxisCount = ((childCount - 1) ~/ crossAxisCount) + 1; - final double mainAxisSpacing = _rowHeight - _childHeight; - return _rowHeight * mainAxisCount - mainAxisSpacing; - } -} - -/// Displays the days of a given month and allows choosing a date range. -/// -/// The days are arranged in a rectangular grid with one column for each day of -/// the week. -class _MonthItem extends StatefulWidget { - /// Creates a month item. - _MonthItem({ - required this.selectedDateStart, - required this.selectedDateEnd, - required this.currentDate, - required this.onChanged, - required this.firstDate, - required this.lastDate, - required this.displayedMonth, - this.dragStartBehavior = DragStartBehavior.start, - }) : assert(firstDate != null), - assert(lastDate != null), - assert(!firstDate.isAfter(lastDate)), - assert(selectedDateStart == null || !selectedDateStart.isBefore(firstDate)), - assert(selectedDateEnd == null || !selectedDateEnd.isBefore(firstDate)), - assert(selectedDateStart == null || !selectedDateStart.isAfter(lastDate)), - assert(selectedDateEnd == null || !selectedDateEnd.isAfter(lastDate)), - assert(selectedDateStart == null || selectedDateEnd == null || !selectedDateStart.isAfter(selectedDateEnd)), - assert(currentDate != null), - assert(onChanged != null), - assert(displayedMonth != null), - assert(dragStartBehavior != null); - - /// The currently selected start date. - /// - /// This date is highlighted in the picker. - final DateTime? selectedDateStart; - - /// The currently selected end date. - /// - /// This date is highlighted in the picker. - final DateTime? selectedDateEnd; - - /// The current date at the time the picker is displayed. - final DateTime currentDate; - - /// Called when the user picks a day. - final ValueChanged onChanged; - - /// The earliest date the user is permitted to pick. - final DateTime firstDate; - - /// The latest date the user is permitted to pick. - final DateTime lastDate; - - /// The month whose days are displayed by this picker. - final DateTime displayedMonth; - - /// Determines the way that drag start behavior is handled. - /// - /// If set to [DragStartBehavior.start], the drag gesture used to scroll a - /// date picker wheel will begin at the position where the drag gesture won - /// the arena. If set to [DragStartBehavior.down] it will begin at the position - /// where a down event is first detected. - /// - /// In general, setting this to [DragStartBehavior.start] will make drag - /// animation smoother and setting it to [DragStartBehavior.down] will make - /// drag behavior feel slightly more reactive. - /// - /// By default, the drag start behavior is [DragStartBehavior.start]. - /// - /// See also: - /// - /// * [DragGestureRecognizer.dragStartBehavior], which gives an example for - /// the different behaviors. - final DragStartBehavior dragStartBehavior; - - @override - _MonthItemState createState() => _MonthItemState(); -} - -class _MonthItemState extends State<_MonthItem> { - /// List of [FocusNode]s, one for each day of the month. - late List _dayFocusNodes; - - @override - void initState() { - super.initState(); - final int daysInMonth = DateUtils.getDaysInMonth(widget.displayedMonth.year, widget.displayedMonth.month); - _dayFocusNodes = List.generate( - daysInMonth, - (int index) => FocusNode(skipTraversal: true, debugLabel: 'Day ${index + 1}'), - ); - } - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - // Check to see if the focused date is in this month, if so focus it. - final DateTime? focusedDate = _FocusedDate.of(context)?.date; - if (focusedDate != null && DateUtils.isSameMonth(widget.displayedMonth, focusedDate)) { - _dayFocusNodes[focusedDate.day - 1].requestFocus(); - } - } - - @override - void dispose() { - for (final FocusNode node in _dayFocusNodes) { - node.dispose(); - } - super.dispose(); - } - - Color _highlightColor(BuildContext context) { - return Theme.of(context).colorScheme.primary.withOpacity(0.12); - } - - void _dayFocusChanged(bool focused) { - if (focused) { - final TraversalDirection? focusDirection = _FocusedDate.of(context)?.scrollDirection; - if (focusDirection != null) { - ScrollPositionAlignmentPolicy policy = ScrollPositionAlignmentPolicy.explicit; - switch (focusDirection) { - case TraversalDirection.up: - case TraversalDirection.left: - policy = ScrollPositionAlignmentPolicy.keepVisibleAtStart; - break; - case TraversalDirection.right: - case TraversalDirection.down: - policy = ScrollPositionAlignmentPolicy.keepVisibleAtEnd; - break; - } - Scrollable.ensureVisible(primaryFocus!.context!, - duration: _monthScrollDuration, - alignmentPolicy: policy, - ); - } - } - } - - Widget _buildDayItem(BuildContext context, DateTime dayToBuild, int firstDayOffset, int daysInMonth) { - final ThemeData theme = Theme.of(context); - final ColorScheme colorScheme = theme.colorScheme; - final TextTheme textTheme = theme.textTheme; - final MaterialLocalizations localizations = MaterialLocalizations.of(context); - final TextDirection textDirection = Directionality.of(context); - final Color highlightColor = _highlightColor(context); - final int day = dayToBuild.day; - - final bool isDisabled = dayToBuild.isAfter(widget.lastDate) || dayToBuild.isBefore(widget.firstDate); - - BoxDecoration? decoration; - TextStyle? itemStyle = textTheme.bodyText2; - - final bool isRangeSelected = widget.selectedDateStart != null && widget.selectedDateEnd != null; - final bool isSelectedDayStart = widget.selectedDateStart != null && dayToBuild.isAtSameMomentAs(widget.selectedDateStart!); - final bool isSelectedDayEnd = widget.selectedDateEnd != null && dayToBuild.isAtSameMomentAs(widget.selectedDateEnd!); - final bool isInRange = isRangeSelected && - dayToBuild.isAfter(widget.selectedDateStart!) && - dayToBuild.isBefore(widget.selectedDateEnd!); - - _HighlightPainter? highlightPainter; - - if (isSelectedDayStart || isSelectedDayEnd) { - // The selected start and end dates gets a circle background - // highlight, and a contrasting text color. - itemStyle = textTheme.bodyText2?.apply(color: colorScheme.onPrimary); - decoration = BoxDecoration( - color: colorScheme.primary, - shape: BoxShape.circle, - ); - - if (isRangeSelected && widget.selectedDateStart != widget.selectedDateEnd) { - final _HighlightPainterStyle style = isSelectedDayStart - ? _HighlightPainterStyle.highlightTrailing - : _HighlightPainterStyle.highlightLeading; - highlightPainter = _HighlightPainter( - color: highlightColor, - style: style, - textDirection: textDirection, - ); - } - } else if (isInRange) { - // The days within the range get a light background highlight. - highlightPainter = _HighlightPainter( - color: highlightColor, - style: _HighlightPainterStyle.highlightAll, - textDirection: textDirection, - ); - } else if (isDisabled) { - itemStyle = textTheme.bodyText2?.apply(color: colorScheme.onSurface.withOpacity(0.38)); - } else if (DateUtils.isSameDay(widget.currentDate, dayToBuild)) { - // The current day gets a different text color and a circle stroke - // border. - itemStyle = textTheme.bodyText2?.apply(color: colorScheme.primary); - decoration = BoxDecoration( - border: Border.all(color: colorScheme.primary), - shape: BoxShape.circle, - ); - } - - // We want the day of month to be spoken first irrespective of the - // locale-specific preferences or TextDirection. This is because - // an accessibility user is more likely to be interested in the - // day of month before the rest of the date, as they are looking - // for the day of month. To do that we prepend day of month to the - // formatted full date. - String semanticLabel = '${localizations.formatDecimal(day)}, ${localizations.formatFullDate(dayToBuild)}'; - if (isSelectedDayStart) { - semanticLabel = localizations.dateRangeStartDateSemanticLabel(semanticLabel); - } else if (isSelectedDayEnd) { - semanticLabel = localizations.dateRangeEndDateSemanticLabel(semanticLabel); - } - - Widget dayWidget = Container( - decoration: decoration, - child: Center( - child: Semantics( - label: semanticLabel, - selected: isSelectedDayStart || isSelectedDayEnd, - child: ExcludeSemantics( - child: Text(localizations.formatDecimal(day), style: itemStyle), - ), - ), - ), - ); - - if (highlightPainter != null) { - dayWidget = CustomPaint( - painter: highlightPainter, - child: dayWidget, - ); - } - - if (!isDisabled) { - dayWidget = InkResponse( - focusNode: _dayFocusNodes[day - 1], - onTap: () => widget.onChanged(dayToBuild), - radius: _monthItemRowHeight / 2 + 4, - splashColor: colorScheme.primary.withOpacity(0.38), - onFocusChange: _dayFocusChanged, - child: dayWidget, - ); - } - - return dayWidget; - } - - Widget _buildEdgeContainer(BuildContext context, bool isHighlighted) { - return Container(color: isHighlighted ? _highlightColor(context) : null); - } - - @override - Widget build(BuildContext context) { - final ThemeData themeData = Theme.of(context); - final TextTheme textTheme = themeData.textTheme; - final MaterialLocalizations localizations = MaterialLocalizations.of(context); - final int year = widget.displayedMonth.year; - final int month = widget.displayedMonth.month; - final int daysInMonth = DateUtils.getDaysInMonth(year, month); - final int dayOffset = DateUtils.firstDayOffset(year, month, localizations); - final int weeks = ((daysInMonth + dayOffset) / DateTime.daysPerWeek).ceil(); - final double gridHeight = - weeks * _monthItemRowHeight + (weeks - 1) * _monthItemSpaceBetweenRows; - final List dayItems = []; - - for (int i = 0; true; i += 1) { - // 1-based day of month, e.g. 1-31 for January, and 1-29 for February on - // a leap year. - final int day = i - dayOffset + 1; - if (day > daysInMonth) { - break; - } - if (day < 1) { - dayItems.add(Container()); - } else { - final DateTime dayToBuild = DateTime(year, month, day); - final Widget dayItem = _buildDayItem( - context, - dayToBuild, - dayOffset, - daysInMonth, - ); - dayItems.add(dayItem); - } - } - - // Add the leading/trailing edge containers to each week in order to - // correctly extend the range highlight. - final List paddedDayItems = []; - for (int i = 0; i < weeks; i++) { - final int start = i * DateTime.daysPerWeek; - final int end = math.min( - start + DateTime.daysPerWeek, - dayItems.length, - ); - final List weekList = dayItems.sublist(start, end); - - final DateTime dateAfterLeadingPadding = DateTime(year, month, start - dayOffset + 1); - // Only color the edge container if it is after the start date and - // on/before the end date. - final bool isLeadingInRange = - !(dayOffset > 0 && i == 0) && - widget.selectedDateStart != null && - widget.selectedDateEnd != null && - dateAfterLeadingPadding.isAfter(widget.selectedDateStart!) && - !dateAfterLeadingPadding.isAfter(widget.selectedDateEnd!); - weekList.insert(0, _buildEdgeContainer(context, isLeadingInRange)); - - // Only add a trailing edge container if it is for a full week and not a - // partial week. - if (end < dayItems.length || (end == dayItems.length && dayItems.length % DateTime.daysPerWeek == 0)) { - final DateTime dateBeforeTrailingPadding = - DateTime(year, month, end - dayOffset); - // Only color the edge container if it is on/after the start date and - // before the end date. - final bool isTrailingInRange = - widget.selectedDateStart != null && - widget.selectedDateEnd != null && - !dateBeforeTrailingPadding.isBefore(widget.selectedDateStart!) && - dateBeforeTrailingPadding.isBefore(widget.selectedDateEnd!); - weekList.add(_buildEdgeContainer(context, isTrailingInRange)); - } - - paddedDayItems.addAll(weekList); - } - - final double maxWidth = MediaQuery.of(context).orientation == Orientation.landscape - ? _maxCalendarWidthLandscape - : _maxCalendarWidthPortrait; - return Column( - children: [ - Container( - constraints: BoxConstraints(maxWidth: maxWidth), - height: _monthItemHeaderHeight, - padding: const EdgeInsets.symmetric(horizontal: 16), - alignment: AlignmentDirectional.centerStart, - child: ExcludeSemantics( - child: Text( - localizations.formatMonthYear(widget.displayedMonth), - style: textTheme.bodyText2!.apply(color: themeData.colorScheme.onSurface), - ), - ), - ), - Container( - constraints: BoxConstraints( - maxWidth: maxWidth, - maxHeight: gridHeight, - ), - child: GridView.custom( - physics: const NeverScrollableScrollPhysics(), - gridDelegate: _monthItemGridDelegate, - childrenDelegate: SliverChildListDelegate( - paddedDayItems, - addRepaintBoundaries: false, - ), - ), - ), - const SizedBox(height: _monthItemFooterHeight), - ], - ); - } -} - -/// Determines which style to use to paint the highlight. -enum _HighlightPainterStyle { - /// Paints nothing. - none, - - /// Paints a rectangle that occupies the leading half of the space. - highlightLeading, - - /// Paints a rectangle that occupies the trailing half of the space. - highlightTrailing, - - /// Paints a rectangle that occupies all available space. - highlightAll, -} - -/// This custom painter will add a background highlight to its child. -/// -/// This highlight will be drawn depending on the [style], [color], and -/// [textDirection] supplied. It will either paint a rectangle on the -/// left/right, a full rectangle, or nothing at all. This logic is determined by -/// a combination of the [style] and [textDirection]. -class _HighlightPainter extends CustomPainter { - _HighlightPainter({ - required this.color, - this.style = _HighlightPainterStyle.none, - this.textDirection, - }); - - final Color color; - final _HighlightPainterStyle style; - final TextDirection? textDirection; - - @override - void paint(Canvas canvas, Size size) { - if (style == _HighlightPainterStyle.none) { - return; - } - - final Paint paint = Paint() - ..color = color - ..style = PaintingStyle.fill; - - final Rect rectLeft = Rect.fromLTWH(0, 0, size.width / 2, size.height); - final Rect rectRight = Rect.fromLTWH(size.width / 2, 0, size.width / 2, size.height); - - switch (style) { - case _HighlightPainterStyle.highlightTrailing: - canvas.drawRect( - textDirection == TextDirection.ltr ? rectRight : rectLeft, - paint, - ); - break; - case _HighlightPainterStyle.highlightLeading: - canvas.drawRect( - textDirection == TextDirection.ltr ? rectLeft : rectRight, - paint, - ); - break; - case _HighlightPainterStyle.highlightAll: - canvas.drawRect( - Rect.fromLTWH(0, 0, size.width, size.height), - paint, - ); - break; - case _HighlightPainterStyle.none: - break; - } - } - - @override - bool shouldRepaint(CustomPainter oldDelegate) => false; -} - -class _InputDateRangePickerDialog extends StatelessWidget { - const _InputDateRangePickerDialog({ - required this.selectedStartDate, - required this.selectedEndDate, - required this.currentDate, - required this.picker, - required this.onConfirm, - required this.onCancel, - required this.confirmText, - required this.cancelText, - required this.helpText, - required this.entryModeButton, - }); - - final DateTime? selectedStartDate; - final DateTime? selectedEndDate; - final DateTime? currentDate; - final Widget picker; - final VoidCallback onConfirm; - final VoidCallback onCancel; - final String? confirmText; - final String? cancelText; - final String? helpText; - final Widget? entryModeButton; - - String _formatDateRange(BuildContext context, DateTime? start, DateTime? end, DateTime now) { - final MaterialLocalizations localizations = MaterialLocalizations.of(context); - final String startText = _formatRangeStartDate(localizations, start, end); - final String endText = _formatRangeEndDate(localizations, start, end, now); - if (start == null || end == null) { - return localizations.unspecifiedDateRange; - } - if (Directionality.of(context) == TextDirection.ltr) { - return '$startText – $endText'; - } else { - return '$endText – $startText'; - } - } - - @override - Widget build(BuildContext context) { - final ThemeData theme = Theme.of(context); - final ColorScheme colorScheme = theme.colorScheme; - final MaterialLocalizations localizations = MaterialLocalizations.of(context); - final Orientation orientation = MediaQuery.of(context).orientation; - final TextTheme textTheme = theme.textTheme; - - final Color onPrimarySurfaceColor = colorScheme.brightness == Brightness.light - ? colorScheme.onPrimary - : colorScheme.onSurface; - final TextStyle? dateStyle = orientation == Orientation.landscape - ? textTheme.headline5?.apply(color: onPrimarySurfaceColor) - : textTheme.headline4?.apply(color: onPrimarySurfaceColor); - final String dateText = _formatDateRange(context, selectedStartDate, selectedEndDate, currentDate!); - final String semanticDateText = selectedStartDate != null && selectedEndDate != null - ? '${localizations.formatMediumDate(selectedStartDate!)} – ${localizations.formatMediumDate(selectedEndDate!)}' - : ''; - - final Widget header = _DatePickerHeader( - helpText: helpText ?? localizations.dateRangePickerHelpText, - titleText: dateText, - titleSemanticsLabel: semanticDateText, - titleStyle: dateStyle, - orientation: orientation, - isShort: orientation == Orientation.landscape, - entryModeButton: entryModeButton, - ); - - final Widget actions = Container( - alignment: AlignmentDirectional.centerEnd, - constraints: const BoxConstraints(minHeight: 52.0), - padding: const EdgeInsets.symmetric(horizontal: 8), - child: OverflowBar( - spacing: 8, - children: [ - TextButton( - onPressed: onCancel, - child: Text(cancelText ?? localizations.cancelButtonLabel), - ), - TextButton( - onPressed: onConfirm, - child: Text(confirmText ?? localizations.okButtonLabel), - ), - ], - ), - ); - - switch (orientation) { - case Orientation.portrait: - return Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - header, - Expanded(child: picker), - actions, - ], - ); - - case Orientation.landscape: - return Row( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - header, - Flexible( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Expanded(child: picker), - actions, - ], - ), - ), - ], - ); - } - } -} - -/// Provides a pair of text fields that allow the user to enter the start and -/// end dates that represent a range of dates. -class _InputDateRangePicker extends StatefulWidget { - /// Creates a row with two text fields configured to accept the start and end dates - /// of a date range. - _InputDateRangePicker({ - super.key, - DateTime? initialStartDate, - DateTime? initialEndDate, - required DateTime firstDate, - required DateTime lastDate, - required this.onStartDateChanged, - required this.onEndDateChanged, - this.helpText, - this.errorFormatText, - this.errorInvalidText, - this.errorInvalidRangeText, - this.fieldStartHintText, - this.fieldEndHintText, - this.fieldStartLabelText, - this.fieldEndLabelText, - this.autofocus = false, - this.autovalidate = false, - }) : initialStartDate = initialStartDate == null ? null : DateUtils.dateOnly(initialStartDate), - initialEndDate = initialEndDate == null ? null : DateUtils.dateOnly(initialEndDate), - assert(firstDate != null), - firstDate = DateUtils.dateOnly(firstDate), - assert(lastDate != null), - lastDate = DateUtils.dateOnly(lastDate), - assert(firstDate != null), - assert(lastDate != null), - assert(autofocus != null), - assert(autovalidate != null); - - /// The [DateTime] that represents the start of the initial date range selection. - final DateTime? initialStartDate; - - /// The [DateTime] that represents the end of the initial date range selection. - final DateTime? initialEndDate; - - /// The earliest allowable [DateTime] that the user can select. - final DateTime firstDate; - - /// The latest allowable [DateTime] that the user can select. - final DateTime lastDate; - - /// Called when the user changes the start date of the selected range. - final ValueChanged? onStartDateChanged; - - /// Called when the user changes the end date of the selected range. - final ValueChanged? onEndDateChanged; - - /// The text that is displayed at the top of the header. - /// - /// This is used to indicate to the user what they are selecting a date for. - final String? helpText; - - /// Error text used to indicate the text in a field is not a valid date. - final String? errorFormatText; - - /// Error text used to indicate the date in a field is not in the valid range - /// of [firstDate] - [lastDate]. - final String? errorInvalidText; - - /// Error text used to indicate the dates given don't form a valid date - /// range (i.e. the start date is after the end date). - final String? errorInvalidRangeText; - - /// Hint text shown when the start date field is empty. - final String? fieldStartHintText; - - /// Hint text shown when the end date field is empty. - final String? fieldEndHintText; - - /// Label used for the start date field. - final String? fieldStartLabelText; - - /// Label used for the end date field. - final String? fieldEndLabelText; - - /// {@macro flutter.widgets.editableText.autofocus} - final bool autofocus; - - /// If true, this the date fields will validate and update their error text - /// immediately after every change. Otherwise, you must call - /// [_InputDateRangePickerState.validate] to validate. - final bool autovalidate; - - @override - _InputDateRangePickerState createState() => _InputDateRangePickerState(); -} - -/// The current state of an [_InputDateRangePicker]. Can be used to -/// [validate] the date field entries. -class _InputDateRangePickerState extends State<_InputDateRangePicker> { - late String _startInputText; - late String _endInputText; - DateTime? _startDate; - DateTime? _endDate; - late TextEditingController _startController; - late TextEditingController _endController; - String? _startErrorText; - String? _endErrorText; - bool _autoSelected = false; - - @override - void initState() { - super.initState(); - _startDate = widget.initialStartDate; - _startController = TextEditingController(); - _endDate = widget.initialEndDate; - _endController = TextEditingController(); - } - - @override - void dispose() { - _startController.dispose(); - _endController.dispose(); - super.dispose(); - } - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - final MaterialLocalizations localizations = MaterialLocalizations.of(context); - if (_startDate != null) { - _startInputText = localizations.formatCompactDate(_startDate!); - final bool selectText = widget.autofocus && !_autoSelected; - _updateController(_startController, _startInputText, selectText); - _autoSelected = selectText; - } - - if (_endDate != null) { - _endInputText = localizations.formatCompactDate(_endDate!); - _updateController(_endController, _endInputText, false); - } - } - - /// Validates that the text in the start and end fields represent a valid - /// date range. - /// - /// Will return true if the range is valid. If not, it will - /// return false and display an appropriate error message under one of the - /// text fields. - bool validate() { - String? startError = _validateDate(_startDate); - final String? endError = _validateDate(_endDate); - if (startError == null && endError == null) { - if (_startDate!.isAfter(_endDate!)) { - startError = widget.errorInvalidRangeText ?? MaterialLocalizations.of(context).invalidDateRangeLabel; - } - } - setState(() { - _startErrorText = startError; - _endErrorText = endError; - }); - return startError == null && endError == null; - } - - DateTime? _parseDate(String? text) { - final MaterialLocalizations localizations = MaterialLocalizations.of(context); - return localizations.parseCompactDate(text); - } - - String? _validateDate(DateTime? date) { - if (date == null) { - return widget.errorFormatText ?? MaterialLocalizations.of(context).invalidDateFormatLabel; - } else if (date.isBefore(widget.firstDate) || date.isAfter(widget.lastDate)) { - return widget.errorInvalidText ?? MaterialLocalizations.of(context).dateOutOfRangeLabel; - } - return null; - } - - void _updateController(TextEditingController controller, String text, bool selectText) { - TextEditingValue textEditingValue = controller.value.copyWith(text: text); - if (selectText) { - textEditingValue = textEditingValue.copyWith(selection: TextSelection( - baseOffset: 0, - extentOffset: text.length, - )); - } - controller.value = textEditingValue; - } - - void _handleStartChanged(String text) { - setState(() { - _startInputText = text; - _startDate = _parseDate(text); - widget.onStartDateChanged?.call(_startDate); - }); - if (widget.autovalidate) { - validate(); - } - } - - void _handleEndChanged(String text) { - setState(() { - _endInputText = text; - _endDate = _parseDate(text); - widget.onEndDateChanged?.call(_endDate); - }); - if (widget.autovalidate) { - validate(); - } - } - - @override - Widget build(BuildContext context) { - final MaterialLocalizations localizations = MaterialLocalizations.of(context); - final InputDecorationTheme inputTheme = Theme.of(context).inputDecorationTheme; - return Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: TextField( - controller: _startController, - decoration: InputDecoration( - border: inputTheme.border ?? const UnderlineInputBorder(), - filled: inputTheme.filled, - hintText: widget.fieldStartHintText ?? localizations.dateHelpText, - labelText: widget.fieldStartLabelText ?? localizations.dateRangeStartLabel, - errorText: _startErrorText, - ), - keyboardType: TextInputType.datetime, - onChanged: _handleStartChanged, - autofocus: widget.autofocus, - ), - ), - const SizedBox(width: 8), - Expanded( - child: TextField( - controller: _endController, - decoration: InputDecoration( - border: inputTheme.border ?? const UnderlineInputBorder(), - filled: inputTheme.filled, - hintText: widget.fieldEndHintText ?? localizations.dateHelpText, - labelText: widget.fieldEndLabelText ?? localizations.dateRangeEndLabel, - errorText: _endErrorText, - ), - keyboardType: TextInputType.datetime, - onChanged: _handleEndChanged, - ), - ), - ], - ); - } -} diff --git a/packages/components/lib/project_ui/project_ui.dart b/packages/components/lib/project_ui/project_ui.dart deleted file mode 100644 index 6d54f3de6..000000000 --- a/packages/components/lib/project_ui/project_ui.dart +++ /dev/null @@ -1,8 +0,0 @@ -export 'default/empty_search_page.dart'; -export 'default/empty_shower.dart'; -export 'default/error_shower.dart'; -export 'default/loading_shower.dart'; -export 'default/error_page.dart'; -export 'default/no_more_widget.dart'; -export 'wrapper/honour_wrapper.dart'; -export 'unit_app_bar.dart'; \ No newline at end of file diff --git a/packages/components/lib/toly_ui/code/code_widget.dart b/packages/components/lib/toly_ui/code/code_widget.dart deleted file mode 100644 index 019db6ffd..000000000 --- a/packages/components/lib/toly_ui/code/code_widget.dart +++ /dev/null @@ -1,50 +0,0 @@ - -/// create by 张风捷特烈 on 2020-04-15 -/// contact me by email 1981462002@qq.com -/// 说明: -import 'package:flutter/material.dart'; - -import 'high_light_code.dart'; -import 'highlighter_style.dart'; -import 'language/dart_languge.dart'; - -class CodeWidget extends StatelessWidget { - CodeWidget({Key? key, required this.code,required this.style, this.fontSize = 13,this.fontFamily}) - : super(key: key); - - final String code; - final HighlighterStyle style; - final double fontSize; - final String? fontFamily; - - @override - Widget build(BuildContext context) { - Widget body; - Widget _codeWidget; - try { - _codeWidget = RichText( - text: TextSpan( - style: TextStyle(fontSize: fontSize,fontFamily: fontFamily), - children: [ - CodeHighlighter( - style: style, - language: const DartLanguage() - ).format(code)], - ), - ); - } catch (err) { - print(err); - _codeWidget = Text(code); - } - body = SingleChildScrollView( - child: Container( - child: _codeWidget, - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - color: style.backgroundColor ?? const Color(0xffF6F8FA), - borderRadius: const BorderRadius.all(Radius.circular(5.0))), - ), - ); - return body; - } -} \ No newline at end of file diff --git a/packages/components/lib/toly_ui/code/language/language.dart b/packages/components/lib/toly_ui/code/language/language.dart deleted file mode 100644 index c2ea52d46..000000000 --- a/packages/components/lib/toly_ui/code/language/language.dart +++ /dev/null @@ -1,17 +0,0 @@ -/// create by 张风捷特烈 on 2021/1/21 -/// contact me by email 1981462002@qq.com -/// 说明: - -abstract class Language { - final String name; - - const Language(this.name); - - bool containsKeywords(String word); - - bool containsInTypes(String word); - - List get keywords; - - List get inTypes; -} diff --git a/packages/components/lib/toly_ui/ti/circle_image.dart b/packages/components/lib/toly_ui/ti/circle_image.dart deleted file mode 100644 index c1146cb8d..000000000 --- a/packages/components/lib/toly_ui/ti/circle_image.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:flutter/material.dart'; - -class CircleImage extends StatelessWidget { - - const CircleImage({ - Key? key, - this.borderSize = 3, - required this.image, - this.size = 70, - this.shadowColor, - this.roundColor, - }) : super(key: key); - - final ImageProvider image; //图片 - final double size; //大小 - final Color? shadowColor; //阴影颜色 - final Color? roundColor; //边框颜色 - final double borderSize; - - @override - Widget build(BuildContext context) { - return Container( - width: size, - height: size, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: roundColor ?? Colors.white, - boxShadow: [ - BoxShadow( - color: shadowColor ?? Colors.grey.withOpacity(0.3), - offset: const Offset(0.0, 0.0), - blurRadius: 3.0, - spreadRadius: 0.0, - ), - ], - ), - child: Padding( - padding: EdgeInsets.all(borderSize), - child: DecoratedBox( - decoration: BoxDecoration( - image: DecorationImage( - image: image, - fit: BoxFit.cover, - filterQuality: FilterQuality.low), - shape: BoxShape.circle, - ), - ), - ), - ); - } -} diff --git a/packages/components/lib/toly_ui/ti/panel.dart b/packages/components/lib/toly_ui/ti/panel.dart deleted file mode 100644 index d1b4f24fd..000000000 --- a/packages/components/lib/toly_ui/ti/panel.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - - -class Panel extends StatelessWidget { - final double radius; - final Color? color; - final Widget? child; - - const Panel({Key? key, this.radius = 5.0, this.color, this.child}) : super(key: key); - - @override - Widget build(BuildContext context) { - - return Container( - child: child, - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - color: color ?? const Color(0xffF6F8FA), - borderRadius: BorderRadius.all(Radius.circular(radius))), - ); - } -} - - diff --git a/packages/components/pubspec.yaml b/packages/components/pubspec.yaml deleted file mode 100644 index e6a0f6117..000000000 --- a/packages/components/pubspec.yaml +++ /dev/null @@ -1,55 +0,0 @@ -name: components -description: A new Flutter package project. -version: 0.0.1 -homepage: - -environment: - sdk: '>=2.18.5 <3.0.0' - flutter: ">=1.17.0" - -dependencies: - flutter: - sdk: flutter - flutter_spinkit: ^5.2.0 # loading - flutter_markdown: ^0.6.4 # markdown -dev_dependencies: - flutter_test: - sdk: flutter - flutter_lints: ^2.0.0 - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter packages. -flutter: - - # To add assets to your package, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - # - # For details regarding assets in packages, see - # https://flutter.dev/assets-and-images/#from-packages - # - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware - - # To add custom fonts to your package, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts in packages, see - # https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/storage/lib/src/db_storage/app_db_storage.dart b/packages/storage/lib/src/db_storage/app_db_storage.dart deleted file mode 100644 index 82df17f9e..000000000 --- a/packages/storage/lib/src/db_storage/app_db_storage.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'dart:async'; -import 'dart:io'; - -import 'package:storage/storage.dart'; - -import 'dao/cache_dao.dart'; -import 'helper/db_open_helper.dart'; -import 'package:sqflite_common_ffi/sqflite_ffi.dart'; - -import 'package:path/path.dart' as path; -import 'package:sqflite/sqflite.dart'; - -import 'update/db_updater.dart'; - - -class AppDbStorage { - Database? _database; - - AppDbStorage._(); - - static AppDbStorage instance = AppDbStorage._(); - - late CacheDao _cacheDao; - - CacheDao get cacheDao => _cacheDao; - - Database get db => _database!; - - Future initDb({String name = "flutter_unit.db"}) async { - if (_database != null) return; - String databasesPath = await DbOpenHelper.getDbDirPath(); - String dbPath = path.join(databasesPath, name); - - if (Platform.isWindows||Platform.isLinux) { - DatabaseFactory databaseFactory = databaseFactoryFfi; - _database = await databaseFactory.openDatabase( - dbPath, - options: OpenDatabaseOptions( - version: DbUpdater.version, - onCreate: _onCreate, - onUpgrade: _onUpgrade, - onOpen: _onOpen - ), - ); - }else{ - _database = await openDatabase(dbPath, - version: DbUpdater.version , - onCreate: _onCreate, - onUpgrade: _onUpgrade, - onOpen: _onOpen, - ); - } - - } - - DbUpdater updater = DbUpdater(); - - FutureOr _onCreate(Database db, int version) async { - print('数据库创建:flutter_unit....'); - await Future.wait([ - CacheDao.createDb(db), - updater.update(db, 1, DbUpdater.version) - ]); - } - - FutureOr _onOpen(Database db) { - print('数据库打开:flutter_unit....'); - _database = db; - _cacheDao = CacheDao(db); - } - - Future closeDb() async { - await _database?.close(); - _database = null; - } - - FutureOr _onUpgrade(Database db, int oldVersion, int newVersion) async { - print('数据库更新:flutter_unit....$oldVersion -> $newVersion'); - await updater.update(db, oldVersion, newVersion); - } - -} diff --git a/packages/storage/lib/src/db_storage/dao/cache_dao.dart b/packages/storage/lib/src/db_storage/dao/cache_dao.dart deleted file mode 100644 index c19e8d8fa..000000000 --- a/packages/storage/lib/src/db_storage/dao/cache_dao.dart +++ /dev/null @@ -1,84 +0,0 @@ -import 'package:sqflite/sqflite.dart'; - -import '../models/cache_po.dart'; - -class CacheDao { - final Database database; - - CacheDao(this.database); - - // 表名 - static const String kTableName = 'app_cache'; - - static const String createTable = """ -CREATE TABLE IF NOT EXISTS `$kTableName` ( -`id` INTEGER PRIMARY KEY AUTOINCREMENT, -`filter` TEXT, -`content` TEXT, -`update` INTEGER, -`create` INTEGER, -`type` INTEGER -)"""; - - static Future createDb(Database db) => db.execute(createTable); - - Future insert(CachePo po) => database.insert( - kTableName, - po.toJson(), - conflictAlgorithm: ConflictAlgorithm.replace, - ); - - Future insertOrUpdate(CachePo po) async { - bool canUpdate = await shouldUpdate(po.id, po.update); - return database.insert( - kTableName, - po.toJson(), - conflictAlgorithm: - canUpdate ? ConflictAlgorithm.replace : ConflictAlgorithm.ignore, - ); - } - - /// 当前数据是否需要更新 - Future shouldUpdate(int id, int updateAt) async { - List> data = await database - .rawQuery("SELECT `update` FROM $kTableName WHERE id = ?", [id]); - // 没有数据,可以更新 - if (data.isEmpty) { - return true; - } - // 服务器中数据更新时间,大于本地数据库内容,可以更新 - return updateAt > data.first['update']; - } - - Future> query({ - required int type, - int page = 1, - int pageSize = 20, - String? filter, - }) async { - String queryArgs = ''; - List args = [type]; - queryArgs = "WHERE type = ? "; - if(filter!=null){ - queryArgs+="AND filter = ? "; - args.add(filter); - } - queryArgs += 'LIMIT ? OFFSET ?'; - args.addAll([pageSize, (page - 1) * pageSize]); - - List> data = await database.rawQuery( - "SELECT * FROM $kTableName $queryArgs", - args, - ); - - List result = data.map((e) => CachePo.fromJson(e)).toList(); - return result; - } -} - -// final String id; -// final String title; -// final String content; -// final int type; -// final int create; -// final int update; diff --git a/packages/storage/lib/src/db_storage/dao/category_dao.dart b/packages/storage/lib/src/db_storage/dao/category_dao.dart deleted file mode 100644 index 669b9d2fb..000000000 --- a/packages/storage/lib/src/db_storage/dao/category_dao.dart +++ /dev/null @@ -1,199 +0,0 @@ - -import 'package:sqflite/sqflite.dart'; -import '../models/category_po.dart'; - -//""" -// CREATE TABLE IF NOT EXISTS category_widget( -// id INTEGER PRIMARY KEY AUTOINCREMENT, -// name VARCHAR(64) NOT NULL, -// color VARCHAR(9) DEFAULT '#FF2196F3', -// info VARCHAR(256) DEFAULT '这里什么都没有...', -// created DATETIME NOT NULL, -// updated DATETIME NOT NULL, -// priority INTEGER DEFAULT 0, -// image VARCHAR(128) NULL image DEFAULT '' -// ); -//"""; - -class CategoryDao { - final Database db; - - CategoryDao(this.db); - - - - Future insert(CategoryPo category) async { - //插入方法 - String addSql = //插入数据 - "INSERT INTO " - "category(id,name,color,info,priority,image,created,updated) " - "VALUES (?,?,?,?,?,?,?,?);"; - return await db.transaction((tran) async => await tran.rawInsert(addSql, [ - category.id, - category.name, - category.color, - category.info, - category.priority, - category.image, - category.created?.toIso8601String(), - category.updated.toIso8601String(), - ])); - } - - Future update(CategoryPo widget) async { - //插入方法 - String updateSql = //插入数据 - "UPDATE category SET name=? , color=? ,info=?, priority=?,image=?,updated=? " - "WHERE id = ?"; - - return await db.transaction((tran) async => - await tran.rawUpdate(updateSql, [ - widget.name, - widget.color, - widget.info, - widget.priority, - widget.image, - widget.updated.toIso8601String(), - widget.id, - ])); - } - - - - Future addWidget(int categoryId,int widgetId,) async { - String addSql = //插入数据 - "INSERT INTO " - "category_widget(widgetId,categoryId) " - "VALUES (?,?);"; - return await db.transaction((tran) async => await tran.rawInsert(addSql, [ - widgetId, - categoryId, - ])); - } - - Future addWidgets(int categoryId,List widgetIds) async { - String addSql = //插入数据 - "INSERT INTO " - "category_widget(widgetId,categoryId) VALUES "; - - String args = ''; - - for(int i=0;i< widgetIds.length;i++){ - args+= "(${widgetIds[i]},$categoryId)"; - if(i==widgetIds.length-1){ - args+=";"; - }else{ - args+=","; - } - } - addSql += args; - return await db.transaction((tran) async => await tran.rawInsert(addSql)); - } - - Future existByName(String name) async { - String sql = //插入数据 - "SELECT COUNT(name) as count FROM category " - "WHERE name = ?"; - List> rawData = await db.rawQuery(sql, [name]); - if (rawData.isNotEmpty) { - return rawData[0]['count'] > 0; - } - return false; - } - - Future>> queryAll() async { - List> data = await db.rawQuery( - "SELECT c.id,c.name,c.info,c.color,c.image,c.created,c.updated,c.priority,COUNT(cw.categoryId) as `count`" - "FROM category AS c " - "LEFT JOIN category_widget AS cw " - "ON c.id = cw.categoryId GROUP BY c.id " - "ORDER BY priority DESC,created DESC", - []); - return data; - } - - Future> categoryWidgetIds(int id) async { - List> data = await db.rawQuery( - "SELECT categoryId FROM `category_widget`" - "WHERE widgetId = ?", - [id]); - return data.toList().map((e)=>e["categoryId"]).toList(); - } - - - - Future deleteCollect(int id) async { - await db.execute( - "DELETE FROM category_widget " - "WHERE categoryId = ?", - [id]); - return await db.execute( - "DELETE FROM category " - "WHERE id = ?", - [id]); - } - - Future clear() async { - await db.execute( - "DELETE FROM category_widget " - "WHERE categoryId >0"); - return await db.execute( - "DELETE FROM category " - "WHERE id > 0"); - } - - Future removeWidget(int categoryId, int widgetId) async { - //插入方法 - String deleteSql = //插入数据 - "DELETE FROM " - "category_widget WHERE categoryId = ? AND widgetId = ? "; - return await db - .transaction((tran) async => await tran.rawInsert(deleteSql, [ - categoryId, - widgetId, - ])); - } - - Future existWidgetInCollect(int categoryId, int widgetId) async { - String sql = //插入数据 - "SELECT COUNT(id) as count FROM category_widget " - "WHERE categoryId = ? AND widgetId = ?"; - List> rawData = await db.rawQuery(sql, [categoryId, widgetId]); - if (rawData.isNotEmpty) { - return rawData[0]['count'] > 0; - } - return false; - } - - Future toggleCollect(int categoryId, int widgetId) async { - if (await existWidgetInCollect(categoryId, widgetId)) { - //已存在: 移除 - await removeWidget(categoryId, widgetId); - } else { - await addWidget(categoryId, widgetId); - } - } - - Future toggleCollectDefault(int widgetId) async { - await toggleCollect(1, widgetId); - } - - Future>> loadCollectWidgets(int categoryId) async{ - String querySql = //插入数据 - "SELECT * FROM widget " - "WHERE id IN (SELECT widgetId FROM category_widget WHERE categoryId = ?) " - "ORDER BY lever DESC"; - - return await db.rawQuery(querySql,[categoryId]); - } - - Future> loadCollectWidgetIds(int categoryId) async{ - String querySql = //插入数据 - "SELECT id FROM widget " - "WHERE id IN (SELECT widgetId FROM category_widget WHERE categoryId = ?) " - "ORDER BY lever DESC"; - - var data = await db.rawQuery(querySql,[categoryId]); - return data.map((e) => e["id"] as int).toList(); - } -} diff --git a/packages/storage/lib/src/db_storage/dao/like_dao.dart b/packages/storage/lib/src/db_storage/dao/like_dao.dart deleted file mode 100644 index aa072fc74..000000000 --- a/packages/storage/lib/src/db_storage/dao/like_dao.dart +++ /dev/null @@ -1,64 +0,0 @@ -import 'package:sqflite/sqflite.dart'; - -class LikeDao { - final Database db; - - LikeDao(this.db); - - Future> likeWidgetIds() async { - var result = await db.rawQuery("SELECT widget_id FROM like_widget"); - var ids = result.map((e) => e['widget_id'] as int).toList(); - return ids; - } - - // 如果已喜欢,取消喜欢 - // 如果未喜欢,设为喜欢 - Future toggleCollect(int widgetId) async { - bool liked = await isLiked(widgetId); - - if (liked) { - await unlike(widgetId, check: false); - } else { - await like(widgetId, check: false); - } - } - - Future like(int widgetId, {bool check = true}) async { - if (check) { - // 如果 liked_widget_bloc ,直接取消,不执行 liked_widget_bloc 操作 - bool liked = await isLiked(widgetId); - if (liked) return 0; - } - - return await db.rawInsert( - "INSERT INTO " - "like_widget(widget_id) " - "VALUES (?);", - [widgetId]); - } - - Future unlike(int widgetId, {bool check = true}) async { - if (check) { - // 如果未 liked_widget_bloc ,直接取消,不执行 unlike 操作 - bool liked = await isLiked(widgetId); - if (!liked) return; - } - await db.execute( - "DELETE FROM like_widget " - "WHERE widget_id = ?", - [widgetId]); - } - - // 判断组件是否已 liked - Future isLiked(int widgetId) async { - var data = await db.rawQuery( - "Select count(id) as `count` FROM like_widget " - "WHERE widget_id = ?", - [widgetId]); - if (data.isNotEmpty) { - var result = data[0]; - return result['count'] as int > 0; - } - return false; - } -} diff --git a/packages/storage/lib/src/db_storage/dao/node_dao.dart b/packages/storage/lib/src/db_storage/dao/node_dao.dart deleted file mode 100644 index 3e4258698..000000000 --- a/packages/storage/lib/src/db_storage/dao/node_dao.dart +++ /dev/null @@ -1,39 +0,0 @@ -import '../models/node_po.dart'; -import 'package:sqflite/sqflite.dart'; - -class NodeDao { - - final Database db; - - NodeDao(this.db); - - Future insert(NodePo widget) async { - //插入方法 - String addSql = //插入数据 - "INSERT INTO " - "node(widgetId,name,priority,subtitle,code) " - "VALUES (?,?,?,?,?);"; - return await db.transaction((tran) async => await tran.rawInsert(addSql, [ - widget.widgetId, - widget.name, - widget.priority, - widget.subtitle, - widget.code - ])); - } - - Future>> queryAll() async { - //插入方法 - return await db.rawQuery("SELECT * " - "FROM node"); - } - - //根据 id 查询组件 node - Future>> queryById(int id) async { - return await db.rawQuery( - "SELECT name,subtitle,code " - "FROM node " - "WHERE widgetId = ? ORDER BY priority", - [id]); - } -} diff --git a/packages/storage/lib/src/db_storage/dao/widget_dao.dart b/packages/storage/lib/src/db_storage/dao/widget_dao.dart deleted file mode 100644 index 30064c5fa..000000000 --- a/packages/storage/lib/src/db_storage/dao/widget_dao.dart +++ /dev/null @@ -1,91 +0,0 @@ - -import 'package:sqflite/sqflite.dart'; -import 'package:widget_repository/widget_repository.dart'; - -import '../models/widget_po.dart'; - - -class WidgetDao { - - final Database db; - - WidgetDao(this.db); - - Future insert(WidgetPo widget) async { - //插入方法 - String addSql = //插入数据 - "INSERT INTO " - "widget(id,name,nameCN,deprecated,family,lever,linkWidget,info) " - "VALUES (?,?,?,?,?,?,?,?);"; - return db.transaction((tran) async => await tran.rawInsert(addSql, [ - widget.id, - widget.name, - widget.nameCN, - widget.deprecated, - widget.family, - widget.lever, - widget.linkWidget, - widget.info - ])); - } - - Future>> queryAll() async { - return db.rawQuery("SELECT * FROM widget"); - } - - Future>> queryByFamily(WidgetFamily family) async { - return db.rawQuery( - "SELECT * " - "FROM widget WHERE family = ? ORDER BY lever DESC", - [family.index]); - } - - Future>> queryByIds(List ids) async { - if (ids.isEmpty) { - return []; - } - String sql = "SELECT * " - "FROM widget WHERE id in (${'?,' * (ids.length - 1)}?) "; - - return db.rawQuery(sql, [...ids]); - } - - Future>> search(WidgetFilter arguments) async { - // 保证 name 参数为空时,不进行搜索 - if (arguments.name.isEmpty) { - return []; - } - - // _表示 name 任意 - String name = arguments.name == '*' ? '' : arguments.name; - bool hasFamily = arguments.family != null; - String familySql = hasFamily ? ' AND family = ?' : ''; - List familyArg = hasFamily ? [arguments.family!.index] : []; - List starArg = arguments.stars; - // 保证在星级参数是 [-1,-1,-1,-1,-1] 时,搜索全星级 - if (arguments.stars.reduce((a, b) => a + b) == -5) { - starArg = [1, 2, 3, 4, 5]; - } - return db.rawQuery( - "SELECT * " - "FROM widget WHERE name like ?$familySql AND lever IN(?,?,?,?,?) ORDER BY lever DESC LIMIT ? OFFSET ?", - ["%$name%", ...familyArg, ...starArg, arguments.pageSize, arguments.offset]); - } - - Future total(WidgetFilter args) async{ - bool hasFamily = args.family != null; - String familySql = hasFamily ? 'family = ?' : ''; - List familyArg = hasFamily ? [args.family!.index] : []; - - String sql = "SELECT count(id) as `count` FROM widget WHERE $familySql"; - - List> result = await db.rawQuery(sql,familyArg); - if(result.isNotEmpty){ - return result.first['count'] as int ??0; - } - return 0; - - } -} - - diff --git a/packages/storage/lib/src/db_storage/exp.dart b/packages/storage/lib/src/db_storage/exp.dart deleted file mode 100644 index 3d3c70a92..000000000 --- a/packages/storage/lib/src/db_storage/exp.dart +++ /dev/null @@ -1,13 +0,0 @@ -export 'dao/category_dao.dart'; -export 'dao/like_dao.dart'; -export 'dao/node_dao.dart'; -export 'dao/widget_dao.dart'; -export 'dao/cache_dao.dart'; - -export 'models/category_po.dart'; -export 'models/node_po.dart'; -export 'models/widget_po.dart'; -export 'models/cache_po.dart'; -export 'flutter_db_storage.dart'; -export 'app_db_storage.dart'; -export 'helper/db_open_helper.dart'; \ No newline at end of file diff --git a/packages/storage/lib/src/db_storage/flutter_db_storage.dart b/packages/storage/lib/src/db_storage/flutter_db_storage.dart deleted file mode 100644 index ef0a6db44..000000000 --- a/packages/storage/lib/src/db_storage/flutter_db_storage.dart +++ /dev/null @@ -1,75 +0,0 @@ -import 'dart:io'; - - - -import 'package:artifact/artifact.dart'; -import 'package:storage/storage.dart'; - -import 'helper/db_open_helper.dart'; -import 'package:sqflite_common_ffi/sqflite_ffi.dart'; - -import 'package:path/path.dart' as path; -import 'package:sqflite/sqflite.dart'; - - -class FlutterDbStorage { - Database? _database; - - FlutterDbStorage._(); - - static FlutterDbStorage instance = FlutterDbStorage._(); - - late WidgetDao _widgetDao; - late CategoryDao _categoryDao; - late NodeDao _nodeDao; - late LikeDao _likeDao; - late ArticleDao _articleDao; - late ColumnizeDao _columnizeDao; - - WidgetDao get widgetDao => _widgetDao; - - CategoryDao get categoryDao => _categoryDao; - - NodeDao get nodeDao => _nodeDao; - - LikeDao get likeDao => _likeDao; - ArticleDao get articleDao => _articleDao; - ColumnizeDao get columnizeDao => _columnizeDao; - - Database get db => _database!; - - Future initDb({String name = "flutter.db"}) async { - if (_database != null) return; - String databasesPath = await DbOpenHelper.getDbDirPath(); - String dbPath = path.join(databasesPath, name); - - if (Platform.isWindows||Platform.isLinux) { - DatabaseFactory databaseFactory = databaseFactoryFfi; - _database = await databaseFactory.openDatabase( - dbPath, - options: OpenDatabaseOptions( - // version: DbUpdater.VERSION, - // onCreate: _onCreate, - // onUpgrade: _onUpgrade, - // onOpen: _onOpen - ), - ); - }else{ - _database = await openDatabase(dbPath); - } - - _widgetDao = WidgetDao(_database!); - _categoryDao = CategoryDao(_database!); - _nodeDao = NodeDao(_database!); - _likeDao = LikeDao(_database!); - _articleDao = ArticleDao(_database!); - _columnizeDao = ColumnizeDao(_database!); - - print('初始化数据库....'); - } - - Future closeDb() async { - await _database?.close(); - _database = null; - } -} diff --git a/packages/storage/lib/src/db_storage/helper/db_open_helper.dart b/packages/storage/lib/src/db_storage/helper/db_open_helper.dart deleted file mode 100644 index 45ffe979c..000000000 --- a/packages/storage/lib/src/db_storage/helper/db_open_helper.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'dart:io'; - -import 'package:path_provider/path_provider.dart'; -import 'package:path/path.dart' as path; -import 'dart:ffi'; - -import 'package:sqlite3/open.dart'; -import 'package:sqlite3/sqlite3.dart'; -import 'package:path/path.dart'; - - -class DbOpenHelper{ - - static void setupDatabase(){ - if(Platform.isWindows){ - String location = Directory.current.path; - _windowsInit(join(location, 'sqlite3.dll')); - } - } - - static void _windowsInit(String path) { - open.overrideFor(OperatingSystem.windows, () { - try { - return DynamicLibrary.open(path); - } catch (e) { - stderr.writeln('Failed to load sqlite3.dll at $path'); - rethrow; - } - }); - sqlite3.openInMemory().dispose(); - } - - static Future getDbDirPath() async{ - Directory appDocDir = await getApplicationDocumentsDirectory(); - String dirName = 'databases'; - String dirPath = path.join(appDocDir.path, dirName); - - if(Platform.isAndroid){ - dirPath = path.join(appDocDir.parent.path, dirName); - } - if(Platform.isWindows||Platform.isLinux){ - dirPath = path.join(appDocDir.path, 'FlutterUnit','databases'); - } - - Directory result = Directory(dirPath); - if(!result.existsSync()){ - result.createSync(recursive: true); - } - print('====数据库所在文件夹: $dirPath======='); - return dirPath; - } -} \ No newline at end of file diff --git a/packages/storage/lib/src/db_storage/models/widget_po.dart b/packages/storage/lib/src/db_storage/models/widget_po.dart deleted file mode 100644 index 70f4dfd81..000000000 --- a/packages/storage/lib/src/db_storage/models/widget_po.dart +++ /dev/null @@ -1,62 +0,0 @@ - -/// create by 张风捷特烈 on 2020-03-04 -/// contact me by email 1981462002@qq.com -/// 说明: 组件信息-数据库-数据模型 -/// - - -class WidgetPo { - final int id; - final String name; - final String nameCN; - final int deprecated; - final int family; - final double lever; - final String info; - final String linkWidget; - - const WidgetPo({ - required this.id, - required this.name, - required this.nameCN, - required this.deprecated, - required this.family, - required this.lever, - required this.linkWidget, - required this.info, - }); - - factory WidgetPo.fromJson(Map map) { - return WidgetPo( - id: map['id'], - name: map['name'], - nameCN: map["nameCN"], - family: map["family"], - deprecated: map["deprecated"] ?? 0, - lever: map["lever"].toDouble(), - linkWidget: map["linkWidget"], - info: map["info"]); - } - - Map toJson() { - return { - "id": id, - "name": name, - "nameCN": nameCN, - "family": family, - "deprecated": deprecated, - "lever": lever, - "linkWidget": linkWidget, - "info": info - }; - } - - @override - String toString() { - return 'WidgetPo{id: $id, name: $name, nameCN: $nameCN, deprecated: $deprecated, family: $family, lever: $lever, info: $info}'; - } - - @override - List get props => - [id, name, nameCN, deprecated, family, linkWidget, lever, info]; -} diff --git a/packages/storage/lib/src/db_storage/update/db_migration.dart b/packages/storage/lib/src/db_storage/update/db_migration.dart deleted file mode 100644 index b801ad604..000000000 --- a/packages/storage/lib/src/db_storage/update/db_migration.dart +++ /dev/null @@ -1,22 +0,0 @@ - -import 'package:sqflite/sqflite.dart'; - -typedef MigrationOperation = Future Function(Database database); - -class DbMigration{ - final Map _migrationMap={}; - - void addMigration(int version,MigrationOperation operation){ - _migrationMap[version] = operation; - } - - Future migration(Database database,int oldVersion,int newVersion) async{ - for(int i=0;i<_migrationMap.length;i++){ - int version = _migrationMap.keys.toList()[i]; - MigrationOperation operation = _migrationMap.values.toList()[i]; - if(version=oldVersion){ - await operation(database); - } - } - } -} \ No newline at end of file diff --git a/packages/storage/lib/src/db_storage/update/db_updater.dart b/packages/storage/lib/src/db_storage/update/db_updater.dart deleted file mode 100644 index c74365849..000000000 --- a/packages/storage/lib/src/db_storage/update/db_updater.dart +++ /dev/null @@ -1,23 +0,0 @@ - -import 'package:sqflite/sqflite.dart'; -import 'db_migration.dart'; - -class DbUpdater { - static const version = 1; - - Future update(Database db, int oldVersion, int newVersion) async { - DbMigration dbMigration = DbMigration(); - // dbMigration.addMigration(1, migration_1_2); - // dbMigration.addMigration(2, migration_2_3); - // dbMigration.addMigration(3, migration_3_4); - // dbMigration.addMigration(4, migration_4_5); - // dbMigration.addMigration(5, migration_5_6); - await dbMigration.migration(db, oldVersion, newVersion); - } - - Future migration_1_2(Database database) async { - - } - - -} diff --git a/packages/storage/lib/storage.dart b/packages/storage/lib/storage.dart deleted file mode 100644 index 798c6946a..000000000 --- a/packages/storage/lib/storage.dart +++ /dev/null @@ -1,5 +0,0 @@ -library storage; - - -export 'src/db_storage/exp.dart'; -export 'src/sp_storage/exp.dart'; diff --git a/packages/storage/pubspec.yaml b/packages/storage/pubspec.yaml deleted file mode 100644 index 13814716c..000000000 --- a/packages/storage/pubspec.yaml +++ /dev/null @@ -1,63 +0,0 @@ -name: storage -description: A new Flutter project. -version: 0.0.1 -homepage: - -environment: - sdk: '>=2.18.1 <3.0.0' - flutter: ">=1.17.0" - -dependencies: - flutter: - sdk: flutter - sqflite: ^2.3.0 # 数据库 - sqflite_common_ffi: ^2.3.0+2 # 数据库 - shared_preferences: ^2.2.1 # xml 固化 - path_provider: ^2.1.1 # 路径 - - widget_repository: - path: ../widget_repository - artifact: - path: ../artifact - -dev_dependencies: - flutter_test: - sdk: flutter - flutter_lints: ^2.0.0 - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter packages. -flutter: - - # To add assets to your package, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - # - # For details regarding assets in packages, see - # https://flutter.dev/assets-and-images/#from-packages - # - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware - - # To add custom fonts to your package, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts in packages, see - # https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/utils/lib/src/color_utils.dart b/packages/utils/lib/src/color_utils.dart deleted file mode 100644 index 4d0f8f438..000000000 --- a/packages/utils/lib/src/color_utils.dart +++ /dev/null @@ -1,71 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; - -import 'random_provider.dart'; - -class ColorUtils { - static Color randomColor({ - int limitA = 120, - int limitR = 0, - int limitG = 0, - int limitB = 0, - }) { - Random random = RandomProvider.random; - int a = limitA + random.nextInt(256 - limitA); //透明度值 - int r = limitR + random.nextInt(256 - limitR); //红值 - int g = limitG + random.nextInt(256 - limitG); //绿值 - int b = limitB + random.nextInt(256 - limitB); //蓝值 - return Color.fromARGB(a, r, g, b); //生成argb模式的颜色 - } - - - /// 使用方法: - /// var color1=ColorUtils.parse("#33428A43"); - /// var color2=ColorUtils.parse("33428A43"); - /// var color3=ColorUtils.parse("#428A43"); - ///var color4=ColorUtils.parse("428A43"); - /// - static Color parse(String code) { - Color result =Colors.red; - int value = 0 ; - if (code.contains("#")) { - try { - value = int.parse(code.substring(1), radix: 16); - } catch (e) { - print(e); - } - switch (code.length) { - case 1 + 6://6位 - result = Color(value + 0xFF000000); - break; - case 1 + 8://8位 - result = Color(value); - break; - default: - result =Colors.red; - } - }else { - try { - value = int.parse(code, radix: 16); - } catch (e) { - print(e); - } - switch (code.length) { - case 6: - result = Color(value + 0xFF000000); - break; - case 8: - result = Color(value); - break; - default: - result =Colors.red; - } - } - return result; - } - - static String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} - diff --git a/packages/utils/lib/src/random_provider.dart b/packages/utils/lib/src/random_provider.dart deleted file mode 100644 index b34218b16..000000000 --- a/packages/utils/lib/src/random_provider.dart +++ /dev/null @@ -1,7 +0,0 @@ -import 'dart:math'; - -class RandomProvider{ - RandomProvider._();//私有化构造 - static final _random= Random(); - static Random get random =>_random; -} \ No newline at end of file diff --git a/packages/utils/pubspec.yaml b/packages/utils/pubspec.yaml deleted file mode 100644 index 9e3d7896e..000000000 --- a/packages/utils/pubspec.yaml +++ /dev/null @@ -1,55 +0,0 @@ -name: utils -description: utils -version: 0.0.1 -homepage: - -environment: - sdk: '>=2.18.1 <3.0.0' - flutter: ">=1.17.0" - -dependencies: - flutter: - sdk: flutter - dio: ^5.3.2 # 网络请求 - jwt_decoder: ^2.0.1 # jwt 解析 -dev_dependencies: - flutter_test: - sdk: flutter - flutter_lints: ^2.0.0 - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter packages. -flutter: - - # To add assets to your package, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - # - # For details regarding assets in packages, see - # https://flutter.dev/assets-and-images/#from-packages - # - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware - - # To add custom fonts to your package, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts in packages, see - # https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/widget_module/lib/blocs/blocs.dart b/packages/widget_module/lib/blocs/blocs.dart deleted file mode 100644 index 3844b6083..000000000 --- a/packages/widget_module/lib/blocs/blocs.dart +++ /dev/null @@ -1,5 +0,0 @@ -export 'category_bloc/category_bloc.dart'; -export 'category_widget_bloc/category_widget_bloc.dart'; -export 'liked_widget_bloc/liked_widget_bloc.dart'; -export 'widget_detail_bloc/widget_detail_bloc.dart'; -export 'widgets_bloc/widgets_bloc.dart'; \ No newline at end of file diff --git a/packages/widget_module/lib/blocs/category_widget_bloc/category_widget_bloc.dart b/packages/widget_module/lib/blocs/category_widget_bloc/category_widget_bloc.dart deleted file mode 100644 index a99451dfb..000000000 --- a/packages/widget_module/lib/blocs/category_widget_bloc/category_widget_bloc.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:equatable/equatable.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:widget_repository/widget_repository.dart'; - - -import '../category_bloc/category_bloc.dart'; - -part 'category_widget_event.dart'; -part 'category_widget_state.dart'; - -/// create by 张风捷特烈 on 2020-04-21 -/// contact me by email 1981462002@qq.com -/// 说明: - -class CategoryWidgetBloc - extends Bloc { - final CategoryBloc categoryBloc; - - CategoryWidgetBloc({required this.categoryBloc}) - : super(CategoryWidgetEmptyState()){ - on(_onEventLoadCategoryWidget); - on(_onEventToggleCategoryWidget); - } - - CategoryRepository get repository => categoryBloc.repository; - - void _onEventLoadCategoryWidget(EventLoadCategoryWidget event, Emitter emit) async{ - final widgets = - await repository.loadCategoryWidgets(categoryId: event.categoryId); - widgets.isNotEmpty - ? emit(CategoryWidgetLoadedState(widgets)) - : emit(CategoryWidgetEmptyState()); - categoryBloc.add(const EventLoadCategory()); - } - - void _onEventToggleCategoryWidget(EventToggleCategoryWidget event, Emitter emit) async{ - await repository.toggleCategory(event.categoryId, event.widgetId); - add(EventLoadCategoryWidget(event.categoryId)); - } -} diff --git a/packages/widget_module/lib/blocs/category_widget_bloc/category_widget_event.dart b/packages/widget_module/lib/blocs/category_widget_bloc/category_widget_event.dart deleted file mode 100644 index 1e2d62bc2..000000000 --- a/packages/widget_module/lib/blocs/category_widget_bloc/category_widget_event.dart +++ /dev/null @@ -1,29 +0,0 @@ -part of 'category_widget_bloc.dart'; - -/// create by 张风捷特烈 on 2020-04-21 -/// contact me by email 1981462002@qq.com -/// 说明: - -abstract class CategoryWidgetEvent extends Equatable{ - @override - List get props => []; -} - -class EventLoadCategoryWidget extends CategoryWidgetEvent{ - final int categoryId; - - EventLoadCategoryWidget(this.categoryId); - - @override - List get props => [categoryId]; -} - -class EventToggleCategoryWidget extends CategoryWidgetEvent{ - final int categoryId; - final int widgetId; - - EventToggleCategoryWidget(this.categoryId,this.widgetId); - - @override - List get props => [categoryId,widgetId]; -} diff --git a/packages/widget_module/lib/blocs/liked_widget_bloc/liked_widget_bloc.dart b/packages/widget_module/lib/blocs/liked_widget_bloc/liked_widget_bloc.dart deleted file mode 100644 index 320354165..000000000 --- a/packages/widget_module/lib/blocs/liked_widget_bloc/liked_widget_bloc.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:equatable/equatable.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:widget_repository/widget_repository.dart'; - - -part 'liked_widget_event.dart'; -part 'liked_widget_state.dart'; - -/// create by 张风捷特烈 on 2020-04-07 -/// contact me by email 1981462002@qq.com -/// 说明: - -class LikeWidgetBloc extends Bloc { - final WidgetRepository repository; - - LikeWidgetBloc({required this.repository}):super(const LikeWidgetState(widgets: [])){ - on(_onEventLoadLikeData) ; - on(_onToggleLikeWidgetEvent) ; - } - - void _onEventLoadLikeData(EventLoadLikeData event, Emitter emit) async{ - final widgets = await repository.loadLikeWidgets(); - emit(LikeWidgetState(widgets: widgets)); - } - - void _onToggleLikeWidgetEvent(ToggleLikeWidgetEvent event, Emitter emit) async{ - await repository.toggleLike(event.id); - final widgets = await repository.loadLikeWidgets(); - emit(LikeWidgetState(widgets: widgets)); - } -} diff --git a/packages/widget_module/lib/blocs/liked_widget_bloc/liked_widget_event.dart b/packages/widget_module/lib/blocs/liked_widget_bloc/liked_widget_event.dart deleted file mode 100644 index f3e1d4873..000000000 --- a/packages/widget_module/lib/blocs/liked_widget_bloc/liked_widget_event.dart +++ /dev/null @@ -1,33 +0,0 @@ -part of 'liked_widget_bloc.dart'; - -/// create by 张风捷特烈 on 2020-04-09 -/// contact me by email 1981462002@qq.com -/// 说明: - -abstract class LikeWidgetEvent extends Equatable { - const LikeWidgetEvent(); -} - -class EventLoadLikeData extends LikeWidgetEvent { - const EventLoadLikeData(); - - @override - List get props => []; -} - -class ToggleLikeWidgetEvent extends LikeWidgetEvent { - final int id; - - const ToggleLikeWidgetEvent({required this.id}); - - @override - // TODO: implement props - List get props => [id]; -} - -class LoadCollectEvent extends LikeWidgetEvent { - const LoadCollectEvent(); - - @override - List get props => []; -} diff --git a/packages/widget_module/lib/blocs/liked_widget_bloc/liked_widget_state.dart b/packages/widget_module/lib/blocs/liked_widget_bloc/liked_widget_state.dart deleted file mode 100644 index 34b57f14a..000000000 --- a/packages/widget_module/lib/blocs/liked_widget_bloc/liked_widget_state.dart +++ /dev/null @@ -1,14 +0,0 @@ -part of 'liked_widget_bloc.dart'; - -/// create by 张风捷特烈 on 2020-04-09 -/// contact me by email 1981462002@qq.com -/// 说明: - -class LikeWidgetState extends Equatable { - final List widgets; - - const LikeWidgetState({required this.widgets}); - - @override - List get props => [widgets]; -} diff --git a/packages/widget_module/lib/blocs/widget_detail_bloc/widget_detail_bloc.dart b/packages/widget_module/lib/blocs/widget_detail_bloc/widget_detail_bloc.dart deleted file mode 100644 index 25e42e716..000000000 --- a/packages/widget_module/lib/blocs/widget_detail_bloc/widget_detail_bloc.dart +++ /dev/null @@ -1,62 +0,0 @@ -import 'package:equatable/equatable.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:widget_repository/widget_repository.dart'; - -part 'widget_detail_state.dart'; - -/// create by 张风捷特烈 on 2020-03-03 -/// contact me by email 1981462002@qq.com -/// 说明: 组件详情页状态数据维护核心类 -/// [_modelStack] 组件详情页支持管理跳转,栈数据交由 Bloc 维护 - -class WidgetDetailBloc extends Cubit { - final WidgetRepository widgetRepository; - final NodeRepository nodeRepository; - - WidgetDetailBloc({ - required this.widgetRepository, - required this.nodeRepository, - }) : super(DetailLoading()); - - final List _modelStack = []; - - WidgetModel get currentWidget => _modelStack.last; - - void push(WidgetModel model){ - _modelStack.add(model); - queryDetail(model); - } - - Future pop() async{ - if(_modelStack.isEmpty){ - return true; - } - _modelStack.removeLast(); - if (_modelStack.isNotEmpty) { - queryDetail(currentWidget); - return false; - } else { - return true; - } - } - - void queryDetail(WidgetModel widget) async { - emit(DetailLoading()); - try { - final List nodes = await nodeRepository.loadNode(widget.id); - final List links = await widgetRepository.loadWidget(widget.links); - if (nodes.isEmpty) { - emit(DetailEmpty()); - } else { - emit(DetailWithData( - widgetModel: widget, - nodes: nodes, - links: links, - )); - } - } catch (_) { - emit(DetailFailed()); - } - } -} diff --git a/packages/widget_module/lib/blocs/widgets_bloc/widgets_bloc.dart b/packages/widget_module/lib/blocs/widgets_bloc/widgets_bloc.dart deleted file mode 100644 index de35a77ee..000000000 --- a/packages/widget_module/lib/blocs/widgets_bloc/widgets_bloc.dart +++ /dev/null @@ -1,118 +0,0 @@ -import 'dart:async'; - -import 'package:equatable/equatable.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:widget_repository/widget_repository.dart'; - -part 'widgets_event.dart'; - -part 'widgets_state.dart'; - -/// create by 张风捷特烈 on 2020-03-03 -/// contact me by email 1981462002@qq.com -/// 说明: 处理主页 Widget 列表加载逻辑 - -class WidgetsBloc extends Bloc { - final WidgetRepository repository; - - WidgetsBloc({required this.repository}) : super(const WidgetsLoading()) { - on(_onEventTabTap); - on(_onEventLoadMore); - on(_onEventRefresh); - on(_onEventSearchWidget); - } - - /// 切换页签,以 [family] 为过滤项 - void _onEventTabTap(EventTabTap event, Emitter emit) async { - emit(const WidgetsLoading(operate: LoadOperate.load)); - WidgetFilter filter = WidgetFilter.family(event.family); - try { - final List widgets = await repository.searchWidgets(filter); - emit(WidgetsLoaded( - widgets: widgets, - filter: filter, - operate: LoadOperate.load, - )); - } catch (err) { - print(err); - emit(WidgetsLoadFailed( - err.toString(), - filter: filter, - operate: LoadOperate.load, - )); - } - } - - FutureOr _onEventRefresh( - EventRefresh event, Emitter emit) async { - // emit(const WidgetsLoading(operate: LoadOperate.refresh)); - try { - await Future.delayed(const Duration(milliseconds: 500)); - final List widgets = - await repository.searchWidgets(state.filter.copyWith( - page: 1, - )); - emit(WidgetsLoaded( - widgets: widgets, - filter: state.filter, - operate: LoadOperate.refresh, - fetchTime: DateTime.now().millisecondsSinceEpoch)); - } catch (err) { - print(err); - emit(WidgetsLoadFailed( - err.toString(), - filter: state.filter, - operate: LoadOperate.refresh, - )); - } - } - - FutureOr _onEventLoadMore( - EventLoadMore event, Emitter emit) async { - if (state is WidgetsLoaded) { - WidgetsLoaded old = (state as WidgetsLoaded); - int total = await repository.total(old.filter); - if (old.widgets.length < old.filter.pageSize) { - // 不满一页 - emit(old.copyWith(full: true, - operate: LoadOperate.more, - fetchTime: DateTime.now().millisecondsSinceEpoch,)); - return; - } - - if (total <= old.widgets.length) { - // 已满 - emit(old.copyWith(full: true, - operate: LoadOperate.more, - fetchTime: DateTime.now().millisecondsSinceEpoch,)); - return; - } - // 未满,继续加载下一页 - int pageIndex = old.widgets.length ~/ old.filter.pageSize + 1; - WidgetFilter filter = old.filter.copyWith(page: pageIndex); - final List newData = await repository.searchWidgets(filter); - List newWidget = old.widgets + newData; - emit(old.copyWith( - widgets: newWidget, - full: newWidget.length == total, - operate: LoadOperate.more, - fetchTime: DateTime.now().millisecondsSinceEpoch, - filter: filter)); - } - } - - void _onEventSearchWidget( - EventSearchWidget event, Emitter emit) async { - emit(const WidgetsLoading(operate: LoadOperate.load)); - try { - final List widgets = - await repository.searchWidgets(event.filter); - emit(WidgetsLoaded( - widgets: widgets, filter: event.filter, operate: LoadOperate.load)); - } catch (err) { - print(err); - emit(WidgetsLoadFailed(err.toString(), - filter: event.filter, operate: LoadOperate.load)); - } - } -} diff --git a/packages/widget_module/lib/widget_module.dart b/packages/widget_module/lib/widget_module.dart deleted file mode 100644 index a76ec00c8..000000000 --- a/packages/widget_module/lib/widget_module.dart +++ /dev/null @@ -1,3 +0,0 @@ -library widget_module; - -export ''; \ No newline at end of file diff --git a/packages/widget_module/pubspec.yaml b/packages/widget_module/pubspec.yaml deleted file mode 100644 index 00b635fcf..000000000 --- a/packages/widget_module/pubspec.yaml +++ /dev/null @@ -1,60 +0,0 @@ -name: widget_module -description: A new Flutter package project. -version: 0.0.1 -homepage: -publish_to: none - -environment: - sdk: '>=2.18.5 <3.0.0' - flutter: ">=1.17.0" - -dependencies: - flutter: - sdk: flutter - flutter_bloc: ^8.1.3 # 状态管理 - equatable: ^2.0.5 # 相等辅助 - widget_repository: - path: ../widget_repository - storage: - path: ../storage -dev_dependencies: - flutter_test: - sdk: flutter - flutter_lints: ^2.0.0 - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter packages. -flutter: - - # To add assets to your package, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - # - # For details regarding assets in packages, see - # https://flutter.dev/assets-and-images/#from-packages - # - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware - - # To add custom fonts to your package, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts in packages, see - # https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/widget_repository/.gitignore b/packages/widget_repository/.gitignore deleted file mode 100644 index 96486fd93..000000000 --- a/packages/widget_repository/.gitignore +++ /dev/null @@ -1,30 +0,0 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.buildlog/ -.history -.svn/ -migrate_working_dir/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ - -# Flutter/Dart/Pub related -# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. -/pubspec.lock -**/doc/api/ -.dart_tool/ -.packages -build/ diff --git a/packages/widget_repository/.metadata b/packages/widget_repository/.metadata deleted file mode 100644 index 6c08927eb..000000000 --- a/packages/widget_repository/.metadata +++ /dev/null @@ -1,10 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled and should not be manually edited. - -version: - revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 - channel: stable - -project_type: package diff --git a/packages/widget_repository/lib/src/db_impl/catagory_db_repository.dart b/packages/widget_repository/lib/src/db_impl/catagory_db_repository.dart deleted file mode 100644 index f0081958e..000000000 --- a/packages/widget_repository/lib/src/db_impl/catagory_db_repository.dart +++ /dev/null @@ -1,116 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; - -import 'package:storage/storage.dart'; - -import 'package:widget_repository/widget_repository.dart'; - - -/// create by 张风捷特烈 on 2020-04-21 -/// contact me by email 1981462002@qq.com -/// 说明: - -class CategoryDbRepository implements CategoryRepository { - - CategoryDao get categoryDao => FlutterDbStorage.instance.categoryDao; - LikeDao get likeDao => FlutterDbStorage.instance.likeDao; - - // CategoryDbRepository({required this.categoryDao,required this.likeDao}); - - @override - Future addCategory(CategoryPo categoryPo) async { - int success = await categoryDao.insert(categoryPo); - return success != -1; - } - - @override - Future check(int categoryId, int widgetId) async { - return await categoryDao.existWidgetInCollect(categoryId, widgetId); - } - - @override - Future deleteCategory(int id) async { - await categoryDao.deleteCollect(id); - } - - @override - Future> loadCategories() async { - List> data = await categoryDao.queryAll(); - List collects = - data.map((e) => CategoryPo.fromJson(e)).toList(); - return collects.map(CategoryModel.fromPo).toList(); - } - - @override - Future> loadCategoryWidgets({int categoryId = 0}) async { - List> rawData = - await categoryDao.loadCollectWidgets(categoryId); - List widgets = rawData.map((e) => WidgetPo.fromJson(e)).toList(); - return widgets.map(WidgetModel.fromPo).toList(); - } - - @override - Future toggleCategory(int categoryId, int widgetId) async { - return await categoryDao.toggleCollect(categoryId, widgetId); - } - - @override - Future> getCategoryByWidget(int widgetId) async { - return await categoryDao.categoryWidgetIds(widgetId); - } - - @override - Future updateCategory(CategoryPo categoryPo) async { - int success = await categoryDao.update(categoryPo); - return success != -1; - } - - @override - Future> loadCategoryData() async { - List> data = await categoryDao.queryAll(); - - Completer> completer = Completer(); - List collects = []; - - if (data.isEmpty) { - completer.complete([]); - } - - for (int i = 0; i < data.length; i++) { - List ids = await categoryDao.loadCollectWidgetIds(data[i]['id']); - collects.add(CategoryTo( - widgetIds: ids, - model: CategoryPo.fromJson(data[i]))); - - if (i == data.length - 1) { - completer.complete(collects); - } - } - - return completer.future; - } - - @override - Future syncCategoryByData(String data,String likeData) async { - try { - await categoryDao.clear(); - List dataMap = json.decode(data); - for (int i = 0; i < dataMap.length; i++) { - CategoryPo po = CategoryPo.fromNetJson(dataMap[i]["model"]); - List widgetIds = dataMap[i]["widgetIds"]; - await addCategory(po); - if (widgetIds.isNotEmpty&&po.id!=null) { - await categoryDao.addWidgets(po.id!, widgetIds); - } - } - List likeWidgets = (json.decode(likeData) as List).map((e) => e).toList(); - for (int i = 0; i < likeWidgets.length; i++) { - await likeDao.like(likeWidgets[i]); - } - return true; - } catch (e) { - print(e); - return false; - } - } -} diff --git a/packages/widget_repository/lib/src/db_impl/db_impl.dart b/packages/widget_repository/lib/src/db_impl/db_impl.dart deleted file mode 100644 index be0f2b859..000000000 --- a/packages/widget_repository/lib/src/db_impl/db_impl.dart +++ /dev/null @@ -1,4 +0,0 @@ - -export 'widget_db_repository.dart'; -export 'catagory_db_repository.dart'; -export 'node_db_repository.dart'; \ No newline at end of file diff --git a/packages/widget_repository/lib/src/db_impl/node_db_repository.dart b/packages/widget_repository/lib/src/db_impl/node_db_repository.dart deleted file mode 100644 index f9f6f861e..000000000 --- a/packages/widget_repository/lib/src/db_impl/node_db_repository.dart +++ /dev/null @@ -1,21 +0,0 @@ - -import 'package:storage/storage.dart'; - -import 'package:widget_repository/src/model/node_model.dart'; - -import '../node_repository.dart'; - -class NodeDbRepository implements NodeRepository{ - - const NodeDbRepository(); - - NodeDao get nodeDao => FlutterDbStorage.instance.nodeDao; - - @override - Future> loadNode(int widgetId) async{ - List> data = await nodeDao.queryById(widgetId); - List nodes = data.map((e) => NodeModel.fromJson(e)).toList(); - return nodes; - } - -} \ No newline at end of file diff --git a/packages/widget_repository/lib/src/db_impl/widget_db_repository.dart b/packages/widget_repository/lib/src/db_impl/widget_db_repository.dart deleted file mode 100644 index 613734ba3..000000000 --- a/packages/widget_repository/lib/src/db_impl/widget_db_repository.dart +++ /dev/null @@ -1,55 +0,0 @@ -import 'package:widget_repository/src/model/model.dart'; -import 'package:widget_repository/src/widget_repository.dart'; -import 'package:storage/storage.dart'; - - - -/// create by 张风捷特烈 on 2020-03-03 -/// contact me by email 1981462002@qq.com -/// 说明 : Widget数据仓库 - -class WidgetDbRepository implements WidgetRepository { - - const WidgetDbRepository(); - - WidgetDao get widgetDao => FlutterDbStorage.instance.widgetDao; - LikeDao get likeDao => FlutterDbStorage.instance.likeDao; - - @override - Future> loadLikeWidgets() async { - List likeIds = await likeDao.likeWidgetIds(); - List> data = await widgetDao.queryByIds(likeIds); - List widgets = data.map((e) => WidgetPo.fromJson(e)).toList(); - return widgets.map(WidgetModel.fromPo).toList(); - } - - @override - Future> searchWidgets(WidgetFilter args) async { - List> data = await widgetDao.search(args); - List widgets = data.map((e) => WidgetPo.fromJson(e)).toList(); - return widgets.map(WidgetModel.fromPo).toList(); - } - - @override - Future> loadWidget(List id) async { - List> data = await widgetDao.queryByIds(id); - List widgets = data.map((e) => WidgetPo.fromJson(e)).toList(); - if (widgets.isNotEmpty) return widgets.map(WidgetModel.fromPo).toList(); - return []; - } - - @override - Future toggleLike( - int id, - ) { - return likeDao.toggleCollect(id); - } - - @override - Future collected(int id) async { - return await likeDao.like(id); - } - - @override - Future total(WidgetFilter args) => widgetDao.total(args); -} diff --git a/packages/widget_repository/lib/src/model/enums.dart b/packages/widget_repository/lib/src/model/enums.dart deleted file mode 100644 index 37a33388c..000000000 --- a/packages/widget_repository/lib/src/model/enums.dart +++ /dev/null @@ -1,13 +0,0 @@ -/// create by 张风捷特烈 on 2020-03-07 -/// contact me by email 1981462002@qq.com -/// 说明: - -enum WidgetFamily { - statelessWidget, - statefulWidget, - singleChildRenderObjectWidget, - multiChildRenderObjectWidget, - sliver, - proxyWidget, - other, -} diff --git a/packages/widget_repository/lib/src/model/model.dart b/packages/widget_repository/lib/src/model/model.dart deleted file mode 100644 index f79c5c9d5..000000000 --- a/packages/widget_repository/lib/src/model/model.dart +++ /dev/null @@ -1,5 +0,0 @@ -export 'node_model.dart'; -export 'widget_model.dart'; -export 'category_model.dart'; -export 'enums.dart'; -export 'widget_filter.dart'; \ No newline at end of file diff --git a/packages/widget_repository/lib/src/model/node_model.dart b/packages/widget_repository/lib/src/model/node_model.dart deleted file mode 100644 index 15b89f6d0..000000000 --- a/packages/widget_repository/lib/src/model/node_model.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:equatable/equatable.dart'; - -/// create by 张风捷特烈 on 2020-03-04 -/// contact me by email 1981462002@qq.com -/// 说明: 详情页节点-展示-数据模型 - -class NodeModel extends Equatable { - final String name; - final String subtitle; - final String code; - - const NodeModel({ - required this.name, - required this.subtitle, - required this.code, - }); - - @override - List get props => [name, subtitle, code]; - - factory NodeModel.fromJson(Map map) { - return NodeModel( - name: map['name'], subtitle: map["subtitle"], code: map["code"]); - } - - @override - String toString() { - return 'Node{name: $name, subtitle: $subtitle, code: $code}'; - } -} diff --git a/packages/widget_repository/lib/src/model/widget_model.dart b/packages/widget_repository/lib/src/model/widget_model.dart deleted file mode 100644 index a35dac947..000000000 --- a/packages/widget_repository/lib/src/model/widget_model.dart +++ /dev/null @@ -1,110 +0,0 @@ -import 'package:equatable/equatable.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:storage/storage.dart'; -import 'package:widget_repository/widget_repository.dart'; - - -/// create by 张风捷特烈 on 2020-03-04 -/// contact me by email 1981462002@qq.com -/// 说明: 组件信息-展示-数据模型 -/// - -const List kTabColors = [ - Color(0xff44D1FD), - Color(0xffFD4F43), - Color(0xffB375FF), - Color(0xFF4CAF50), - Color(0xFFFF9800), - Color(0xFF00F1F1), - Color(0xFFDBD83F), -]; - -class WidgetModel extends Equatable { - final int id; - final String name; - final String nameCN; - final WidgetFamily family; - final bool deprecated; - final bool death; - final List links; - final double lever; - final ImageProvider? image; - final String info; - - - const WidgetModel( - { - required this.id, - required this.name, - required this.nameCN, - required this.family, - this.deprecated =false, - this.death =false, - required this.links, - // required this.type, - required this.lever, - this.image, - required this.info}); - - @override - List get props => [id]; - - Color get color => kTabColors[family.index]; - - static WidgetModel fromPo(WidgetPo po) { - return WidgetModel( - id: po.id, - name: po.name, - nameCN: po.nameCN, - family: toFamily(po.family), - image: convertImage(po.name), - lever: po.lever, - deprecated: po.deprecated == 1, - death: po.deprecated == -1, - info: po.info, - links: formatLinkTo(po.linkWidget), - ); - } - - static convertImage(String name) { - // return image.isEmpty ? null : AssetImage(image); - return null; - } - - - @override - String toString() { - return 'WidgetModel{id: $id, name: $name, nameCN: $nameCN, family: $family, deprecated: $deprecated, links: $links, lever: $lever, image: $image, info: $info}'; - } - - static List formatLinkTo(String links) { - if(links.isEmpty){ - return []; - } - if(!links.contains(',')){ - return [int.parse(links)]; - } - return links.split(',').map((e)=>int.parse(e)).toList(); - } - - static WidgetFamily toFamily(int id) { - switch (id) { - case 0: - return WidgetFamily.statelessWidget; - case 1: - return WidgetFamily.statefulWidget; - case 2: - return WidgetFamily.singleChildRenderObjectWidget; - case 3: - return WidgetFamily.multiChildRenderObjectWidget; - case 4: - return WidgetFamily.sliver; - case 5: - return WidgetFamily.proxyWidget; - case 6: - return WidgetFamily.other; - default: - return WidgetFamily.statelessWidget; - } - } -} diff --git a/packages/widget_repository/lib/src/node_repository.dart b/packages/widget_repository/lib/src/node_repository.dart deleted file mode 100644 index 217899882..000000000 --- a/packages/widget_repository/lib/src/node_repository.dart +++ /dev/null @@ -1,12 +0,0 @@ - -import 'model/model.dart'; - -abstract class NodeRepository{ - - Future> loadNode(int widgetId); - -} - - - - diff --git a/packages/widget_repository/lib/src/widget_repository.dart b/packages/widget_repository/lib/src/widget_repository.dart deleted file mode 100644 index e17aa1606..000000000 --- a/packages/widget_repository/lib/src/widget_repository.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'model/model.dart'; - -/// create by 张风捷特烈 on 2020-03-03 -/// contact me by email 1981462002@qq.com - -abstract class WidgetRepository { - // Future> loadWidgets(WidgetFamily family); - - /// - Future> loadWidget(List ids); - - /// 根据 [WidgetFilter] 搜索 [WidgetModel] 列表 - Future> searchWidgets(WidgetFilter args); - - Future toggleLike(int id); - - Future total(WidgetFilter args); - - Future> loadLikeWidgets(); - - Future collected(int id); -} diff --git a/packages/widget_repository/lib/widget_repository.dart b/packages/widget_repository/lib/widget_repository.dart deleted file mode 100644 index 8913dcc30..000000000 --- a/packages/widget_repository/lib/widget_repository.dart +++ /dev/null @@ -1,7 +0,0 @@ -library widget_repository; - -export 'src/widget_repository.dart'; -export 'src/category_repository.dart'; -export 'src/node_repository.dart'; -export 'src/model/model.dart'; -export 'src/db_impl/db_impl.dart'; \ No newline at end of file diff --git a/packages/widget_repository/pubspec.yaml b/packages/widget_repository/pubspec.yaml deleted file mode 100644 index 16ad71a1a..000000000 --- a/packages/widget_repository/pubspec.yaml +++ /dev/null @@ -1,61 +0,0 @@ -name: widget_repository -description: A new Flutter project. -version: 0.0.1 -publish_to: none -homepage: - -environment: - sdk: '>=2.18.1 <3.0.0' - flutter: ">=1.17.0" - -dependencies: - flutter: - sdk: flutter - equatable: ^2.0.5 - intl: ^0.18.0 - utils: - path: ../utils - storage: - path: ../storage - -dev_dependencies: - flutter_test: - sdk: flutter - flutter_lints: ^2.0.0 - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter packages. -flutter: - - # To add assets to your package, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - # - # For details regarding assets in packages, see - # https://flutter.dev/assets-and-images/#from-packages - # - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware - - # To add custom fonts to your package, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts in packages, see - # https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/widget_repository/test/widget_repository_test.dart b/packages/widget_repository/test/widget_repository_test.dart deleted file mode 100644 index a5eb09003..000000000 --- a/packages/widget_repository/test/widget_repository_test.dart +++ /dev/null @@ -1,4 +0,0 @@ - -void main() { - -} diff --git a/packages/widgets/.gitignore b/packages/widgets/.gitignore deleted file mode 100644 index 96486fd93..000000000 --- a/packages/widgets/.gitignore +++ /dev/null @@ -1,30 +0,0 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.buildlog/ -.history -.svn/ -migrate_working_dir/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ - -# Flutter/Dart/Pub related -# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. -/pubspec.lock -**/doc/api/ -.dart_tool/ -.packages -build/ diff --git a/packages/widgets/.metadata b/packages/widgets/.metadata deleted file mode 100644 index 541c2f65c..000000000 --- a/packages/widgets/.metadata +++ /dev/null @@ -1,10 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled and should not be manually edited. - -version: - revision: 135454af32477f815a7525073027a3ff9eff1bfd - channel: stable - -project_type: package diff --git a/packages/widgets/lib/MultiChildRenderObjectWidget/Column/node1_base.dart b/packages/widgets/lib/MultiChildRenderObjectWidget/Column/node1_base.dart deleted file mode 100644 index d35167f98..000000000 --- a/packages/widgets/lib/MultiChildRenderObjectWidget/Column/node1_base.dart +++ /dev/null @@ -1,69 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-03-30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 96, -// "name": 'Column基本使用', -// "priority": 1, -// "subtitle": -// "【children】 : 组件列表 【List】\n" -// "【mainAxisAlignment】 : 主轴对齐 【MainAxisAlignment】\n" -// "【crossAxisAlignment】 : 交叉轴对齐 【CrossAxisAlignment】\n" -// "【textBaseline】 : 文字基线 【TextBaseline】\n" -// "【verticalDirection】 : 竖直方向 【VerticalDirection】\n" -// "【mainAxisSize】 : 主轴尺寸 【MainAxisSize】", -// } - -class CustomColumn extends StatelessWidget { - const CustomColumn({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Column( - children: [ - _buildTitle(), - _buildContent(context), - ], - ); - } - - Widget _buildTitle() { - return Container( - height: 70, - color: const Color(0x4484FFFF), - child: Row( - children: const [ - Padding( - child: Icon( - Icons.add_location, - size: 30, - color: Colors.pink, - ), - padding: EdgeInsets.only(left: 25, right: 20), - ), - Expanded( - child: Text( - "附近", - style: TextStyle(fontSize: 18), - ), - ), - Padding( - child: Icon(Icons.keyboard_arrow_right, color: Colors.black38), - padding: EdgeInsets.only(right: 25), - ), - ], - )); - } - - Widget _buildContent(ctx) => Container( - width: MediaQuery.of(ctx).size.width, - color: Colors.orangeAccent, - height: 100, - child: const Icon( - Icons.android, - size: 50, - color: Colors.white, - ), - ); -} diff --git a/packages/widgets/lib/MultiChildRenderObjectWidget/CustomMultiChildLayout/node1_base.dart b/packages/widgets/lib/MultiChildRenderObjectWidget/CustomMultiChildLayout/node1_base.dart deleted file mode 100644 index 04ac34264..000000000 --- a/packages/widgets/lib/MultiChildRenderObjectWidget/CustomMultiChildLayout/node1_base.dart +++ /dev/null @@ -1,94 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/6/6 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 341, -// "name": 'CustomMultiChildLayout基本使用', -// "priority": 1, -// "subtitle": -// "【children】 : 子组件集 【List】\n" -// "【delegate】 : 布局代理 【MultiChildLayoutDelegate】", -// } - - -class CustomMultiChildLayoutDemo extends StatelessWidget { - const CustomMultiChildLayoutDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - width: 300, - height: 150, - color: Colors.grey.withAlpha(33), - child: CustomMultiChildLayout( - delegate: CornerCustomMultiChildLayout( - padding:const EdgeInsets.only(left: 10,top: 5,right: 10,bottom: 5), - ), - children: [ - LayoutId(id: CornerType.topLeft, child: const Box50(Colors.red)), - LayoutId(id: CornerType.topRight, child: const Box50(Colors.yellow)), - LayoutId(id: CornerType.bottomLeft, child: const Box50(Colors.blue)), - LayoutId(id: CornerType.bottomRight, child: const Box50(Colors.green)), - ], - ), - ); - } -} - -// 50 颜色盒 -class Box50 extends StatelessWidget { - final Color color; - const Box50(this.color, {Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - width: 50, - height: 50, - color: color, - ); - } -} - - -enum CornerType{ - topLeft, - topRight, - bottomLeft, - bottomRight -} - - -class CornerCustomMultiChildLayout extends MultiChildLayoutDelegate{ - final EdgeInsets padding; - - CornerCustomMultiChildLayout({this.padding = EdgeInsets.zero}); - - @override - void performLayout(Size size) { - if (hasChild(CornerType.topLeft)) { - layoutChild(CornerType.topLeft, BoxConstraints.loose(size)); - positionChild(CornerType.topLeft, Offset.zero.translate(padding.left, padding.top)); - } - if (hasChild(CornerType.topRight)) { - var childSize = layoutChild(CornerType.topRight, BoxConstraints.loose(size)); - positionChild(CornerType.topRight, Offset(size.width-childSize.width,0).translate(-padding.right, padding.top)); - } - if (hasChild(CornerType.bottomLeft)) { - var childSize = layoutChild(CornerType.bottomLeft, BoxConstraints.loose(size)); - positionChild(CornerType.bottomLeft, Offset(0,size.height-childSize.height).translate(padding.left, -padding.bottom)); - } - if (hasChild(CornerType.bottomRight)) { - var childSize = layoutChild(CornerType.bottomRight, BoxConstraints.loose(size)); - positionChild(CornerType.bottomRight, Offset(size.width-childSize.width,size.height-childSize.height).translate(-padding.right, -padding.bottom)); - } - } - - @override - bool shouldRelayout(CornerCustomMultiChildLayout oldDelegate) => oldDelegate.padding!=padding; - -} - diff --git a/packages/widgets/lib/MultiChildRenderObjectWidget/Flex/node1_direction.dart b/packages/widgets/lib/MultiChildRenderObjectWidget/Flex/node1_direction.dart deleted file mode 100644 index b740fea96..000000000 --- a/packages/widgets/lib/MultiChildRenderObjectWidget/Flex/node1_direction.dart +++ /dev/null @@ -1,67 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 94, -// "name": 'Flex的排布方向', -// "priority": 1, -// "subtitle": -// "【children】 : 组件列表 【List】\n" -// "【direction】 : 方向 【Axis】", -// } -class DirectionFlex extends StatelessWidget { - DirectionFlex({Key? key}) : super(key: key); - - static TextStyle textStyle = - const TextStyle(color: Colors.white, fontWeight: FontWeight.bold); - - final Widget blueBox = Container( - alignment: Alignment.center, - color: Colors.blue, - height: 20, - width: 30, - child: Text('1', style: textStyle), - ); - - final Widget redBox = Container( - alignment: Alignment.center, - color: Colors.red, - height: 30, - width: 40, - child: Text('2', style: textStyle), - ); - - final Widget greenBox = Container( - alignment: Alignment.center, - color: Colors.green, - height: 20, - width: 20, - child: Text('3', style: textStyle), - ); - - @override - Widget build(BuildContext context) { - return Wrap( - children: Axis.values - .map((mode) => Column(children: [ - Container( - margin: const EdgeInsets.all(5), - width: 160, - height: 80, - color: Colors.grey.withAlpha(33), - child: _buildItem(mode)), - Text(mode.toString().split('.')[1]) - ])) - .toList()); - } - - Widget _buildItem(mode) => Flex( - direction: mode, - children: [ - blueBox, redBox, greenBox - ], - ); -} \ No newline at end of file diff --git a/packages/widgets/lib/MultiChildRenderObjectWidget/Flex/node2_mainAxisAlignment.dart b/packages/widgets/lib/MultiChildRenderObjectWidget/Flex/node2_mainAxisAlignment.dart deleted file mode 100644 index 15f2a22be..000000000 --- a/packages/widgets/lib/MultiChildRenderObjectWidget/Flex/node2_mainAxisAlignment.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 94, -// "name": 'Flex主轴对齐方式', -// "priority": 2, -// "subtitle": -// "【mainAxisAlignment】 : 主轴对齐 【MainAxisAlignment】", -// } -class MainAxisAlignmentFlex extends StatelessWidget { - MainAxisAlignmentFlex({Key? key}) : super(key: key); - - static TextStyle textStyle = - const TextStyle(color: Colors.white, fontWeight: FontWeight.bold); - - final Widget blueBox = Container( - alignment: Alignment.center, - color: Colors.blue, - height: 20, - width: 30, - child: Text('1', style: textStyle), - ); - - final Widget redBox = Container( - alignment: Alignment.center, - color: Colors.red, - height: 30, - width: 40, - child: Text('2', style: textStyle), - ); - - final Widget greenBox = Container( - alignment: Alignment.center, - color: Colors.green, - height: 20, - width: 20, - child: Text('3', style: textStyle), - ); - - @override - Widget build(BuildContext context) { - return Wrap( - runSpacing: 5, - children: MainAxisAlignment.values - .map((mode) => Column(children: [ - Container( - margin: const EdgeInsets.all(5), - width: 160, - height: 80, - color: Colors.grey.withAlpha(33), - child: _buildItem(mode)), - Text(mode.toString().split('.')[1]) - ])) - .toList()); - } - - Widget _buildItem(mode) => Flex( - direction: Axis.horizontal, - mainAxisAlignment: mode, - children: [ - blueBox, redBox, greenBox - ], - ); -} \ No newline at end of file diff --git a/packages/widgets/lib/MultiChildRenderObjectWidget/Flex/node3_crossAxisAlignment.dart b/packages/widgets/lib/MultiChildRenderObjectWidget/Flex/node3_crossAxisAlignment.dart deleted file mode 100644 index 9071792bc..000000000 --- a/packages/widgets/lib/MultiChildRenderObjectWidget/Flex/node3_crossAxisAlignment.dart +++ /dev/null @@ -1,79 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 94, -// "name": 'Flex交叉轴对齐方式', -// "priority": 3, -// "subtitle": -// "【crossAxisAlignment】 : 交叉轴对齐 【CrossAxisAlignment】", -// } -class CrossAxisAlignmentFlex extends StatelessWidget { - CrossAxisAlignmentFlex({Key? key}) : super(key: key); - - static TextStyle textStyle = - const TextStyle(color: Colors.white, fontWeight: FontWeight.bold); - - final Widget blueBox = Container( - alignment: Alignment.center, - color: Colors.blue, - height: 20, - width: 30, - child: Text( - '1', - style: textStyle - ), - ); - - final Widget redBox = Container( - alignment: Alignment.center, - color: Colors.red, - height: 30, - width: 40, - child: Text( - '2', - style: textStyle - ), - ); - - final Widget greenBox = Container( - alignment: Alignment.center, - color: Colors.green, - height: 20, - width: 20, - child: Text( - '3', - style: textStyle - ), - ); - - - @override - Widget build(BuildContext context) { - return Wrap( - runSpacing: 5, - children: CrossAxisAlignment.values - .map((mode) => Column(children: [ - Container( - margin: const EdgeInsets.all(5), - width: 160, - height: 80, - color: Colors.grey.withAlpha(33), - child: _buildItem(mode)), - Text(mode.toString().split('.')[1]) - ])) - .toList()); - } - - Widget _buildItem(mode) => Flex( - direction: Axis.horizontal, - crossAxisAlignment: mode, - textBaseline: TextBaseline.alphabetic, - children: [ - blueBox, redBox, greenBox - ], - ); -} \ No newline at end of file diff --git a/packages/widgets/lib/MultiChildRenderObjectWidget/Flex/node4_verticalDirection.dart b/packages/widgets/lib/MultiChildRenderObjectWidget/Flex/node4_verticalDirection.dart deleted file mode 100644 index e3b2c6fe5..000000000 --- a/packages/widgets/lib/MultiChildRenderObjectWidget/Flex/node4_verticalDirection.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 94, -// "name": 'Flex垂直方向顺序', -// "priority": 4, -// "subtitle": -// "【verticalDirection】 : 垂直方向顺序 【VerticalDirection】", -// } -class VerticalDirectionFlex extends StatelessWidget { - VerticalDirectionFlex({Key? key}) : super(key: key); - - static TextStyle textStyle = - const TextStyle(color: Colors.white, fontWeight: FontWeight.bold); - - final Widget blueBox = Container( - alignment: Alignment.center, - color: Colors.blue, - height: 20, - width: 30, - child: Text('1', style: textStyle), - ); - - final Widget redBox = Container( - alignment: Alignment.center, - color: Colors.red, - height: 30, - width: 40, - child: Text('2', style: textStyle), - ); - - final Widget greenBox = Container( - alignment: Alignment.center, - color: Colors.green, - height: 20, - width: 20, - child: Text('3', style: textStyle), - ); - - @override - Widget build(BuildContext context) { - return Wrap( - runSpacing: 5, - children: VerticalDirection.values - .map((mode) => Column(children: [ - Container( - margin: const EdgeInsets.all(5), - width: 160, - height: 80, - color: Colors.grey.withAlpha(33), - child: _buildItem(mode)), - Text(mode.toString().split('.')[1]) - ])) - .toList()); - } - - Widget _buildItem(mode) => Flex( - direction: Axis.vertical, - verticalDirection: mode, - children: [blueBox, redBox, greenBox], - ); -} diff --git a/packages/widgets/lib/MultiChildRenderObjectWidget/Flex/node5_textDirection.dart b/packages/widgets/lib/MultiChildRenderObjectWidget/Flex/node5_textDirection.dart deleted file mode 100644 index e0abf3fb9..000000000 --- a/packages/widgets/lib/MultiChildRenderObjectWidget/Flex/node5_textDirection.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 94, -// "name": 'Flex水平方向顺序', -// "priority": 5, -// "subtitle": -// "【textDirection】 : 水平方向顺序 【TextDirection】", -// } -class TextDirectionFlex extends StatelessWidget { - TextDirectionFlex({Key? key}) : super(key: key); - - static TextStyle textStyle = - const TextStyle(color: Colors.white, fontWeight: FontWeight.bold); - - final Widget blueBox = Container( - alignment: Alignment.center, - color: Colors.blue, - height: 20, - width: 30, - child: Text('1', style: textStyle), - ); - - final Widget redBox = Container( - alignment: Alignment.center, - color: Colors.red, - height: 30, - width: 40, - child: Text('2', style: textStyle), - ); - - final Widget greenBox = Container( - alignment: Alignment.center, - color: Colors.green, - height: 20, - width: 20, - child: Text('3', style: textStyle), - ); - - @override - Widget build(BuildContext context) { - return Wrap( - runSpacing: 5, - children: TextDirection.values - .map((mode) => Column(children: [ - Container( - margin: const EdgeInsets.all(5), - width: 160, - height: 80, - color: Colors.grey.withAlpha(33), - child: _buildItem(mode)), - Text(mode.toString().split('.')[1]) - ])) - .toList()); - } - - Widget _buildItem(mode) => Flex( - direction: Axis.horizontal, - textDirection: mode, - children: [blueBox, redBox, greenBox], - ); -} \ No newline at end of file diff --git a/packages/widgets/lib/MultiChildRenderObjectWidget/Flex/zz_node_play.dart b/packages/widgets/lib/MultiChildRenderObjectWidget/Flex/zz_node_play.dart deleted file mode 100644 index 1d70d95a2..000000000 --- a/packages/widgets/lib/MultiChildRenderObjectWidget/Flex/zz_node_play.dart +++ /dev/null @@ -1,188 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: - -class PlayFlex extends StatefulWidget { - const PlayFlex({Key? key}) : super(key: key); - - @override - _PlayFlexState createState() => _PlayFlexState(); -} - -class _PlayFlexState extends State { - final Widget redBox = Container( - color: Colors.red, - height: 50, - width: 50, - ); - final Widget blueBox = Container( - color: Colors.blue, - width: 60, - height: 60, - ); - final Widget yellowBox = Container( - color: Colors.yellow, - height: 10, - width: 10, - ); - final Widget greenBox = Container( - color: Colors.green, - height: 30, - width: 20, - ); - Axis _direction = Axis.horizontal; - MainAxisAlignment _mainAxisAlignment = MainAxisAlignment.start; - CrossAxisAlignment _crossAxisAlignment = CrossAxisAlignment.center; - VerticalDirection _verticalDirection = VerticalDirection.up; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - _buildDirectionSelector(), - _buildMainAxisAlignmentSelector(), - _buildCrossAxisAlignmentSelector(), - _buildVerticalDirectionSelector(), - Container( - width: 300, - height: 300 * 0.618, - color: Colors.grey.withAlpha(33), - child: Flex( - textBaseline: TextBaseline.alphabetic, - direction: _direction, - mainAxisAlignment: _mainAxisAlignment, - crossAxisAlignment: _crossAxisAlignment, - verticalDirection: _verticalDirection, - children: [redBox, blueBox, yellowBox, greenBox], - ), - ), - ], - ); - } - - Widget _buildDirectionSelector() { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 10), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text( - "direction", - style: TextStyle( - fontSize: 16, color: Colors.blue, fontWeight: FontWeight.bold), - ), - DropdownButton( - elevation: 1, - underline: Container(), - value: _direction, - items: Axis.values - .map((e) => DropdownMenuItem( - value: e, - child: Text(e.toString()), - )) - .toList(), - onChanged: (e) { - setState(() { - _direction = e??_direction; - }); - }), - ], - ), - ); - } - - Widget _buildMainAxisAlignmentSelector() { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 10), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text( - "mainAxisAlignment", - style: TextStyle( - fontSize: 16, color: Colors.blue, fontWeight: FontWeight.bold), - ), - DropdownButton( - elevation: 1, - underline: Container(), - value: _mainAxisAlignment, - items: MainAxisAlignment.values - .map((e) => DropdownMenuItem( - value: e, - child: Text(e.toString().split('.')[1]), - )) - .toList(), - onChanged: (e) { - setState(() { - _mainAxisAlignment = e??_mainAxisAlignment; - }); - }), - ], - ), - ); - } - - Widget _buildCrossAxisAlignmentSelector() { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 10), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text( - "crossAxisAlignment", - style: TextStyle( - fontSize: 16, color: Colors.blue, fontWeight: FontWeight.bold), - ), - DropdownButton( - elevation: 1, - underline: Container(), - value: _crossAxisAlignment, - items: CrossAxisAlignment.values - .map((e) => DropdownMenuItem( - value: e, - child: Text(e.toString().split('.')[1]), - )) - .toList(), - onChanged: (e) { - setState(() { - _crossAxisAlignment = e??_crossAxisAlignment; - }); - }), - ], - ), - ); - } - - Widget _buildVerticalDirectionSelector() { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 10), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text( - "MainAxisSize", - style: TextStyle( - fontSize: 16, color: Colors.blue, fontWeight: FontWeight.bold), - ), - DropdownButton( - elevation: 1, - underline: Container(), - value: _verticalDirection, - items: VerticalDirection.values - .map((e) => DropdownMenuItem( - value: e, - child: Text(e.toString().split('.')[1]), - )) - .toList(), - onChanged: (e) { - setState(() { - _verticalDirection = e??_verticalDirection; - }); - }), - ], - ), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/MultiChildRenderObjectWidget/Flow/node1_base.dart b/packages/widgets/lib/MultiChildRenderObjectWidget/Flow/node1_base.dart deleted file mode 100644 index 585c2636b..000000000 --- a/packages/widgets/lib/MultiChildRenderObjectWidget/Flow/node1_base.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 99, -// "name": 'Flow圆形排布', -// "priority": 1, -// "subtitle": "【children】 : 组件列表 【List】\n" -// "【delegate】 : 代理 【FlowDelegate】", -// } -class CircleFlow extends StatelessWidget { - CircleFlow({Key? key}) : super(key: key); - - final List data = List.generate( - 16, - (index) => index.isEven - ? "assets/images/icon_head.webp" - : "assets/images/wy_300x200.webp"); - - @override - Widget build(BuildContext context) { - return Container( - width: 300, - height: 300, - alignment: Alignment.center, - child: Flow( - delegate: _CircleFlowDelegate(), - children: data - .map((e) => CircleAvatar(backgroundImage: AssetImage(e))) - .toList(), - ), - ); - } -} - -class _CircleFlowDelegate extends FlowDelegate { - - @override //绘制孩子的方法 - void paintChildren(FlowPaintingContext context) { - final double radius = context.size.shortestSide / 2; - final int count = context.childCount; - final double perRad = 2 * pi / count; - for (int i = 0; i < count; i++) { - final Size size = context.getChildSize(i) ?? Size.zero; - final double offsetX = - (radius - size.width / 2) * cos(i * perRad) + radius; - final double offsetY = - (radius - size.height / 2) * sin(i * perRad) + radius; - context.paintChild(i, - transform: Matrix4.translationValues( - offsetX - size.width / 2, offsetY - size.height / 2, 0.0)); - } - } - - @override - bool shouldRepaint(FlowDelegate oldDelegate) => false; -} diff --git a/packages/widgets/lib/MultiChildRenderObjectWidget/Flow/node2_anim.dart b/packages/widgets/lib/MultiChildRenderObjectWidget/Flow/node2_anim.dart deleted file mode 100644 index 3ed205eee..000000000 --- a/packages/widgets/lib/MultiChildRenderObjectWidget/Flow/node2_anim.dart +++ /dev/null @@ -1,115 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-03-30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 99, -// "name": 'Flow圆形与动画结合', -// "priority": 2, -// "subtitle": -// "通过动画来更改周围组件的位置实现效果", -// } - -class BurstFlow extends StatefulWidget { - static final data = List.generate( - 16, - (index) => index.isEven - ? "assets/images/icon_head.webp" - : "assets/images/wy_300x200.webp"); - static final show = Container( - width: 300, - height: 300, - alignment: Alignment.center, - child: BurstFlow( - children: data - .map((e) => CircleAvatar(backgroundImage: AssetImage(e))) - .toList(), - menu: const CircleAvatar( - backgroundImage: AssetImage('assets/images/icon_head.webp'), - ))); - - final List children; - final Widget menu; - - const BurstFlow({Key? key, required this.children, required this.menu}) : super(key: key); - - @override - _BurstFlowState createState() => _BurstFlowState(); -} - -class _BurstFlowState extends State - with SingleTickerProviderStateMixin { - late AnimationController _controller; - double _rad = 0.0; - bool _closed = true; - - @override - void initState() { - _controller = AnimationController( - duration: const Duration(milliseconds: 1000), - vsync: this, - ) - ..addListener(() => setState( - () => _rad = (_closed ? (_controller.value) : 1 - _controller.value))) - ..addStatusListener((status) { - if (status == AnimationStatus.completed) { - _closed = !_closed; - } - }); - super.initState(); - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Flow( - delegate: _BurstFlowDelegate(_rad), - children: [ - ...widget.children, - InkWell( - onTap: () { - _controller.reset(); - _controller.forward(); - }, - child: widget.menu) - ], - ); - } -} - -class _BurstFlowDelegate extends FlowDelegate { - final double rad; - - _BurstFlowDelegate(this.rad); - - @override //绘制孩子的方法 - void paintChildren(FlowPaintingContext context) { - double radius = context.size.shortestSide / 2; - final int count = context.childCount - 1; - final double perRad = 2 * pi / count; - for (int i = 0; i < count; i++) { - Size size = context.getChildSize(i) ?? Size.zero; - final double offsetX = rad * (radius - size.width/2) * cos(i * perRad) + radius; - final double offsetY = rad * (radius - size.height/2) * sin(i * perRad) + radius; - context.paintChild(i, - transform: Matrix4.translationValues( - offsetX - size.width/2, offsetY - size.height/2, 0.0)); - } - - Size size = context.getChildSize(context.childCount - 1) ?? Size.zero; - - context.paintChild(context.childCount - 1, - transform: Matrix4.translationValues( - radius - size.width / 2, radius - size.height / 2, 0.0)); - } - - @override - bool shouldRepaint(_BurstFlowDelegate oldDelegate) => oldDelegate.rad != rad; -} diff --git a/packages/widgets/lib/MultiChildRenderObjectWidget/IndexedStack/node1_base.dart b/packages/widgets/lib/MultiChildRenderObjectWidget/IndexedStack/node1_base.dart deleted file mode 100644 index 0ce56b2fe..000000000 --- a/packages/widgets/lib/MultiChildRenderObjectWidget/IndexedStack/node1_base.dart +++ /dev/null @@ -1,63 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-26 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 161, -// "name": 'IndexedStack基本使用', -// "priority":1 , -// "subtitle": -// "【children】 : 子组件列表 【Lis】\n" -// "【alignment】 : 对齐方式 【AlignmentGeometry】\n" -// "【index】 : 当前显示组件 【int】", -// } -class CustomIndexedStack extends StatefulWidget { - const CustomIndexedStack({Key? key}) : super(key: key); - - @override - _CustomIndexedStackState createState() => _CustomIndexedStackState(); -} - -class _CustomIndexedStackState extends State { - int _index = 1; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - _buildSwitch(), - Container( - width: 200, - height: 100, - color: Colors.grey.withAlpha(33), - child: IndexedStack( - index: _index, - children: [ - Container( - color: Colors.red, - width: 80, - height: 80 - ), - Positioned( - bottom: 10, - right: 10, - child: Container( - color: Colors.blue, - width: 80, - height: 80, - ), - ) - ], - ), - ), - ], - ); - } - - Widget _buildSwitch() => Switch( - value: _index == 0, - onChanged: (v) => setState(() => _index = v ? 0 : 1), - ); -} diff --git a/packages/widgets/lib/MultiChildRenderObjectWidget/ListBody/node1_base.dart b/packages/widgets/lib/MultiChildRenderObjectWidget/ListBody/node1_base.dart deleted file mode 100644 index ef5cda55d..000000000 --- a/packages/widgets/lib/MultiChildRenderObjectWidget/ListBody/node1_base.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/8/2 -/// contact me by email 1981462002@qq.com -/// 说明: 342 ListBody 列表体 将若干子组件按照轴向进行排列,可设置的属性很少,一般很少使用,而选择使用ListVIew。 - -// { -// "widgetId": 342, -// "name": "ListView的基本使用", -// "priority": 1, -// "subtitle": "【mainAxis】 : 主轴方向 【Axis】\n" -// "【reverse】: 是否反向 【bool】\n" -// "【children】: 子组件集 【List】", -// } - -class ListBodyDemo extends StatelessWidget { - const ListBodyDemo({Key? key}) : super(key: key); - - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 300, - child: ListView( - children: [ - ListBody( - mainAxis: Axis.vertical, - reverse: false, - children: [ - Container(color: Colors.red, height: 50.0,), - Container(color: Colors.orange, height: 50.0,), - Container(color: Colors.yellow, height: 50.0,), - ], - ), - Container(color: Colors.green, height: 80.0,), - ListBody( - mainAxis: Axis.vertical, - reverse: false, - children: [ - Container(color: Colors.blue, height: 50.0,), - Container(color: Colors.indigo, height: 50.0,), - Container(color: Colors.purple, height: 50.0,), - ], - ) - ] - ), - ); - } -} diff --git a/packages/widgets/lib/MultiChildRenderObjectWidget/NestedScrollViewViewport/node1_base.dart b/packages/widgets/lib/MultiChildRenderObjectWidget/NestedScrollViewViewport/node1_base.dart deleted file mode 100644 index 9d98f3b54..000000000 --- a/packages/widgets/lib/MultiChildRenderObjectWidget/NestedScrollViewViewport/node1_base.dart +++ /dev/null @@ -1,42 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 344 NestedScrollViewViewport 嵌套滑动视口 -/// 在 NestedScrollView 中使用的视口,该视口持有 SliverOverlapAbsorberHandle,会在视口需要重新计算布局时通知它。例如,当滚动它时。 -/// -// { -// "widgetId": 344, -// "name": 'NestedScrollViewViewport 介绍', -// "priority": 1, -// "subtitle": -// "【offset】 : *偏移 【ViewportOffset】\n" -// "【handle】 : *处理器 【SliverOverlapAbsorberHandle】\n" -// "【axisDirection】 : 轴向 【AxisDirection】\n" -// "【crossAxisDirection】 : 交叉轴向 【AxisDirection】\n" -// "【slivers】 : 子组件 【List】\n" -// "【clipBehavior】 : 裁剪行为 【Clip】\n" -// "【anchor】 : 锚点 【double】", -// } - -class NestedScrollViewViewportDemo extends StatelessWidget { - const NestedScrollViewViewportDemo({Key? key}) : super(key: key); - - final String info = - 'NestedScrollViewViewport 在源码中只有一处使用:' - '_NestedScrollViewCustomScrollView 继承自 CustomScrollView,复写了 buildViewport 方法,返回 NestedScrollViewViewport 。' - '而 NestedScrollView 构建时使用了 _NestedScrollViewCustomScrollView,也就是 NestedScrollView 的视口依赖于 NestedScrollViewViewport。' - 'NestedScrollViewViewport 的特点是持有 SliverOverlapAbsorberHandle 类对象 handle,源码中该 handle 在 NestedScrollViewState 中初始化。' - '可通过上下文获取,用于 SliverOverlapAbsorber/SliverOverlapInjector 组件,使用详见相关组件。'; - - - @override - Widget build(BuildContext context) { - return Container( - color: Colors.blue.withOpacity(0.1), - padding: const EdgeInsets.all(10), - margin: const EdgeInsets.all(10), - child: Text(info), - ); - } -} diff --git a/packages/widgets/lib/MultiChildRenderObjectWidget/RichText/node1_base.dart b/packages/widgets/lib/MultiChildRenderObjectWidget/RichText/node1_base.dart deleted file mode 100644 index 6c5477770..000000000 --- a/packages/widgets/lib/MultiChildRenderObjectWidget/RichText/node1_base.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../../utils/color_utils.dart'; - -/// create by 张风捷特烈 on 2020-03-30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 101, -// "name": 'RichText基本使用', -// "priority": 1, -// "subtitle": -// "【text】 : 文字 【TextSpan】\n" -// " 其他属性与Text相同,详见之。", -// } -class CustomRichText extends StatelessWidget { - const CustomRichText({Key? key}) : super(key: key); - - final str = " 发光强度简称光强,国际单位是(坎德拉)简写cd。" - "1cd是指光源在指定方向的单位立体角内发出的光通量。" - "光源辐射是均匀时,则光强为I=F/Ω,Ω为立体角,单位为球面度(sr),F为光通量," - "单位是流明,对于点光源由I=F/4π 。光亮度是表示发光面明亮程度的," - "指发光表面在指定方向的发光强度与垂直且指定方向的发光面的面积之比," - "单位是坎德拉/平方米。对于一个漫散射面,尽管各个方向的光强和光通量不同," - "但各个方向的亮度都是相等的。电视机的荧光屏就是近似于这样的漫散射面," - "所以从各个方向上观看图像,都有相同的亮度感。"; - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.only( - left: 10.0, - right: 10, - ), - child: RichText( - text: TextSpan( - children: str - .split("") - .map((str) => TextSpan( - text: str, - style: TextStyle( - fontSize: 14, color: ColorUtils.randomColor()))) - .toList())), - ); - } -} diff --git a/packages/widgets/lib/MultiChildRenderObjectWidget/RichText/node2_widget.dart b/packages/widgets/lib/MultiChildRenderObjectWidget/RichText/node2_widget.dart deleted file mode 100644 index abe813491..000000000 --- a/packages/widgets/lib/MultiChildRenderObjectWidget/RichText/node2_widget.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-03-30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 101, -// "name": 'RichText包含其他组件', -// "priority": 2, -// "subtitle": -// "使用WidgetSpan来承载普通组件,作为RichText的内容", -// } -class RichTextWithWidget extends StatelessWidget { - const RichTextWithWidget({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return RichText( - text: TextSpan( - text: 'hello ', - style: const TextStyle(color: Colors.black, fontSize: 18), - children: [ - WidgetSpan( - child: Image.asset( - 'assets/images/icon_head.webp', - width: 30, - ), - alignment: PlaceholderAlignment.baseline, - baseline: TextBaseline.ideographic), - const TextSpan( - text: ' , welcome to ', - style: TextStyle(color: Colors.blue, fontSize: 18), - ), - const WidgetSpan( - child: FlutterLogo(), - alignment: PlaceholderAlignment.baseline, - baseline: TextBaseline.ideographic), - const TextSpan( - text: ' .\n', - ), - const TextSpan( - text: 'focus me on ', - style: TextStyle(color: Colors.orange, fontSize: 16), - ), - const TextSpan( - text: 'https://github.com/toly1994328', - style: TextStyle( - color: Colors.blue, - fontSize: 18, - decoration: TextDecoration.underline), - ), - const TextSpan( - text: ' .\n', - ), - ], - ), - ); - } -} diff --git a/packages/widgets/lib/MultiChildRenderObjectWidget/Row/node1_base.dart b/packages/widgets/lib/MultiChildRenderObjectWidget/Row/node1_base.dart deleted file mode 100644 index aeae44290..000000000 --- a/packages/widgets/lib/MultiChildRenderObjectWidget/Row/node1_base.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-03-30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 95, -// "name": 'Row基本使用', -// "priority": 1, -// "subtitle": -// "【children】 : 组件列表 【List】\n" -// "【mainAxisAlignment】 : 主轴对齐 【MainAxisAlignment】\n" -// "【crossAxisAlignment】 : 交叉轴对齐 【CrossAxisAlignment】\n" -// "【textBaseline】 : 文字基线 【TextBaseline】\n" -// "【verticalDirection】 : 竖直方向 【VerticalDirection】\n" -// "【mainAxisSize】 : 主轴尺寸 【MainAxisSize】", -// } - -class CustomRow extends StatelessWidget { - const CustomRow({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - height: 70, - color: const Color(0x4484FFFF), - child: Row( - children: const [ - Padding( - child: Icon( - Icons.add_location, - size: 30, - color: Colors.pink, - ), - padding: EdgeInsets.only(left: 25, right: 20), - ), - Expanded( - child: Text( - "附近", - style: TextStyle(fontSize: 18), - ), - ), - Padding( - child: Icon(Icons.keyboard_arrow_right, color: Colors.black38), - padding: EdgeInsets.only(right: 25), - ), - ], - )); - } -} diff --git a/packages/widgets/lib/MultiChildRenderObjectWidget/ShrinkWrappingViewport/node1_base.dart b/packages/widgets/lib/MultiChildRenderObjectWidget/ShrinkWrappingViewport/node1_base.dart deleted file mode 100644 index 0580f29e0..000000000 --- a/packages/widgets/lib/MultiChildRenderObjectWidget/ShrinkWrappingViewport/node1_base.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 343 ShrinkWrappingViewport 收缩包围视图 -/// 和 ScrollView 的 shrinkWrap 属性之间关联。ShrinkWrappingViewport 在主轴上调整自身的大小以适应它的子节点,在无边界约束的情况下使用。 -/// -// { -// "widgetId": 343, -// "name": 'NestedScrollViewViewport 介绍', -// "priority": 1, -// "subtitle": -// "【offset】 : *偏移 【ViewportOffset】\n" -// "【axisDirection】 : 轴向 【AxisDirection】\n" -// "【crossAxisDirection】 : 交叉轴向 【AxisDirection】\n" -// "【slivers】 : 子组件 【List】\n" -// "【clipBehavior】 : 裁剪行为 【Clip】", -// } - -class ShrinkWrappingViewportDemo extends StatelessWidget { - - const ShrinkWrappingViewportDemo({Key? key}) : super(key: key); - - final String info = - 'ShrinkWrappingViewport 在源码中只有一处使用:' - '在 ScrollView 中如果 shrinkWrap 为 true,会使用 ShrinkWrappingViewport,该属性在其子类 ListView、GridView、CustomScrollView 中可指定。' - '如果 shrinkWrap 为 false,视口会使用 Viewport,此时,视图区域将会沿滑动方向尽可能延伸。在无边界约束的情况下,shrinkWrap 需要是 true。' - '另外 ShrinkWrappingViewport 使用比较昂贵,因为滑动时需要重新计算滑动视图的尺寸。'; - - @override - Widget build(BuildContext context) { - return Container( - color: Colors.blue.withOpacity(0.1), - padding: const EdgeInsets.all(10), - margin: const EdgeInsets.all(10), - child: Text(info), - ); - } -} diff --git a/packages/widgets/lib/MultiChildRenderObjectWidget/Stack/node1_base.dart b/packages/widgets/lib/MultiChildRenderObjectWidget/Stack/node1_base.dart deleted file mode 100644 index c1fdee434..000000000 --- a/packages/widgets/lib/MultiChildRenderObjectWidget/Stack/node1_base.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-03-30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 97, -// "name": 'Stack基本使用', -// "priority": 1, -// "subtitle": -// "【children】 : 组件列表 【List】\n" -// "【textDirection】 : 孩子排布方向 【MainAxisAlignment】\n" -// "【alignment】 : 对齐方式 【AlignmentGeometry】\n" -// "【overflow】 : 溢出模式 【Overflow】\n" -// "【fit】 : 适应模式 【StackFit】", -// } -class CustomStack extends StatelessWidget { - const CustomStack({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - Widget yellowBox = Container( - color: Colors.yellow, - height: 100, - width: 100, - ); - - Widget redBox = Container( - color: Colors.red, - height: 90, - width: 90, - ); - - Widget greenBox = Container( - color: Colors.green, - height: 80, - width: 80, - ); - - Widget cyanBox = Container( - color: Colors.cyanAccent, - height: 70, - width: 70, - ); - - return Container( - width: 200, - height: 120, - color: Colors.grey.withAlpha(33), - child: Stack( - textDirection: TextDirection.rtl, - fit: StackFit.loose, - alignment: Alignment.topRight, - // overflow: Overflow.clip, // 1.22.0 被去除 - children: [yellowBox, redBox, greenBox, cyanBox], - ), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/MultiChildRenderObjectWidget/Stack/node2_positioned.dart b/packages/widgets/lib/MultiChildRenderObjectWidget/Stack/node2_positioned.dart deleted file mode 100644 index c4a3985ee..000000000 --- a/packages/widgets/lib/MultiChildRenderObjectWidget/Stack/node2_positioned.dart +++ /dev/null @@ -1,55 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-03-30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 97, -// "name": 'Stack和Positioned结合使用', -// "priority": 2, -// "subtitle": -// "Positioned组件只能用与Stack中,可以指定左上右下的距离对某个组件进行位置精确安放。", -// } -class PositionedStack extends StatelessWidget { - const PositionedStack({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - Widget yellowBox = Container( - color: Colors.yellow, - height: 100, - width: 100, - ); - - Widget redBox = Container( - color: Colors.red, - height: 90, - width: 90, - ); - - Widget greenBox = Container( - color: Colors.green, - height: 80, - width: 80, - ); - - Widget cyanBox = Container( - color: Colors.cyanAccent, - height: 70, - width: 70, - ); - - return Container( - width: 200, - height: 120, - color: Colors.grey.withAlpha(33), - child: Stack( - children: [yellowBox, redBox, greenBox, - Positioned( - child: cyanBox, - bottom: 10, - right: 10, - ) - ], - )); - } -} diff --git a/packages/widgets/lib/MultiChildRenderObjectWidget/Viewport/node1_base.dart b/packages/widgets/lib/MultiChildRenderObjectWidget/Viewport/node1_base.dart deleted file mode 100644 index 0cbbb1e18..000000000 --- a/packages/widgets/lib/MultiChildRenderObjectWidget/Viewport/node1_base.dart +++ /dev/null @@ -1,92 +0,0 @@ -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; - -/// create by 张风捷特烈 on 2020/8/2 -/// contact me by email 1981462002@qq.com -/// 说明: 340 Viewport 视口组件 通常用于为滑动视图提供视口,仅构建显示和预加载的部位。可指定预加载的长度、滑动轴向等。是ScrollView的核心实现组件之一,一般不直接使用。 - -// { -// "widgetId": 340, -// "name": "Viewport的基本使用", -// "priority": 1, -// "subtitle": "【offset】 : *视口偏移量 【ViewportOffset】\n" -// "【cacheExtentStyle】: 预加载类型 【CacheExtentStyle】\n" -// "【cacheExtent】: 预加载量 【double】\n" -// "【axisDirection】: 滑动方向 【AxisDirection】\n" -// "【slivers】: 子Sliver组件集 【List】\n" -// "【anchor】: 锚点 【double】\n" -// "可以运行这些代码,查看ColorItem的构建情况,128个色条并非一次性全部构建。", -// } - -class ViewportDemo extends StatelessWidget { - ViewportDemo({Key? key}) : super(key: key); - - final data = List.generate(128, (i) => Color(0xFF6600FF - 2 * i)); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 250, - child: Scrollable( - axisDirection: AxisDirection.down, - physics: const BouncingScrollPhysics(), - dragStartBehavior: DragStartBehavior.start, - viewportBuilder: (ctx, position) => Viewport( - axisDirection: AxisDirection.down, - cacheExtent: 200, - anchor: 0, - cacheExtentStyle: CacheExtentStyle.pixel, - offset: position, - slivers: [_buildSliverList()], - ), - ), - ); - } - - Widget _buildSliverList() => SliverList( - delegate: SliverChildBuilderDelegate( - (_, int index) =>ColorItem(color: data[index],), - childCount: data.length), - ); - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} - -class ColorItem extends StatefulWidget { - final Color color; - - const ColorItem({Key? key, required this.color}) : super(key: key); - - @override - _ColorItemState createState() => _ColorItemState(); -} - -class _ColorItemState extends State { - - @override - void initState() { - super.initState(); - print('-----initState----${colorString(widget.color)}-----------'); - } - - @override - Widget build(BuildContext context) { - return Container( - margin: const EdgeInsets.only(top: 1), - alignment: Alignment.center, - width: 100, - height: 60, - color: widget.color, - child: Text( - colorString(widget.color), - style: const TextStyle(color: Colors.white, shadows: [ - Shadow(color: Colors.black, offset: Offset(.5, .5), blurRadius: 2) - ]), - ), - ); - } - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/packages/widgets/lib/MultiChildRenderObjectWidget/Wrap/node1_base.dart b/packages/widgets/lib/MultiChildRenderObjectWidget/Wrap/node1_base.dart deleted file mode 100644 index 894e2f71d..000000000 --- a/packages/widgets/lib/MultiChildRenderObjectWidget/Wrap/node1_base.dart +++ /dev/null @@ -1,79 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-03-30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 98, -// "name": 'Wrap的基础用法', -// "priority": 1, -// "subtitle": -// "【children】 : 组件列表 【List】\n" -// "【spacing】 : 主轴条目间距 【double】\n" -// "【runSpacing】 : 交叉轴条目间距 【double】\n" -// "【direction】 : 主轴对齐 【Axis】", -// } -class DirectionWrap extends StatelessWidget { - DirectionWrap({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Wrap( - children: Axis.values - .map((mode) => Column(children: [ - Container( - margin: const EdgeInsets.all(5), - width: 160, - height: 100, - color: Colors.grey.withAlpha(33), - child: _buildItem(mode)), - Text(mode.toString().split('.')[1]) - ])) - .toList()); - } - final Widget yellowBox = Container( - color: Colors.yellow, - height: 30, - width: 50, - ); - - final Widget redBox = Container( - color: Colors.red, - height: 40, - width: 40, - ); - final Widget greenBox = Container( - color: Colors.green, - height: 40, - width: 20, - ); - final Widget blackBox = Container( - color: Colors.black, - height: 10, - width: 10, - ); - final Widget purpleBox = Container( - color: Colors.purple, - height: 20, - width: 20, - ); - final Widget orangeBox = Container( - color: Colors.orange, - height: 80, - width: 20, - ); - final Widget cyanBox = Container( - color: Colors.cyanAccent, - height: 10, - width: 20, - ); - - Widget _buildItem(mode) => Wrap( - direction: mode, - runSpacing: 10, - spacing: 10, - children: [ - yellowBox, redBox, greenBox, cyanBox, - blackBox, purpleBox, orangeBox, - ], - ); -} \ No newline at end of file diff --git a/packages/widgets/lib/MultiChildRenderObjectWidget/Wrap/node2_alignment.dart b/packages/widgets/lib/MultiChildRenderObjectWidget/Wrap/node2_alignment.dart deleted file mode 100644 index b37f59d8c..000000000 --- a/packages/widgets/lib/MultiChildRenderObjectWidget/Wrap/node2_alignment.dart +++ /dev/null @@ -1,80 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-03-30 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 98, -// "name": 'Wrap的alignment属性', -// "priority": 2, -// "subtitle": -// "【alignment】 : 主轴对齐 【WrapAlignment】", -// } -class WrapAlignmentWrap extends StatelessWidget { - WrapAlignmentWrap({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Wrap( - children: WrapAlignment.values - .map((mode) => Column(children: [ - Container( - margin: const EdgeInsets.all(5), - width: 160, - height: 100, - color: Colors.grey.withAlpha(88), - child: _buildItem(mode)), - Text(mode.toString().split('.')[1]) - ])) - .toList()); - } - - final Widget yellowBox = Container( - color: Colors.yellow, - height: 30, - width: 50, - ); - - final Widget redBox = Container( - color: Colors.red, - height: 40, - width: 40, - ); - final Widget greenBox = Container( - color: Colors.green, - height: 40, - width: 20, - ); - final Widget blackBox = Container( - color: Colors.black, - height: 10, - width: 10, - ); - final Widget purpleBox = Container( - color: Colors.purple, - height: 20, - width: 20, - ); - final Widget orangeBox = Container( - color: Colors.orange, - height: 80, - width: 20, - ); - final Widget cyanBox = Container( - color: Colors.cyanAccent, - height: 10, - width: 20, - ); - - Widget _buildItem(mode) => Wrap( - alignment: mode, - runSpacing: 10, - spacing: 10, - children: [ - yellowBox, redBox, - greenBox, cyanBox, - blackBox, purpleBox, - orangeBox, - ], - ); -} \ No newline at end of file diff --git a/packages/widgets/lib/MultiChildRenderObjectWidget/Wrap/node3_crossAxisAlignment.dart b/packages/widgets/lib/MultiChildRenderObjectWidget/Wrap/node3_crossAxisAlignment.dart deleted file mode 100644 index 90b61ca97..000000000 --- a/packages/widgets/lib/MultiChildRenderObjectWidget/Wrap/node3_crossAxisAlignment.dart +++ /dev/null @@ -1,81 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 98, -// "name": 'Wrap的crossAxisAlignment属性', -// "priority": 3, -// "subtitle": -// "【crossAxisAlignment】 : 交叉轴对齐 【CrossAxisAlignment】", -// } -class CrossAxisAlignmentWrap extends StatelessWidget { - CrossAxisAlignmentWrap({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Wrap( - children: WrapCrossAlignment.values - .map((mode) => Column(children: [ - Container( - margin: const EdgeInsets.all(5), - width: 160, - height: 100, - color: Colors.grey.withAlpha(88), - child: _buildItem(mode)), - Text(mode.toString().split('.')[1]) - ])) - .toList()); - } - - final Widget yellowBox = Container( - color: Colors.yellow, - height: 30, - width: 50, - ); - - final Widget redBox = Container( - color: Colors.red, - height: 40, - width: 40, - ); - final Widget greenBox = Container( - color: Colors.green, - height: 40, - width: 20, - ); - final Widget blackBox = Container( - color: Colors.black, - height: 10, - width: 10, - ); - final Widget purpleBox = Container( - color: Colors.purple, - height: 20, - width: 20, - ); - final Widget orangeBox = Container( - color: Colors.orange, - height: 80, - width: 20, - ); - final Widget cyanBox = Container( - color: Colors.cyanAccent, - height: 10, - width: 20, - ); - - Widget _buildItem(mode) => Wrap( - crossAxisAlignment: mode, - runSpacing: 10, - spacing: 10, - children: [ - yellowBox, redBox, - greenBox, cyanBox, - blackBox, purpleBox, - orangeBox, - ], - ); -} \ No newline at end of file diff --git a/packages/widgets/lib/MultiChildRenderObjectWidget/Wrap/node4_textDirection.dart b/packages/widgets/lib/MultiChildRenderObjectWidget/Wrap/node4_textDirection.dart deleted file mode 100644 index 252839c43..000000000 --- a/packages/widgets/lib/MultiChildRenderObjectWidget/Wrap/node4_textDirection.dart +++ /dev/null @@ -1,79 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 98, -// "name": 'Wrap的textDirection属性', -// "priority": 4, -// "subtitle": -// "【textDirection】 : 文字方向 【TextDirection】", -// } -class TextDirectionWrap extends StatelessWidget { - TextDirectionWrap({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Wrap( - children: TextDirection.values - .map((mode) => Column(children: [ - Container( - margin: const EdgeInsets.all(5), - width: 160, - height: 100, - color: Colors.grey.withAlpha(88), - child: _buildItem(mode)), - Text(mode.toString().split('.')[1]) - ])) - .toList()); - } - - final Widget yellowBox = Container( - color: Colors.yellow, - height: 30, - width: 50, - ); - - final Widget redBox = Container( - color: Colors.red, - height: 40, - width: 40, - ); - final Widget greenBox = Container( - color: Colors.green, - height: 40, - width: 20, - ); - final Widget blackBox = Container( - color: Colors.black, - height: 10, - width: 10, - ); - final Widget purpleBox = Container( - color: Colors.purple, - height: 20, - width: 20, - ); - final Widget orangeBox = Container( - color: Colors.orange, - height: 80, - width: 20, - ); - final Widget cyanBox = Container( - color: Colors.cyanAccent, - height: 10, - width: 20, - ); - - Widget _buildItem(mode) => Wrap( - textDirection: mode, - runSpacing: 10, - spacing: 10, - children: [ - yellowBox, redBox, greenBox, cyanBox, - blackBox, purpleBox, orangeBox, - ], - ); -} diff --git a/packages/widgets/lib/MultiChildRenderObjectWidget/Wrap/node5_verticalDirection.dart b/packages/widgets/lib/MultiChildRenderObjectWidget/Wrap/node5_verticalDirection.dart deleted file mode 100644 index d8ea28ba3..000000000 --- a/packages/widgets/lib/MultiChildRenderObjectWidget/Wrap/node5_verticalDirection.dart +++ /dev/null @@ -1,80 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 98, -// "name": 'Wrap的verticalDirection属性', -// "priority": 5, -// "subtitle": -// "【verticalDirection】 : 竖直方向 【VerticalDirection】", -// } -class VerticalDirectionWrap extends StatelessWidget { - VerticalDirectionWrap({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Wrap( - children: VerticalDirection.values - .map((mode) => Column(children: [ - Container( - margin: const EdgeInsets.all(5), - width: 160, - height: 100, - color: Colors.grey.withAlpha(88), - child: _buildItem(mode)), - Text(mode.toString().split('.')[1]) - ])) - .toList()); - } - - final Widget yellowBox = Container( - color: Colors.yellow, - height: 30, - width: 50, - ); - - final Widget redBox = Container( - color: Colors.red, - height: 40, - width: 40, - ); - final Widget greenBox = Container( - color: Colors.green, - height: 40, - width: 20, - ); - final Widget blackBox = Container( - color: Colors.black, - height: 10, - width: 10, - ); - final Widget purpleBox = Container( - color: Colors.purple, - height: 20, - width: 20, - ); - final Widget orangeBox = Container( - color: Colors.orange, - height: 80, - width: 20, - ); - final Widget cyanBox = Container( - color: Colors.cyanAccent, - height: 10, - width: 20, - ); - - Widget _buildItem(mode) => Wrap( - verticalDirection: mode, - direction: Axis.vertical, - runSpacing: 10, - spacing: 10, - children: [ - yellowBox, redBox, greenBox, cyanBox, - blackBox, purpleBox, orangeBox, - ], - ); -} \ No newline at end of file diff --git a/packages/widgets/lib/Other/ErrorWidget/node1_base.dart b/packages/widgets/lib/Other/ErrorWidget/node1_base.dart deleted file mode 100644 index 601771c75..000000000 --- a/packages/widgets/lib/Other/ErrorWidget/node1_base.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-31 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 197, -// "name": 'ErrorWidget基本使用', -// "priority": 1, -// "subtitle": "入参 : 显示信息 【Object】", -// } -class ErrorWidgetDemo extends StatelessWidget { - const ErrorWidgetDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 200, - child: ErrorWidget( - 'I am Error ErrorWidget\n' - 'But now, there has no error.' - ), - ); - } -} diff --git a/packages/widgets/lib/Other/ListWheelViewport/node1_base.dart b/packages/widgets/lib/Other/ListWheelViewport/node1_base.dart deleted file mode 100644 index 318c99be5..000000000 --- a/packages/widgets/lib/Other/ListWheelViewport/node1_base.dart +++ /dev/null @@ -1,75 +0,0 @@ -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 291 ListWheelViewport 列表滚轮视口 一个将孩子列表显示在柱状滚轮上的视口,是 ListWheelScrollView、CupertinoPicker 的底层依赖。 -/// link 179,139,137,253 -/// -// { -// "widgetId": 291, -// "name": 'ListWheelViewport 简单使用', -// "priority": 1, -// "subtitle": "【itemExtent】 : 轴向item尺寸 【double】\n" -// "【offset】 : 视口偏移 【ViewportOffset】\n" -// "【childDelegate】 : 孩子代理构造器 【ListWheelChildDelegate】", -// } - -class ListWheelViewportDemo extends StatelessWidget { - ListWheelViewportDemo({Key? key}) : super(key: key); - - final List data = [ - Colors.blue[50]!, - Colors.blue[100]!, - Colors.blue[200]!, - Colors.blue[300]!, - Colors.blue[400]!, - Colors.blue[500]!, - Colors.blue[600]!, - Colors.blue[700]!, - Colors.blue[800]!, - Colors.blue[900]!, - Colors.blue[800]!, - Colors.blue[700]!, - Colors.blue[600]!, - Colors.blue[500]!, - Colors.blue[400]!, - Colors.blue[300]!, - Colors.blue[200]!, - Colors.blue[100]!, - ]; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 250, - width: 320, - child: Scrollable( - axisDirection: AxisDirection.down, - physics: const BouncingScrollPhysics(), - dragStartBehavior: DragStartBehavior.start, - viewportBuilder: (ctx, position) => ListWheelViewport( - itemExtent: 100, - offset: position, - childDelegate: ListWheelChildLoopingListDelegate( - children: data.map((e) => _buildItem(e)).toList()), - )), - ); - } - - Widget _buildItem(Color color) => Container( - alignment: Alignment.center, - color: color, - child: Text(colorString(color), - style: const TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2, - ), - ])), - ); - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/packages/widgets/lib/Other/ListWheelViewport/node2_perspective.dart b/packages/widgets/lib/Other/ListWheelViewport/node2_perspective.dart deleted file mode 100644 index f4507cf92..000000000 --- a/packages/widgets/lib/Other/ListWheelViewport/node2_perspective.dart +++ /dev/null @@ -1,76 +0,0 @@ -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 291, -// "name": 'ListWheelViewport 透视效果', -// "priority": 2, -// "subtitle": "【perspective】 : 透视参数 【double】\n" -// "【squeeze】 : 挤压值 【double】\n" -// "【diameterRatio】 : 直径分率 【double】", -// } - -class ListWheelViewportDemo2 extends StatelessWidget { - ListWheelViewportDemo2({Key? key}) : super(key: key); - - final List data = [ - Colors.blue[50]!, - Colors.blue[100]!, - Colors.blue[200]!, - Colors.blue[300]!, - Colors.blue[400]!, - Colors.blue[500]!, - Colors.blue[600]!, - Colors.blue[700]!, - Colors.blue[800]!, - Colors.blue[900]!, - Colors.blue[800]!, - Colors.blue[700]!, - Colors.blue[600]!, - Colors.blue[500]!, - Colors.blue[400]!, - Colors.blue[300]!, - Colors.blue[200]!, - Colors.blue[100]!, - ]; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 250, - width: 320, - child: Scrollable( - axisDirection: AxisDirection.down, - physics: const BouncingScrollPhysics(), - dragStartBehavior: DragStartBehavior.start, - viewportBuilder: (ctx, position) => ListWheelViewport( - perspective: 0.008, - squeeze: 1, - diameterRatio: 2, - itemExtent: 50, - offset: position, - childDelegate: ListWheelChildLoopingListDelegate( - children: data.map((e) => _buildItem(e)).toList()), - )), - ); - } - - Widget _buildItem(Color color) => Container( - alignment: Alignment.center, - color: color, - child: Text(colorString(color), - style: const TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2, - ) - ])), - ); - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/packages/widgets/lib/Other/ListWheelViewport/node3_magnifier.dart b/packages/widgets/lib/Other/ListWheelViewport/node3_magnifier.dart deleted file mode 100644 index 5aa08c63d..000000000 --- a/packages/widgets/lib/Other/ListWheelViewport/node3_magnifier.dart +++ /dev/null @@ -1,80 +0,0 @@ -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: -/// -// { -// "widgetId": 291, -// "name": 'ListWheelViewport 放大', -// "priority": 3, -// "subtitle": "【useMagnifier】 : 是否放大 【bool】\n" -// "【magnification】 : 放大比例 【double】\n" -// "【clipBehavior】 : 剪裁行为 【Clip】\n" -// "【renderChildrenOutsideViewport】 : 出视野是否渲染 【bool】", -// } - -class ListWheelViewportDemo3 extends StatelessWidget { - ListWheelViewportDemo3({Key? key}) : super(key: key); - - final List data = [ - Colors.blue[50]!, - Colors.blue[100]!, - Colors.blue[200]!, - Colors.blue[300]!, - Colors.blue[400]!, - Colors.blue[500]!, - Colors.blue[600]!, - Colors.blue[700]!, - Colors.blue[800]!, - Colors.blue[900]!, - Colors.blue[800]!, - Colors.blue[700]!, - Colors.blue[600]!, - Colors.blue[500]!, - Colors.blue[400]!, - Colors.blue[300]!, - Colors.blue[200]!, - Colors.blue[100]!, - ]; - - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 250, - width: 320, - // color: Colors.red, - child: Scrollable( - axisDirection: AxisDirection.down, - physics: const BouncingScrollPhysics(), - dragStartBehavior: DragStartBehavior.start, - viewportBuilder: (ctx, position) => ListWheelViewport( - perspective: 0.008, - squeeze: 1, - diameterRatio: 2, - itemExtent: 50, - useMagnifier: true, - magnification: 2, - renderChildrenOutsideViewport: true, - clipBehavior: Clip.none, - offset: position, - childDelegate: ListWheelChildLoopingListDelegate( - children: data.map((e) => _buildItem(e)).toList()), - )), - ); - } - - Widget _buildItem(Color color) => Container( - alignment: Alignment.center, - color: color, - child: Text(colorString(color), - style: const TextStyle(color: Colors.white, shadows: [ - Shadow(color: Colors.black, offset: Offset(.5, .5), blurRadius: 2) - ])), - ); - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/packages/widgets/lib/Other/ListWheelViewport/node4_opacity.dart b/packages/widgets/lib/Other/ListWheelViewport/node4_opacity.dart deleted file mode 100644 index b85633522..000000000 --- a/packages/widgets/lib/Other/ListWheelViewport/node4_opacity.dart +++ /dev/null @@ -1,79 +0,0 @@ -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: -/// -// { -// "widgetId": 291, -// "name": '偏移和透明度', -// "priority": 4, -// "subtitle": "【offAxisFraction】 : 轴中心偏移比 【double】\n" -// "【overAndUnderCenterOpacity】 : 放大器之外的透明度 【double】", -// } - -class ListWheelViewportDemo4 extends StatelessWidget { - ListWheelViewportDemo4({Key? key}) : super(key: key); - - final List data = [ - Colors.blue[50]!, - Colors.blue[100]!, - Colors.blue[200]!, - Colors.blue[300]!, - Colors.blue[400]!, - Colors.blue[500]!, - Colors.blue[600]!, - Colors.blue[700]!, - Colors.blue[800]!, - Colors.blue[900]!, - Colors.blue[800]!, - Colors.blue[700]!, - Colors.blue[600]!, - Colors.blue[500]!, - Colors.blue[400]!, - Colors.blue[300]!, - Colors.blue[200]!, - Colors.blue[100]!, - ]; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 250, - width: 320, - // color: Colors.red, - child: Scrollable( - axisDirection: AxisDirection.down, - physics: const BouncingScrollPhysics(), - dragStartBehavior: DragStartBehavior.start, - viewportBuilder: (ctx, position) => ListWheelViewport( - perspective: 0.008, - squeeze: 1, - diameterRatio: 2, - offAxisFraction: 0.2, - overAndUnderCenterOpacity: 0.4, - itemExtent: 50, - offset: position, - childDelegate: ListWheelChildLoopingListDelegate( - children: data.map((e) => _buildItem(e)).toList()), - )), - ); - } - - Widget _buildItem(Color color) => Container( - alignment: Alignment.center, - color: color, - child: Text(colorString(color), - style: const TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2, - ) - ])), - ); - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/packages/widgets/lib/Other/PerformanceOverlay/node1_base.dart b/packages/widgets/lib/Other/PerformanceOverlay/node1_base.dart deleted file mode 100644 index 7362c0d15..000000000 --- a/packages/widgets/lib/Other/PerformanceOverlay/node1_base.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/7/22 -/// contact me by email 1981462002@qq.com -/// 说明: 312 PerformanceOverlay 性能浮层 6 可以非常方便地开启性能监测的两个柱图,方便查看刷新界面时帧率的变化情况。 -// { -// "widgetId": 312, -// "name": "PerformanceOverlay基本使用", -// "priority": 1, -// "subtitle": "使用PerformanceOverlay.allEnabled可以开始所有的监测项。", -// } - -class PerformanceOverlayDemo extends StatelessWidget { - const PerformanceOverlayDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return PerformanceOverlay.allEnabled( - - ); - } -} - diff --git a/packages/widgets/lib/Other/RawImage/node1_base.dart b/packages/widgets/lib/Other/RawImage/node1_base.dart deleted file mode 100644 index c33cfcbfc..000000000 --- a/packages/widgets/lib/Other/RawImage/node1_base.dart +++ /dev/null @@ -1,93 +0,0 @@ -import 'dart:async'; -import 'dart:typed_data'; -import 'dart:ui' as ui; - -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -/// create by 张风捷特烈 on 2020/7/22 -/// contact me by email 1981462002@qq.com -/// 说明: 313 RawImage 6 是实现Image组件的核心组件,可以显示ui的Image,基本属性同Image,一般很少单独使用。 -// { -// "widgetId": 313, -// "name": "RawImage基本使用", -// "priority": 1, -// "subtitle": "【image】 : 图片 【ui.Image】\n" -// "【width】 : 宽 【int】\n" -// "【height】: 高 【int】\n" -// "【isAntiAlias】: 是否抗锯齿 【bool】\n" -// "【filterQuality】: 过滤质量 【FilterQuality】\n" -// "很多属性同Image,详见之.", -// } -class RawImageDemo extends StatefulWidget { - const RawImageDemo({Key? key}) : super(key: key); - - @override - _RawImageDemoState createState() => _RawImageDemoState(); -} - -class _RawImageDemoState extends State { - ui.Image? _image; - - @override - void initState() { - super.initState(); - _loadImageFromAssets('assets/images/icon_head.webp'); - } - - @override - Widget build(BuildContext context) { - if (_image == null) { - return const SizedBox( - width: 80, - height: 80, - ); - } - - return Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Column( - mainAxisSize: MainAxisSize.min, - children: [ - RawImage( - image: _image, - width: 150, - height: 150, - isAntiAlias: true, - filterQuality: FilterQuality.high, - ), - const Text('isAntiAlias: true'), - const Text('FilterQuality.high') - ], - ), - Column( - mainAxisSize: MainAxisSize.min, - children: [ - RawImage( - image: _image, - width: 150, - height: 150, - isAntiAlias: false, - ), - const Text('isAntiAlias: false'), - const Text('FilterQuality.low') - ], - ), - ], - ); - } - - void _loadImageFromAssets(String name) async { - _image = await loadImageFromAssets(name); - setState(() {}); - } - - //读取 assets 中的图片 - Future loadImageFromAssets(String path) async { - ByteData data = await rootBundle.load(path); - List bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); - return decodeImageFromList(Uint8List.fromList(bytes)); - } -} diff --git a/packages/widgets/lib/Other/RenderObjectToWidgetAdapter/node1_base.dart b/packages/widgets/lib/Other/RenderObjectToWidgetAdapter/node1_base.dart deleted file mode 100644 index f82eb1c68..000000000 --- a/packages/widgets/lib/Other/RenderObjectToWidgetAdapter/node1_base.dart +++ /dev/null @@ -1,34 +0,0 @@ - -import 'package:flutter/material.dart'; - - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 289 RenderObjectToWidgetAdapter 根组件 RenderObject 和 Element 树的桥梁。 -/// -// { -// "widgetId": 289, -// "name": 'RenderObjectToWidgetAdapter 介绍', -// "priority": 1, -// "subtitle": "【container】 : 渲染对象 【RenderObjectWithChildMixin】\n" -// "【child】 : 子组件 【Widget】\n" -// "【debugShortDescription】 : 调试简介 【String】", -// } - -class RenderObjectToWidgetAdapterDemo extends StatelessWidget { - const RenderObjectToWidgetAdapterDemo({Key? key}) : super(key: key); - - final String info = - '该组件并没有什么太大的使用价值,但却非常有纪念意义。它是 Flutter 框架中最顶层的 Widget,它的 child 是 runApp 传入的组件,在 attachRootWidget 方法中被实例化。' - '它持有根渲染对象 RenderView ,负责创建根元素 RenderObjectToWidgetElement,是一个无名英雄,一个深藏功与名的组件。'; - - @override - Widget build(BuildContext context) { - return Container( - color: Colors.blue.withOpacity(0.1), - padding: const EdgeInsets.all(10), - margin: const EdgeInsets.all(10), - child: Text(info), - ); - } -} diff --git a/packages/widgets/lib/Other/Table/node1_base.dart b/packages/widgets/lib/Other/Table/node1_base.dart deleted file mode 100644 index 4f6d0c782..000000000 --- a/packages/widgets/lib/Other/Table/node1_base.dart +++ /dev/null @@ -1,83 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 110, -// "name": 'Table基本使用', -// "priority": 1, -// "subtitle": "【children】 : 组件列表 【List】\n" -// "【columnWidths】 : 列宽 【Map】\n" -// "【defaultColumnWidth】 : 默认列宽 【TableColumnWidth】\n" -// "【border】 : 边线 【TableBorder】\n" -// "【textDirection】 : 文字方向 【TextDirection】\n" -// "【defaultVerticalAlignment】 : 单元格竖直方向对齐模式 【TableCellVerticalAlignment】", -// } -class CustomTable extends StatelessWidget { - const CustomTable({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - _ItemBean title = _ItemBean("单位称", "量纲", "单位", "单位名称", "单位符号"); - _ItemBean m = _ItemBean("长度", "L", "1m", "米", "m"); - _ItemBean kg = _ItemBean("质量", "M", "1Kg", "千克", "Kg"); - _ItemBean s = _ItemBean("时间", "T", "1s", "秒", "s"); - _ItemBean a = _ItemBean("安培", "Ι", "1A", "安培", "A"); - _ItemBean k = _ItemBean("热力学温度", "θ", "1K", "开尔文", "K"); - _ItemBean mol = _ItemBean("物质的量", "N", "1mol", "摩尔", "mol"); - _ItemBean cd = _ItemBean("发光强度", "J", "1cd", "坎德拉", "cd"); - - List<_ItemBean> data = [title, m, kg, s, a, k, mol, cd]; - - return SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Table( - columnWidths: const { - 0: FixedColumnWidth(80.0), - 1: FixedColumnWidth(80.0), - 2: FixedColumnWidth(80.0), - 3: FixedColumnWidth(80.0), - 4: FixedColumnWidth(80.0), - }, - defaultVerticalAlignment: TableCellVerticalAlignment.middle, - border: TableBorder.all( - color: Colors.orangeAccent, width: 1.0, style: BorderStyle.solid), - children: data - .map((item) => TableRow(children: [ - Center( - child: Text( - item.name, - style: const TextStyle(color: Colors.blue), - )), - Padding( - padding: const EdgeInsets.all(8.0), - child: Center(child: Text(item.symbol)), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Center(child: Text(item.unitSymbol)), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Center(child: Text(item.unitName)), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Center(child: Text(item.unit)), - ), - ])) - .toList(), - ), - ); - } -} - -class _ItemBean { - String name; - String symbol; - String unit; - String unitName; - String unitSymbol; - - _ItemBean(this.name, this.symbol, this.unit, this.unitName, this.unitSymbol); -} diff --git a/packages/widgets/lib/ProxyWidget/ButtonBarTheme/node1_base.dart b/packages/widgets/lib/ProxyWidget/ButtonBarTheme/node1_base.dart deleted file mode 100644 index 0f0f91e23..000000000 --- a/packages/widgets/lib/ProxyWidget/ButtonBarTheme/node1_base.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/7/22 -/// contact me by email 1981462002@qq.com -/// 说明: 338 ButtonBarTheme 5 主要用于为后代的ButtonBar组件统一设置默认属性,也可以通过该组件获取默认ButtonBarTheme的属性。 -// { -// "widgetId": 338, -// "name": "ButtonBarTheme基本使用", -// "priority": 1, -// "subtitle": "可指定ButtonBarThemeData数据属性为【后代】的ButtonBar组件设置默认样式,如对齐方式、样式、边距等。也可以用ButtonBarTheme.of获取ButtonBar的主题属性。", -// } - -class ButtonBarThemeDemo extends StatelessWidget { - const ButtonBarThemeDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return ButtonBarTheme( - child: const TempButtonBar(), - data: ButtonBarTheme.of(context).copyWith( - alignment: MainAxisAlignment.center, - buttonPadding: const EdgeInsets.symmetric(horizontal: 6), - overflowDirection: VerticalDirection.up, - buttonMinWidth: 150, - buttonHeight: 30, - buttonTextTheme: ButtonTextTheme.primary)); - } -} - -class TempButtonBar extends StatelessWidget { - const TempButtonBar({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return ButtonBar( - alignment: MainAxisAlignment.center, - children: [ - ElevatedButton( - style: ElevatedButton.styleFrom(backgroundColor: Colors.blue), - child: const Text("1.Elevated"), onPressed: () {}), - OutlinedButton(child: const Text("2.Outlined"), onPressed: () {}), - TextButton( - style: ElevatedButton.styleFrom(backgroundColor: Colors.blue), - onPressed: () {}, - child: const Text("3.Text"), - ) - ], - ); - } -} diff --git a/packages/widgets/lib/ProxyWidget/ButtonTheme/node1_base.dart b/packages/widgets/lib/ProxyWidget/ButtonTheme/node1_base.dart deleted file mode 100644 index 9aa327e0d..000000000 --- a/packages/widgets/lib/ProxyWidget/ButtonTheme/node1_base.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-04-12 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 326, -// "name": 'ButtonTheme使用', -// "priority": 1, -// "subtitle": "属性参数同MaterialButton,可以通过ButtonTheme.of获取按钮主题数据," -// "也可以为ButtonTheme【后代】的按钮组件设置默认样式,包括颜色、形状、尺寸等。", -// } - - -class ButtonThemeDemo extends StatelessWidget { - const ButtonThemeDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return ButtonTheme( - buttonColor: Colors.orange, - splashColor: Colors.blue, - minWidth: 40, - shape: const CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFDFDFDF)), - ), - child: Wrap( - spacing: 10, - children: [ - ElevatedButton(onPressed: (){},child: const Icon(Icons.add)), - TextButton(onPressed: (){},child: const Icon(Icons.add)), - OutlinedButton(onPressed: (){},child: const Icon(Icons.add)), - MaterialButton(onPressed: (){},child: const Icon(Icons.add)), - ], - ), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/ProxyWidget/ChipTheme/node1_base.dart b/packages/widgets/lib/ProxyWidget/ChipTheme/node1_base.dart deleted file mode 100644 index aa1ca8240..000000000 --- a/packages/widgets/lib/ProxyWidget/ChipTheme/node1_base.dart +++ /dev/null @@ -1,80 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/7/20 -/// contact me by email 1981462002@qq.com -/// 说明: 328 ChipTheme 主要用于为后代的Chip类型组件统一设置默认属性,也可以通过该组件获取默认Chip的属性。 -// { -// "widgetId": 328, -// "name": 'ChipTheme基本使用', -// "priority": 1, -// "subtitle": "可指定ChipThemeData数据属性为【后代】的Chip类型组件设置默认样式,属性和Chip属性类似,如阴影、颜色、边距、形状、文字样式等。也可以用ChipTheme.of获取Chip的主题数据。", -// } -class ChipThemeDemo extends StatelessWidget { - const ChipThemeDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return ChipTheme( - data: ChipTheme.of(context).copyWith( - selectedColor: Colors.orange.withAlpha(55), - selectedShadowColor: Colors.blue, - shadowColor: Colors.orangeAccent, - pressElevation: 5, - elevation: 3, - ), - child: const CustomFilterChip(), - ); - } -} - -class CustomFilterChip extends StatefulWidget { - const CustomFilterChip({Key? key}) : super(key: key); - - @override - _CustomFilterChipState createState() => _CustomFilterChipState(); -} - -class _CustomFilterChipState extends State { - final Map map = { - 'A': 'Ant', - 'B': 'Bug', - 'C': 'Cat', - 'D': 'Dog', - }; - final List _selected = []; - - @override - Widget build(BuildContext context) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Wrap( - children: map.keys.map((key) => _buildChild(key)).toList(), - ), - Container( - padding: const EdgeInsets.all(10), - child: Text('您已选择: ${_selected.join(', ')}')), - ], - ); - } - - Padding _buildChild(String key) { - return Padding( - padding: const EdgeInsets.all(4.0), - child: FilterChip( - avatar: CircleAvatar(child: Text(key)), - label: Text(map[key]!), - selected: _selected.contains(map[key]), - onSelected: (bool value) { - setState(() { - if (value) { - _selected.add(map[key]!); - } else { - _selected.removeWhere((name) => name == map[key]); - } - }); - }, - ), - ); - } -} diff --git a/packages/widgets/lib/ProxyWidget/CupertinoUserInterfaceLevel/node1_base.dart b/packages/widgets/lib/ProxyWidget/CupertinoUserInterfaceLevel/node1_base.dart deleted file mode 100644 index b13045c85..000000000 --- a/packages/widgets/lib/ProxyWidget/CupertinoUserInterfaceLevel/node1_base.dart +++ /dev/null @@ -1,44 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: 337 CupertinoUserInterfaceLevel 用户接口等级 -/// ios 中的概念,内容可视级别 UIUserInterfaceLevel ,分为 base 和 elevated。作为一个 InheritedWidget ,主要就是共享该数据。 - -// { -// "widgetId": 337, -// "name": 'CupertinoUserInterfaceLevel 介绍', -// "priority": 1, -// "subtitle": "CupertinoUserInterfaceLevel.of(context) 可以获取 CupertinoUserInterfaceLevelData 数据。也可以使用该组件设置该数据与子树共享。关于数据原图详见: https://developer.apple.com/documentation/uikit/uiuserinterfacelevel", -// } - -class CupertinoUserInterfaceLevelDemo extends StatelessWidget { - const CupertinoUserInterfaceLevelDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return const CupertinoUserInterfaceLevel( - data: CupertinoUserInterfaceLevelData.elevated, - child: LevelShower() - ); - } - -} - -class LevelShower extends StatelessWidget { - const LevelShower({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - CupertinoUserInterfaceLevelData data = CupertinoUserInterfaceLevel.of(context); - return Container( - height: 150, - alignment: Alignment.center, - color: Theme.of(context).primaryColor.withOpacity(0.1), - child: Text(data.toString()), - ); - } -} - - diff --git a/packages/widgets/lib/ProxyWidget/DefaultAssetBundle/node1_base.dart b/packages/widgets/lib/ProxyWidget/DefaultAssetBundle/node1_base.dart deleted file mode 100644 index 0c25dca14..000000000 --- a/packages/widgets/lib/ProxyWidget/DefaultAssetBundle/node1_base.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'dart:ui' as ui; -/// create by 张风捷特烈 on 2020-04-01 -/// contact me by email 1981462002@qq.com -/// 说明: 320 DefaultAssetBundle 默认资源包 -/// 一个 InheritedWidget,设置 AssetBundle 对象后,该节点后的节点上下文可以通过 DefaultAssetBundle.of(context) 获取 AssetBundle 对象用于访问资源文件。 -// { -// "widgetId": 320, -// "name": 'DefaultAssetBundle 介绍', -// "priority": 1, -// "subtitle": "【bundle】 : *资源包 【AssetBundle】\n" -// "【child】 : *子组件 【Widget】\n" -// "我们可以定义自己的 DefaultAssetBundle 来供后续节点使用,也可以直接使用默认的。该案例演示通过框架提供的 DefaultAssetBundle 加载一张资源图片进行显示。", -// } -class DefaultAssetBundleDemo extends StatefulWidget { - const DefaultAssetBundleDemo({Key? key}) : super(key: key); - - @override - _DefaultAssetBundleDemoState createState() => _DefaultAssetBundleDemoState(); -} - -class _DefaultAssetBundleDemoState extends State { - ui.Image? _image; - @override - void initState() { - super.initState(); - _load(); - } - - @override - Widget build(BuildContext context) { - - return Container( - width: 150, - height: 150, - color: Colors.blue.withOpacity(0.1), - padding: const EdgeInsets.all(10), - margin: const EdgeInsets.all(10), - child: _image==null?Container():RawImage(image: _image,fit: BoxFit.cover,), - ); - } - - void _load() async{ - AssetBundle info = DefaultAssetBundle.of(context); - ByteData data = await info.load('assets/images/sabar.webp'); - _image = await decodeImageFromList(data.buffer.asUint8List()); - setState(() { - - }); - } -} diff --git a/packages/widgets/lib/ProxyWidget/DefaultTextStyle/node1_base.dart b/packages/widgets/lib/ProxyWidget/DefaultTextStyle/node1_base.dart deleted file mode 100644 index 28d297ce4..000000000 --- a/packages/widgets/lib/ProxyWidget/DefaultTextStyle/node1_base.dart +++ /dev/null @@ -1,36 +0,0 @@ - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-04-12 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 324, -// "name": 'DefaultTextStyle使用', -// "priority": 1, -// "subtitle": "各属性同Text,详见之。\n" -// "其功能是: 设置默认的文字样式应用于【后代组件】,注意后代组件也可以指定自身的样式", -// } -class DefaultTextStyleDemo extends StatelessWidget { - const DefaultTextStyleDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return DefaultTextStyle( - style: const TextStyle( - fontSize: 18, - color: Colors.blue, - decoration: TextDecoration.underline), - child: Wrap( - spacing: 5, - children: const[ - Text("Hello,",), - FlutterLogo(), - Text("Flutter",style: TextStyle(color: Colors.red),), - Text("Unit."), - ], - ), - ); - } -} diff --git a/packages/widgets/lib/ProxyWidget/Directionality/node1_base.dart b/packages/widgets/lib/ProxyWidget/Directionality/node1_base.dart deleted file mode 100644 index 734e9e742..000000000 --- a/packages/widgets/lib/ProxyWidget/Directionality/node1_base.dart +++ /dev/null @@ -1,62 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-23 -/// contact me by email 1981462002@qq.com -/// 说明: 319 Directionality 定向性 为后代改变有textDirection属性的组件统一设置属性值,也可以通过Directionality.of(context)获取当前textDirection默认属性。 -// { -// "widgetId": 319, -// "name": "Directionality基本使用", -// "priority": 1, -// "subtitle": "【textDirection】 : 文字排列方向 【TextDirection】\n" -// "【child】 : 子组件 【Widget】", -// } -class DirectionalityDemo extends StatefulWidget { - const DirectionalityDemo({Key? key}) : super(key: key); - - @override - _DirectionalityDemoState createState() => _DirectionalityDemoState(); -} - -class _DirectionalityDemoState extends State { - TextDirection _textDirection = TextDirection.rtl; - - @override - Widget build(BuildContext context) { - return Directionality( - textDirection: _textDirection, - child: Container( - padding: const EdgeInsets.all(8), - width: 250, - color: Colors.grey.withAlpha(33), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const Text( - 'A widget that determines the ambient directionality of text and text direction sensitive render objects.'), - _buildSwitch(), - const Text( - 'The text direction from the closest instance of this class that encloses the given context.'), - ], - ), - ), - ); - } - - Widget _buildSwitch() => Row( - children: [ - Switch( - value: _textDirection == TextDirection.rtl, - onChanged: (v) { - setState(() { - _textDirection = - v ? TextDirection.rtl : TextDirection.ltr; - }); - }, - ), - Text( - _textDirection.toString(), - style: const TextStyle(color: Colors.blue, fontSize: 18), - ) - ], - ); -} diff --git a/packages/widgets/lib/ProxyWidget/DividerTheme/node1_base.dart b/packages/widgets/lib/ProxyWidget/DividerTheme/node1_base.dart deleted file mode 100644 index 93c10205c..000000000 --- a/packages/widgets/lib/ProxyWidget/DividerTheme/node1_base.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-04-12 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 329, -// "name": 'DividerTheme使用', -// "priority": 1, -// "subtitle": "属性参数与Divider类似,可以通过DividerTheme.of获取分割线主题数据," -// "也可以为DividerTheme【后代】的分割线设置默认样式,包括颜色、粗细、高度等。", -// } - - -class DividerThemeDemo extends StatelessWidget { - const DividerThemeDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return DividerTheme( - data: const DividerThemeData( - color: Colors.orange, - thickness: 2, - space: 10, - indent: 10, - endIndent: 10, - ), - child: Wrap( - spacing: 10, - children: [ - const Divider(), - const Divider(), - const Divider(), - const Divider(), - const Divider(), - SizedBox( - height: 100, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: const [ - VerticalDivider(), - VerticalDivider(), - VerticalDivider(), - VerticalDivider(), - VerticalDivider(), - ], - ), - ) - ], - ), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/ProxyWidget/DropdownButtonHideUnderline/node1_base.dart b/packages/widgets/lib/ProxyWidget/DropdownButtonHideUnderline/node1_base.dart deleted file mode 100644 index eec57a57a..000000000 --- a/packages/widgets/lib/ProxyWidget/DropdownButtonHideUnderline/node1_base.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 181, -// "name": 'DropDownButtonHideUnderline使用', -// "priority": 1, -// "subtitle": "【child】 : 子组件 【Widget】\n", -// } -class CustomDropDownButtonHideUnderline extends StatefulWidget { - const CustomDropDownButtonHideUnderline({Key? key}) : super(key: key); - - @override - _CustomDropDownButtonHideUnderlineState createState() => - _CustomDropDownButtonHideUnderlineState(); -} - -class _CustomDropDownButtonHideUnderlineState - extends State { - Color _color = Colors.red; - final List _colors = const[ - Colors.red, - Colors.yellow, - Colors.blue, - Colors.green - ]; - final List _info = const["红色", "黄色", "蓝色", "绿色"]; - - @override - Widget build(BuildContext context) { - return Wrap( - children: [ - Container( - margin: const EdgeInsets.symmetric(horizontal: 20), - width: 50, - height: 50, - color: _color, - ), - DropdownButtonHideUnderline( - child: DropdownButton( - value: _color, - elevation: 1, - icon: Icon( - Icons.expand_more, - size: 20, - color: _color, - ), - items: _buildItems(), - onChanged: (Color? color) => - setState(() => _color = color ?? _color)), - ), - ], - ); - } - - List> _buildItems() => _colors - .map((e) => DropdownMenuItem( - value: e, - child: Text( - _info[_colors.indexOf(e)], - style: TextStyle(color: e), - ))) - .toList(); -} diff --git a/packages/widgets/lib/ProxyWidget/Expanded/node1_base.dart b/packages/widgets/lib/ProxyWidget/Expanded/node1_base.dart deleted file mode 100644 index fbf56eda0..000000000 --- a/packages/widgets/lib/ProxyWidget/Expanded/node1_base.dart +++ /dev/null @@ -1,55 +0,0 @@ - import 'package:flutter/material.dart'; -import 'package:widgets/utils/color_utils.dart'; - -/// create by 张风捷特烈 on 2020-03-22 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 106, -// "name": 'Expanded基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子 【Widget】\n" -// "【flex】 : 剩余空间分配占比 【int】", -// } -class CustomExpended extends StatefulWidget { - const CustomExpended({Key? key}) : super(key: key); - - @override - _CustomExpendedState createState() => _CustomExpendedState(); -} - -class _CustomExpendedState extends State { - @override - Widget build(BuildContext context) { - return Column( - children: [ - buildRow([0, 0, 0]), - const SizedBox(height: 10,), - buildRow([0, 0, 1]), - const SizedBox(height: 10,), - buildRow([1, 1, 1]), - const SizedBox(height: 10,), - buildRow([2, 3, 3]), - ], - ); - } - - Widget buildRow(List num) { - return Row( - children: num.map((e) => Expanded( - flex: e, - child: Container( - alignment: Alignment.center, - width: 50, - height: 50, - color: ColorUtils.randomColor(), - child: Text( - 'flex=$e', - style: const TextStyle(color: Colors.white), - ), - ), - )).toList()); - } -} diff --git a/packages/widgets/lib/ProxyWidget/Flexible/node1_base.dart b/packages/widgets/lib/ProxyWidget/Flexible/node1_base.dart deleted file mode 100644 index 4e3bc43a0..000000000 --- a/packages/widgets/lib/ProxyWidget/Flexible/node1_base.dart +++ /dev/null @@ -1,95 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-22 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 109, -// "name": 'Flexible基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子 【Widget】\n" -// "【fit】 : 适应模式*2 【FlexFit】\n" -// "【flex】 : 剩余空间分配占比 【int】", -// } -class CustomFlexible extends StatefulWidget { - const CustomFlexible({Key? key}) : super(key: key); - - @override - _CustomFlexibleState createState() => _CustomFlexibleState(); -} - -class _CustomFlexibleState extends State { - double _width = 300.0; - bool _loose = false; - - @override - Widget build(BuildContext context) { - return Column(children: [ - Container( - color: Colors.grey.withAlpha(33), - width: _width, - padding: const EdgeInsets.all(8.0), - child: Row( - children: [ - Flexible( - flex: 2, - child: Container( - alignment: Alignment.center, - height: 50, - color: Colors.red, - child: const Text( - 'flex=2', - style: TextStyle(color: Colors.white), - ), - ), - ), - Flexible( - flex: 3, - child: Container( - alignment: Alignment.center, - height: 50, - color: Colors.blue, - child: const Text( - 'flex=3', - style: TextStyle(color: Colors.white), - ), - ), - ), - Flexible( - flex: 4, - fit: _loose?FlexFit.loose:FlexFit.tight, - child: Container( - constraints: const BoxConstraints(maxWidth: 60), - alignment: Alignment.center, - height: 50, - color: Colors.green, - child: Text( - 'flex=4 \nfit:${_loose?'loose':'tight'}', - style: const TextStyle(color: Colors.white), - ), - ), - ) - ], - )), - _buildOp() - ]); - } - - Widget _buildOp() => Row( - children: [ - Switch( - value: _loose, - onChanged: (v) => setState(() => _loose = v)), - Expanded( - child: Slider( - divisions: 10, - min: 100, - max: 350, - value: _width, - onChanged: (v) => setState(() => _width = v)), - ), - ], - ); -} diff --git a/packages/widgets/lib/ProxyWidget/IconTheme/node1_base.dart b/packages/widgets/lib/ProxyWidget/IconTheme/node1_base.dart deleted file mode 100644 index fe55c057f..000000000 --- a/packages/widgets/lib/ProxyWidget/IconTheme/node1_base.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-04-12 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 325, -// "name": 'IconTheme使用', -// "priority": 1, -// "subtitle": "可以通过IconTheme.of获取图标主题数据,也可以为IconTheme【后代】的图标组件设置默认样式,包括颜色、透明度、尺寸。", -// } -class IconThemeDemo extends StatelessWidget { - const IconThemeDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return IconTheme( - data: const IconThemeData( - color: Colors.purple, - opacity: 1.0, - size: 30 - ), - child: Wrap( - spacing: 10, - children: const[ - Icon(Icons.add), - Icon(Icons.ac_unit), - Icon(Icons.g_translate), - Icon(Icons.remove) - ], - ), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/ProxyWidget/InheritedTheme/node1_base.dart b/packages/widgets/lib/ProxyWidget/InheritedTheme/node1_base.dart deleted file mode 100644 index 2a217591e..000000000 --- a/packages/widgets/lib/ProxyWidget/InheritedTheme/node1_base.dart +++ /dev/null @@ -1,63 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 345 InheritedTheme 传承主题 -/// 它是抽象类,有非常多的 XXXTheme 相关子类,用于定义颜色、文字样式等属性,在子树中共享这些属性。 -/// link 324,326,328,329 -/// -// { -// "widgetId": 345, -// "name": 'InheritedTheme 介绍', -// "priority": 1, -// "subtitle": -// "InheritedTheme.capture 可以抓取上层主题,获取 CapturedThemes 对象,通过该对象 wrap 方法可以跨路由使用抓到的主题。", -// } - -class InheritedThemeDemo extends StatelessWidget { - const InheritedThemeDemo({Key? key}) : super(key: key); - - - @override - Widget build(BuildContext context) { - return const DefaultTextStyle( - style: TextStyle(fontSize: 24, color: Colors.blue), - child: TestBody(), - ); - } -} - -class TestBody extends StatelessWidget { - const TestBody({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - - return GestureDetector( - onTap: () => _toNextPage(context), - child: Container( - height: 60, - margin: const EdgeInsets.only(left: 40,right: 40), - alignment: Alignment.center, - color: Theme.of(context).primaryColor.withOpacity(0.1), - child: const Text('InheritedTheme'))); - } - - void _toNextPage(BuildContext context) { - // final NavigatorState navigator = Navigator.of(context); - // final CapturedThemes themes = - // InheritedTheme.capture(from: context, to: navigator.context); - // - // Navigator.of(context).push( - // MaterialPageRoute( - // builder: (BuildContext _) { - // return themes.wrap(Container( - // alignment: Alignment.center, - // color: Colors.white, - // child: Text('Flutter Unit'), - // )); - // }, - // ), - // ); - } -} diff --git a/packages/widgets/lib/ProxyWidget/InheritedWidget/node1_base.dart b/packages/widgets/lib/ProxyWidget/InheritedWidget/node1_base.dart deleted file mode 100644 index 9f0350cdf..000000000 --- a/packages/widgets/lib/ProxyWidget/InheritedWidget/node1_base.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 346 InheritedWidget 传承组件 -/// 该类是抽象类,作用是可以在本上下文存储数据,在其后续节点的上下文中共享该数据。有很多实现类,包括各种主题组件、MediaQuery等。 -/// link: 167,319,328,324,331 -/// -// { -// "widgetId": 346, -// "name": 'InheritedWidget 使用', -// "priority": 1, -// "subtitle": "【child】 : 子组件 【Widget】\n" -// "下面是一个简单的自定义 InheritedWidget,实现信息的子树共享。", -// } - -class InheritedWidgetDemo extends StatelessWidget { - final String info = - 'InheritedWidget 是一个抽象类,不可以直接使用。可以自定义对应共享数据的子类,如这里的通过 InfoInheritedWidget 实现:当前这段话可以在任意子树节点上下文获取。' - '一般都会定义一个 XXX.of(context) 的方法来获取数据,如 MediaQuery.of,Theme.of 等。'; - - const InheritedWidgetDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return InfoInheritedWidget( - info: info, - child: const InfoWidget(), - ); - } -} - -class InfoWidget extends StatelessWidget { - const InfoWidget({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - String info = InfoInheritedWidget.of(context)?.info??''; - - return Container( - color: Colors.blue.withOpacity(0.1), - padding: const EdgeInsets.all(10), - margin: const EdgeInsets.all(10), - child: Text(info), - ); - } -} - -class InfoInheritedWidget extends InheritedWidget { - final String info; - - const InfoInheritedWidget({Key? key,required this.info, required Widget child}) - : super(key: key, child: child); - - @override - bool updateShouldNotify(covariant InfoInheritedWidget oldWidget) => - info != oldWidget.info; - - static InfoInheritedWidget? of(BuildContext context) => - context.dependOnInheritedWidgetOfExactType(); -} diff --git a/packages/widgets/lib/ProxyWidget/KeepAlive/node1_base.dart b/packages/widgets/lib/ProxyWidget/KeepAlive/node1_base.dart deleted file mode 100644 index c302cc19c..000000000 --- a/packages/widgets/lib/ProxyWidget/KeepAlive/node1_base.dart +++ /dev/null @@ -1,123 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 316 KeepAlive 保活 在懒加载的列表中,孩子的状态是否需要保活。是 AutomaticKeepAlive 的底层实现,一般不单独使用。 -/// link 239 -/// -// { -// "widgetId": 316, -// "name": 'KeepAlive 介绍', -// "priority": 1, -// "subtitle": "【child】 : *子组件 【Widget】\n" -// "【keepAlive】 : *是否保活 【bool】\n" -// "在 flutter 框架层中,只用于 AutomaticKeepAlive 中,源码中也说很少单独使用它。该示例展示出 ListView 条目的状态保活。", -// } - -class KeepAliveDemo extends StatelessWidget { - KeepAliveDemo({Key? key}) : super(key: key); - - final List data = [ - Colors.purple[50]!, - Colors.purple[100]!, - Colors.purple[200]!, - Colors.purple[300]!, - Colors.purple[400]!, - Colors.purple[500]!, - Colors.purple[600]!, - Colors.purple[700]!, - Colors.purple[800]!, - Colors.purple[900]!, - Colors.red[50]!, - Colors.red[100]!, - Colors.red[200]!, - Colors.red[300]!, - Colors.red[400]!, - Colors.red[500]!, - Colors.red[600]!, - Colors.red[700]!, - Colors.red[800]!, - Colors.red[900]!, - ]; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 300, - child: ListView.builder( - itemCount: data.length, - itemBuilder: (_, index) => ColorBox( - color: data[index], - index: index, - ), - ), - ); - } -} - -class ColorBox extends StatefulWidget { - final Color color; - final int index; - - const ColorBox({ - Key? key, - required this.color, - required this.index, - }) : super(key: key); - - @override - _ColorBoxState createState() => _ColorBoxState(); -} - -class _ColorBoxState extends State with AutomaticKeepAliveClientMixin { - bool _checked = false; - - @override - void initState() { - super.initState(); - _checked = false; - print('-----_ColorBoxState#initState---${widget.index}-------'); - } - - @override - void dispose() { - print('-----_ColorBoxState#dispose---${widget.index}-------'); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - super.build(context); - - return Container( - alignment: Alignment.center, - height: 50, - color: widget.color, - child: Row( - children: [ - const SizedBox(width: 60), - Checkbox( - value: _checked, - onChanged: (bool? v) { - setState(() { - _checked = v??false; - }); - }, - ), - Text( - "index ${widget.index}: ${colorString(widget.color)}", - style: const TextStyle(color: Colors.white, shadows: [ - Shadow(color: Colors.black, offset: Offset(.5, .5), blurRadius: 2) - ]), - ), - ], - ), - ); - } - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; - - @override - bool get wantKeepAlive => true; -} diff --git a/packages/widgets/lib/ProxyWidget/LayoutId/node1_base.dart b/packages/widgets/lib/ProxyWidget/LayoutId/node1_base.dart deleted file mode 100644 index 0e9c48ca5..000000000 --- a/packages/widgets/lib/ProxyWidget/LayoutId/node1_base.dart +++ /dev/null @@ -1,93 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/6/6 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 315, -// "name": 'LayoutId使用场景', -// "priority": 1, -// "subtitle": -// "【id】 : 标识id 【Object】\n" -// "【child】 : 子组件 【Widget】", -// } - - -class LayoutIdDemo extends StatelessWidget { - const LayoutIdDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - width: 300, - height: 150, - color: Colors.grey.withAlpha(33), - child: CustomMultiChildLayout( - delegate: CornerCustomMultiChildLayout( - padding:const EdgeInsets.only(left: 10,top: 5,right: 10,bottom: 5), - ), - children: [ - LayoutId(id: CornerType.topLeft, child: const Box50(Colors.red)), - LayoutId(id: CornerType.topRight, child: const Box50(Colors.yellow)), - LayoutId(id: CornerType.bottomLeft, child: const Box50(Colors.blue)), - LayoutId(id: CornerType.bottomRight, child: const Box50(Colors.green)), - ], - ), - ); - } -} - -// 50 颜色盒 -class Box50 extends StatelessWidget { - final Color color; - const Box50(this.color, {Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - width: 50, - height: 50, - color: color, - ); - } -} - - -enum CornerType{ - topLeft, - topRight, - bottomLeft, - bottomRight -} - - -class CornerCustomMultiChildLayout extends MultiChildLayoutDelegate{ - final EdgeInsets padding; - - CornerCustomMultiChildLayout({this.padding = EdgeInsets.zero}); - - @override - void performLayout(Size size) { - if (hasChild(CornerType.topLeft)) { - layoutChild(CornerType.topLeft, BoxConstraints.loose(size)); - positionChild(CornerType.topLeft, Offset.zero.translate(padding.left, padding.top)); - } - if (hasChild(CornerType.topRight)) { - var childSize = layoutChild(CornerType.topRight, BoxConstraints.loose(size)); - positionChild(CornerType.topRight, Offset(size.width-childSize.width,0).translate(-padding.right, padding.top)); - } - if (hasChild(CornerType.bottomLeft)) { - var childSize = layoutChild(CornerType.bottomLeft, BoxConstraints.loose(size)); - positionChild(CornerType.bottomLeft, Offset(0,size.height-childSize.height).translate(padding.left, -padding.bottom)); - } - if (hasChild(CornerType.bottomRight)) { - var childSize = layoutChild(CornerType.bottomRight, BoxConstraints.loose(size)); - positionChild(CornerType.bottomRight, Offset(size.width-childSize.width,size.height-childSize.height).translate(-padding.right, -padding.bottom)); - } - } - - @override - bool shouldRelayout(CornerCustomMultiChildLayout oldDelegate) => oldDelegate.padding!=padding; -} - diff --git a/packages/widgets/lib/ProxyWidget/ListTileTheme/node1_base.dart b/packages/widgets/lib/ProxyWidget/ListTileTheme/node1_base.dart deleted file mode 100644 index f18b56c11..000000000 --- a/packages/widgets/lib/ProxyWidget/ListTileTheme/node1_base.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/7/22 -/// contact me by email 1981462002@qq.com -/// 说明: 334 ListTileTheme 主要用于为后代的ListTile组件统一设置默认属性,也可以通过该组件获取默认ListTile的属性。 -// { -// "widgetId": 334, -// "name": "ListTileTheme基本使用", -// "priority": 1, -// "subtitle": "可指定ListTileThemeData数据属性为【后代】的ListTile组件设置默认样式,如样式、颜色、装饰、边距等。也可以用ListTileTheme.of获取ListTile的主题属性。", -// } - -class ListTileThemeDemo extends StatelessWidget { - const ListTileThemeDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return ListTileTheme( - dense: false, - style: ListTileStyle.list, - selectedColor: Colors.blue, - contentPadding: const EdgeInsets.only(left: 15,right: 15,top: 5,bottom: 5), - iconColor: Colors.purple, - textColor: Colors.orange, - child: _ListTileSimple(), - ); - } -} - -class _ListTileSimple extends StatefulWidget { - @override - _ListTileSimpleState createState() => _ListTileSimpleState(); -} - -class _ListTileSimpleState extends State<_ListTileSimple> { - bool _selected = false; - - @override - Widget build(BuildContext context) { - return Container( - margin: const EdgeInsets.all(10), - color: Colors.grey.withAlpha(22), - child: ListTile( - leading: Image.asset("assets/images/icon_head.webp"), - selected: _selected, - title: const Text("以梦为马"), - subtitle: const Text("海子"), - trailing: const Icon(Icons.more_vert), - onTap: () => setState(() => _selected = !_selected), - ), - ); - } -} diff --git a/packages/widgets/lib/ProxyWidget/MaterialBannerTheme/node1_base.dart b/packages/widgets/lib/ProxyWidget/MaterialBannerTheme/node1_base.dart deleted file mode 100644 index a6a11a321..000000000 --- a/packages/widgets/lib/ProxyWidget/MaterialBannerTheme/node1_base.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/7/22 -/// contact me by email 1981462002@qq.com -/// 说明: 327 MaterialBannerTheme 主要用于为后代的MaterialBanner组件统一设置默认属性,也可以通过该组件获取默认MaterialBanner的属性。 -// { -// "widgetId": 327, -// "name": "MaterialBannerTheme基本使用", -// "priority": 1, -// "subtitle": "可指定MaterialBannerThemeData数据属性为【后代】的MaterialBanner组件设置默认样式,如背景色、边距、文字样式等。也可以用MaterialBannerTheme.of获取MaterialBanner的主题数据。", -// } -class MaterialBannerThemeDemo extends StatelessWidget { - const MaterialBannerThemeDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return MaterialBannerTheme( - data: MaterialBannerTheme.of(context).copyWith( - backgroundColor: Colors.purple, - padding: const EdgeInsetsDirectional.only(start: 16.0, top: 2.0,end: 2), - leadingPadding:const EdgeInsetsDirectional.only(end: 16.0) , - contentTextStyle: const TextStyle(color: Colors.white), - ), - child: _MaterialBannerDemo(), - ); - } -} - - -class _MaterialBannerDemo extends StatelessWidget { - final info = - 'A banner displays an important, succinct message, and provides actions for users to address. ' - 'A user action is required for itto be dismissed.'; - - @override - Widget build(BuildContext context) { - return Column( - children: [MaterialBanner( - content: Text(info), - leading: const Icon(Icons.warning, color: Colors.yellow), - actions: [ - ElevatedButton( - style: ElevatedButton.styleFrom(backgroundColor: Colors.white), - onPressed: () {}, - child: const Text( - 'I KNOW', - style: TextStyle( - color: Colors.purple, - fontWeight: FontWeight.bold, - fontSize: 14), - ), - ), - ElevatedButton( - style: ElevatedButton.styleFrom(backgroundColor: Colors.white), - onPressed: () {}, - child: const Text( - 'I IGNORE', - style: TextStyle( - color: Colors.purple, - fontWeight: FontWeight.bold, - fontSize: 14), - ), - ), - ], - )], - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/ProxyWidget/MediaQuery/node1_base.dart b/packages/widgets/lib/ProxyWidget/MediaQuery/node1_base.dart deleted file mode 100644 index eaf5474f1..000000000 --- a/packages/widgets/lib/ProxyWidget/MediaQuery/node1_base.dart +++ /dev/null @@ -1,72 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 167, -// "name": 'MediaQuery获取数据信息', -// "priority": 1, -// "subtitle": "MediaQuery.of(context)可以获取MediaQueryData", -// } - -class CustomMediaQuery extends StatelessWidget { - const CustomMediaQuery({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - MediaQueryData queryData = MediaQuery.of(context); - Map data = { - "size": queryData.size, - "devicePixelRatio": queryData.devicePixelRatio.toStringAsFixed(1), - "textScaleFactor": queryData.textScaleFactor.toStringAsFixed(1), - "platformBrightness": queryData.platformBrightness, - "padding": queryData.padding, - "viewInsets": queryData.viewInsets, - "systemGestureInsets": queryData.padding, - "viewPadding": queryData.padding, - "physicalDepth": queryData.padding, - "alwaysUse24HourFormat": queryData.padding, - "accessibleNavigation": queryData.alwaysUse24HourFormat, - "invertColors": queryData.invertColors, - "highContrast": queryData.highContrast, - "disableAnimations": queryData.disableAnimations, - "boldText": queryData.boldText, - }; - - return Container( - height: 200, - color: Colors.grey.withAlpha(11), - child:ListView( - children: data.keys.map((e) => buildItem(e, data)).toList(), - ), - ); - } - - Widget buildItem(String e, Map data) => Column( - children: [ - Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - e, - style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), - ), - Text( - data[e].toString(), - style: const TextStyle(fontSize: 16, color: Colors.orange), - ) - ], - ), - ), - const Divider( - height: 1, - ) - ], - ); -} - - diff --git a/packages/widgets/lib/ProxyWidget/ParentDataWidget/node1_base.dart b/packages/widgets/lib/ProxyWidget/ParentDataWidget/node1_base.dart deleted file mode 100644 index 76ca7bf23..000000000 --- a/packages/widgets/lib/ProxyWidget/ParentDataWidget/node1_base.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 347 ParentDataWidget 父数据组件 -/// 抽象类,用于将 ParentData 信息挂钩到 RenderObjectWidget 子组件上。其子类有 Positioned、Flexible、Expanded等,这些组件只能用于特定的组件之下。 -/// -// { -// "widgetId": 347, -// "name": 'ParentDataWidget 介绍', -// "priority": 1, -// "subtitle": "【child】 : 子组件 【Widget】", -// } - -class ParentDataWidgetDemo extends StatelessWidget { - const ParentDataWidgetDemo({Key? key}) : super(key: key); - - final String info = - 'ParentDataWidget 是一个抽象类,不能直接使用,它拥有 ParentData 子类型的泛型,该泛型会限定该组件的适应场景。' - '如 Positioned 组件继承自 ParentDataWidget,就说明 Positioned 的上层组件必须使用 Stack 族组件。' - '如 Flexible 组件继承自 ParentDataWidget,就说明 Flexible 的上层组件必须使用 Flex 族组件。'; - - @override - Widget build(BuildContext context) { - return Container( - color: Colors.blue.withOpacity(0.1), - padding: const EdgeInsets.all(10), - margin: const EdgeInsets.all(10), - child: Text(info), - ); - } -} diff --git a/packages/widgets/lib/ProxyWidget/PopupMenuTheme/node1_base.dart b/packages/widgets/lib/ProxyWidget/PopupMenuTheme/node1_base.dart deleted file mode 100644 index 96f01b2f8..000000000 --- a/packages/widgets/lib/ProxyWidget/PopupMenuTheme/node1_base.dart +++ /dev/null @@ -1,73 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/7/22 -/// contact me by email 1981462002@qq.com -/// 说明: 330 PopupMenuTheme 主要用于为后代的PopupMenuButton组件统一设置默认属性,也可以通过该组件获取默认PopupMenu的属性。 -// { -// "widgetId": 330, -// "name": "PopupMenuTheme基本使用", -// "priority": 1, -// "subtitle": "可指定PopupMenuThemeData数据属性为【后代】的PopupMenuButton组件设置默认样式,如形状、影深、颜色、文字样式等。也可以用PopupMenuTheme.of获取PopupMenu的主题数据。", -// } -class PopupMenuThemeDemo extends StatelessWidget { - const PopupMenuThemeDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return PopupMenuTheme( - data: PopupMenuTheme.of(context).copyWith( - color: Colors.orangeAccent, - elevation: 1, - textStyle: const TextStyle(color: Colors.white), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(20), - bottomRight: Radius.circular(20), - topRight: Radius.circular(5), - bottomLeft: Radius.circular(5), - )), - ), - child: _PopupMenuButtonSimple(), - ); - } -} - -class _PopupMenuButtonSimple extends StatefulWidget { - @override - _PopupMenuButtonSimpleState createState() => _PopupMenuButtonSimpleState(); -} - -class _PopupMenuButtonSimpleState extends State<_PopupMenuButtonSimple> { - final Map map = { - "关于": Icons.info_outline, - "帮助": Icons.help_outline, - "反馈": Icons.add_comment, - }; - - @override - Widget build(BuildContext context) { - return PopupMenuButton( - itemBuilder: (context) => buildItems(), - offset: const Offset(0, 50), - onSelected: print, - onCanceled: () => print('onCanceled'), - ); - } - - List> buildItems() { - return map.keys - .toList() - .map((e) => PopupMenuItem( - value: e, - child: Wrap( - spacing: 6, - children: [ - Icon( - map[e], - ), - Text(e), - ], - ))) - .toList(); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/ProxyWidget/Positioned/node1_base.dart b/packages/widgets/lib/ProxyWidget/Positioned/node1_base.dart deleted file mode 100644 index 99b7c8530..000000000 --- a/packages/widgets/lib/ProxyWidget/Positioned/node1_base.dart +++ /dev/null @@ -1,63 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-22 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 108, -// "name": 'Positioned基本使用', -// "priority": 1, -// "subtitle": "【child】 : 组件 【Widget】\n" -// "【top】 : 到父顶距离 【double】\n" -// "【right】 : 到父右距离 【double】\n" -// "【left】 : 到父左距离 【double】\n" -// "【bottom】 : 到父底距离 【double】", -// } -class CustomPositioned extends StatelessWidget { - const CustomPositioned({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - Widget yellowBox = Container( - color: Colors.yellow, - height: 100, - width: 100, - ); - - Widget redBox = Container( - color: Colors.red, - height: 90, - width: 90, - ); - - Widget greenBox = Container( - color: Colors.green, - height: 80, - width: 80, - ); - - Widget cyanBox = Container( - color: Colors.cyanAccent, - height: 70, - width: 70, - ); - - return Container( - width: 200, - height: 120, - color: Colors.grey.withAlpha(33), - child: Stack( - children: [ - yellowBox, - redBox, - Positioned(top: 20, left: 20, child: greenBox), - Positioned( - child: cyanBox, - bottom: 10, - right: 10, - ) - ], - )); - } -} diff --git a/packages/widgets/lib/ProxyWidget/PrimaryScrollController/node1_base.dart b/packages/widgets/lib/ProxyWidget/PrimaryScrollController/node1_base.dart deleted file mode 100644 index 02dff47f3..000000000 --- a/packages/widgets/lib/ProxyWidget/PrimaryScrollController/node1_base.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/3/31 -/// contact me by email 1981462002@qq.com -/// -/// 说明: 335 PrimaryScrollController 5 初始滑动控制器 它是 InheritedWidget 子类,通过 context 向子树中的可滑动视图提供默认的 ScrollController 对象。 -// { -// "widgetId": 335, -// "name": 'PrimaryScrollController 介绍', -// "priority": 1, -// "subtitle": "【controller】 : 滑动控制器 【ScrollController】\n" -// "【child】 : 子组件 【Widget】", -// } -class PrimaryScrollControllerDemo extends StatelessWidget { - const PrimaryScrollControllerDemo({Key? key}) : super(key: key); - - final String info = - 'PrimaryScrollController 是 InheritedWidget 子类,也就说明它可以为子树组件提供某些默认数据,' - '子树可以通过 context 来获取上层该组件的提供 ScrollController 对象。\n' - '对于一些可滑动组件 ScrollView、SingleChildScrollView、NestedScrollView 等,' - '在使用者未提供 ScrollController 时,且 primary 属性为 true 时(默认true) ,' - '会使用上层 PrimaryScrollController 组件提供的滑动控制器。\n' - '使用 MaterialApp 组件,其已经内置 PrimaryScrollController,'; - - @override - Widget build(BuildContext context) { - ScrollController? label = PrimaryScrollController.of(context); - - return Container( - color: Colors.blue.withOpacity(0.1), - padding: const EdgeInsets.all(10), - margin: const EdgeInsets.all(10), - child: Text(info+"当前其持有的滑动控制器对象: $label"), - ); - } -} diff --git a/packages/widgets/lib/ProxyWidget/ScrollConfiguration/node1_base.dart b/packages/widgets/lib/ProxyWidget/ScrollConfiguration/node1_base.dart deleted file mode 100644 index e63f3f60b..000000000 --- a/packages/widgets/lib/ProxyWidget/ScrollConfiguration/node1_base.dart +++ /dev/null @@ -1,70 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 180, -// "name": 'ScrollConfiguration基本使用', -// "priority": 1, -// "subtitle": "【child】 : 子组件 【Widget】\n" -// "【behavior】 : 滑动行为 【ScrollBehavior】\n" -// " 可以使用ScrollConfiguration让ListView无蓝色阴影", -// } -class CustomScrollConfiguration extends StatelessWidget { - CustomScrollConfiguration({Key? key}) : super(key: key); - - final List data = [ - Colors.cyan[50]!, - Colors.cyan[100]!, - Colors.cyan[200]!, - Colors.cyan[300]!, - Colors.cyan[400]!, - Colors.cyan[500]!, - Colors.cyan[600]!, - Colors.cyan[700]!, - Colors.cyan[800]!, - Colors.cyan[900]!, - ]; - - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 200, - child: ScrollConfiguration( - behavior: NoScrollBehavior(), child: _buildListView()), - ); - } - - Widget _buildListView() => ListView( - padding: const EdgeInsets.symmetric(horizontal: 5), - children: data - .map((color) => Container( - alignment: Alignment.center, - width: 100, - height: 50, - color: color, - child: Text( - colorString(color), - style: const TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2) - ]), - ), - )) - .toList(), - ); - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} - -class NoScrollBehavior extends ScrollBehavior { - @override - Widget buildViewportChrome( - BuildContext context, Widget child, AxisDirection axisDirection) => - child; -} diff --git a/packages/widgets/lib/ProxyWidget/SliderTheme/node1_base.dart b/packages/widgets/lib/ProxyWidget/SliderTheme/node1_base.dart deleted file mode 100644 index 75a7a833c..000000000 --- a/packages/widgets/lib/ProxyWidget/SliderTheme/node1_base.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-04-12 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 331, -// "name": 'SliderTheme使用', -// "priority": 1, -// "subtitle": "可通过SliderTheme.of获取Slider主题数据对象,其中包含大量属性用于对Slider的设定。" -// "可以为ButtonTheme【后代】的按钮组件设置默认样式,包括颜色、形状、尺寸等。", -// } - -class SliderThemeDemo extends StatefulWidget { - const SliderThemeDemo({Key? key}) : super(key: key); - - @override - _SliderThemeDemoState createState() => _SliderThemeDemoState(); -} - -class _SliderThemeDemoState extends State { - double _bliss = 0.5; - - @override - Widget build(BuildContext context) { - return SliderTheme( - data: SliderTheme.of(context).copyWith(activeTrackColor: Colors.orange), - child: Slider( - min: 0.0, - max: 200.0, - divisions: 10, - label: _bliss.toStringAsFixed(1), - onChanged: (double value) { - setState(() { - _bliss = value; - }); - }, - value: _bliss, - ), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/ProxyWidget/SliderTheme/node2_diy.dart b/packages/widgets/lib/ProxyWidget/SliderTheme/node2_diy.dart deleted file mode 100644 index 68c0104d2..000000000 --- a/packages/widgets/lib/ProxyWidget/SliderTheme/node2_diy.dart +++ /dev/null @@ -1,181 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-04-12 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 331, -// "name": 'SliderTheme对Slider的样式定制', -// "priority": 2, -// "subtitle": "通过thumbShape和valueIndicatorShape可以对Slider进行样式定制。" -// "注: 本例参考flutter-gallery中的SlideDemo", -// } - -class DIYSliderTheme extends StatefulWidget { - const DIYSliderTheme({Key? key}) : super(key: key); - - @override - _DIYSliderThemeState createState() => _DIYSliderThemeState(); -} - -class _DIYSliderThemeState extends State { - double _bliss = 0.5; - - @override - Widget build(BuildContext context) { - final ThemeData theme = Theme.of(context); - return SliderTheme( - data: theme.sliderTheme.copyWith( - activeTrackColor: Colors.deepPurple, - inactiveTrackColor: Colors.blue.withAlpha(55), - activeTickMarkColor: theme.colorScheme.onSurface.withOpacity(0.7), - inactiveTickMarkColor: theme.colorScheme.surface.withOpacity(0.7), - overlayColor: theme.colorScheme.onSurface.withOpacity(0.12), - thumbColor: Colors.deepPurple, - valueIndicatorColor: Colors.deepPurpleAccent, - thumbShape: _CustomThumbShape(), - valueIndicatorShape: _CustomValueIndicatorShape(), - valueIndicatorTextStyle: theme.primaryTextTheme.bodyMedium?.copyWith(color: theme.colorScheme.onSurface), - ), - child: Slider( - min: 0.0, - max: 200.0, - divisions: 10, - label: _bliss.toStringAsFixed(1), - onChanged: (double value) { - setState(() { - _bliss = value; - }); - }, - value: _bliss, - ), - ); - } -} - -class _CustomThumbShape extends SliderComponentShape { - static const double _thumbSize = 4.0; - static const double _disabledThumbSize = 3.0; - - @override - Size getPreferredSize(bool isEnabled, bool isDiscrete) { - return isEnabled - ? const Size.fromRadius(_thumbSize) - : const Size.fromRadius(_disabledThumbSize); - } - - static final Animatable sizeTween = Tween( - begin: _disabledThumbSize, - end: _thumbSize, - ); - - @override - void paint( - PaintingContext context, - Offset center, { - required Animation activationAnimation, - required Animation enableAnimation, - required bool isDiscrete, - required TextPainter labelPainter, - required RenderBox parentBox, - required SliderThemeData sliderTheme, - required TextDirection textDirection, - required double value, - required double textScaleFactor, - required Size sizeWithOverflow, - }) { - final Canvas canvas = context.canvas; - final ColorTween colorTween = ColorTween( - begin: sliderTheme.disabledThumbColor, - end: sliderTheme.thumbColor, - ); - final double size = _thumbSize * sizeTween.evaluate(enableAnimation); - final Path thumbPath = _downTriangle(size, center); - canvas.drawPath(thumbPath, - Paint()..color = colorTween.evaluate(enableAnimation) ?? Colors.blue); - } -} - -Path _upTriangle(double size, Offset thumbCenter) => - _downTriangle(size, thumbCenter, invert: true); - -Path _downTriangle(double size, Offset thumbCenter, {bool invert = false}) { - final Path thumbPath = Path(); - final double height = sqrt(3.0) / 2.0; - final double centerHeight = size * height / 3.0; - final double halfSize = size / 2.0; - final double sign = invert ? -1.0 : 1.0; - thumbPath.moveTo( - thumbCenter.dx - halfSize, thumbCenter.dy + sign * centerHeight); - thumbPath.lineTo(thumbCenter.dx, thumbCenter.dy - 2.0 * sign * centerHeight); - thumbPath.lineTo( - thumbCenter.dx + halfSize, thumbCenter.dy + sign * centerHeight); - thumbPath.close(); - return thumbPath; -} - -class _CustomValueIndicatorShape extends SliderComponentShape { - static const double _indicatorSize = 4.0; - static const double _disabledIndicatorSize = 3.0; - static const double _slideUpHeight = 30.0; - - @override - Size getPreferredSize(bool isEnabled, bool isDiscrete) { - return Size.fromRadius(isEnabled ? _indicatorSize : _disabledIndicatorSize); - } - - static final Animatable sizeTween = Tween( - begin: _disabledIndicatorSize, - end: _indicatorSize, - ); - - @override - void paint(PaintingContext context, Offset center, - {required Animation activationAnimation, - required Animation enableAnimation, - required bool isDiscrete, - required TextPainter labelPainter, - required RenderBox parentBox, - required SliderThemeData sliderTheme, - required TextDirection textDirection, - required double value, - required double textScaleFactor, - required Size sizeWithOverflow}) { - final Canvas canvas = context.canvas; - final ColorTween enableColor = ColorTween( - begin: sliderTheme.disabledThumbColor, - end: sliderTheme.valueIndicatorColor, - ); - final Tween slideUpTween = Tween( - begin: 0.0, - end: _slideUpHeight, - ); - final double size = _indicatorSize * sizeTween.evaluate(enableAnimation); - final Offset slideUpOffset = - Offset(0.0, -slideUpTween.evaluate(activationAnimation)); - final Path thumbPath = _upTriangle(size, center + slideUpOffset); - final Color paintColor = enableColor - .evaluate(enableAnimation) - ?.withAlpha((255.0 * activationAnimation.value).round()) ?? - Colors.black; - canvas.drawPath( - thumbPath, - Paint()..color = paintColor, - ); - canvas.drawLine( - center, - center + slideUpOffset, - Paint() - ..color = paintColor - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0); - labelPainter.paint( - canvas, - center + - slideUpOffset + - Offset(-labelPainter.width / 2.0, -labelPainter.height - 4.0)); - } -} diff --git a/packages/widgets/lib/ProxyWidget/TableCell/node1_base.dart b/packages/widgets/lib/ProxyWidget/TableCell/node1_base.dart deleted file mode 100644 index 3d409381d..000000000 --- a/packages/widgets/lib/ProxyWidget/TableCell/node1_base.dart +++ /dev/null @@ -1,79 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 317 TableCell 表室 必须在 Table 组件的后代中使用,用于控制表孩子的竖直方向对齐方式,并没是什么太大的作用。 -// { -// "widgetId": 317, -// "name": 'TableCell基本使用', -// "priority": 1, -// "subtitle": "【child】 : 组件 【Widget】\n" -// "【verticalAlignment】 : 竖直对齐方式 【TableCellVerticalAlignment】", -// } - -class TableCellDemo extends StatelessWidget { - const TableCellDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - _ItemBean title = _ItemBean("单位称", "量纲", "单位", "单位名称", "单位符号"); - _ItemBean m = _ItemBean("长度", "L", "1m", "米", "m"); - _ItemBean kg = _ItemBean("质量", "M", "1Kg", "千克", "Kg"); - _ItemBean s = _ItemBean("时间", "T", "1s", "秒", "s"); - _ItemBean a = _ItemBean("安培", "Ι", "1A", "安培", "A"); - _ItemBean k = _ItemBean("热力学温度", "θ", "1K", "开尔文", "K"); - _ItemBean mol = _ItemBean("物质的量", "N", "1mol", "摩尔", "mol"); - _ItemBean cd = _ItemBean("发光强度", "J", "1cd", "坎德拉", "cd"); - - List<_ItemBean> data = [title, m, kg, s, a, k, mol, cd]; - - return SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Table( - columnWidths: const { - 0: FixedColumnWidth(80.0), - 1: FixedColumnWidth(80.0), - 2: FixedColumnWidth(80.0), - 3: FixedColumnWidth(80.0), - 4: FixedColumnWidth(80.0), - }, - defaultVerticalAlignment: TableCellVerticalAlignment.middle, - border: TableBorder.all( - color: Colors.orangeAccent, width: 1.0, style: BorderStyle.solid), - children: data - .map((item) => TableRow(children: [ - TableCell( - verticalAlignment: TableCellVerticalAlignment.bottom, - child: Text( - item.name, - style: const TextStyle(color: Colors.blue), - )), - TableCell( - verticalAlignment: TableCellVerticalAlignment.baseline, - child: Text(item.symbol)), - TableCell( - verticalAlignment: TableCellVerticalAlignment.top, - child: Text(item.unitSymbol)), - TableCell( - verticalAlignment: TableCellVerticalAlignment.fill, - child: Text(item.unitName)), - TableCell( - verticalAlignment: TableCellVerticalAlignment.middle, - child: SizedBox(height: 30, child: Text(item.unit)), - ), - ])) - .toList(), - ), - ); - } -} - -class _ItemBean { - String name; - String symbol; - String unit; - String unitName; - String unitSymbol; - - _ItemBean(this.name, this.symbol, this.unit, this.unitName, this.unitSymbol); -} diff --git a/packages/widgets/lib/ProxyWidget/ToggleButtonsTheme/node1_base.dart b/packages/widgets/lib/ProxyWidget/ToggleButtonsTheme/node1_base.dart deleted file mode 100644 index e43562d4d..000000000 --- a/packages/widgets/lib/ProxyWidget/ToggleButtonsTheme/node1_base.dart +++ /dev/null @@ -1,57 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/7/22 -/// contact me by email 1981462002@qq.com -/// 说明: 332 ToggleButtonsTheme 主要用于为后代的ToggleButtons组件统一设置默认属性,也可以通过该组件获取默认ToggleButtons的属性。 -// { -// "widgetId": 332, -// "name": "ToggleButtonsTheme基本使用", -// "priority": 1, -// "subtitle": "可指定ToggleButtonsThemeData数据属性为【后代】的ToggleButtons组件设置默认样式,如边框样式、颜色、装饰等。也可以用ToggleButtonsTheme.of获取ToggleButtons的主题数据。", -// } - -class ToggleButtonsThemeDemo extends StatelessWidget { - const ToggleButtonsThemeDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return ToggleButtonsTheme( - data: ToggleButtonsTheme.of(context).copyWith( - borderWidth: 1, - borderColor: Colors.orangeAccent, - selectedBorderColor: Colors.blue, - splashColor: Colors.purple.withAlpha(66), - borderRadius: BorderRadius.circular(10), - selectedColor: Colors.red, - fillColor: Colors.green.withAlpha(11), - ), - child: _ToggleButtonsSimple(), - ); - } -} - - -class _ToggleButtonsSimple extends StatefulWidget { - @override - _ToggleButtonsSimpleState createState() => _ToggleButtonsSimpleState(); -} - -class _ToggleButtonsSimpleState extends State<_ToggleButtonsSimple> { - var _isSelected = [true, false, false]; - - @override - Widget build(BuildContext context) { - return ToggleButtons( - children: const[ - Icon(Icons.skip_previous), - Icon(Icons.pause), - Icon(Icons.skip_next), - ], - isSelected: _isSelected, - onPressed: (value) => setState(() { - _isSelected = _isSelected.map((e) => false).toList(); - _isSelected[value] = true; - }), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/ProxyWidget/TooltipTheme/node1_base.dart b/packages/widgets/lib/ProxyWidget/TooltipTheme/node1_base.dart deleted file mode 100644 index 931a17730..000000000 --- a/packages/widgets/lib/ProxyWidget/TooltipTheme/node1_base.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/7/22 -/// contact me by email 1981462002@qq.com -/// 说明: 333 TooltipTheme 5 主要用于为后代的Tooltip组件统一设置默认属性,也可以通过该组件获取默认TooltipTheme的属性。 -// { -// "widgetId": 333, -// "name": "TooltipTheme基本使用", -// "priority": 1, -// "subtitle": "可指定TooltipThemeData数据属性为【后代】的Tooltip组件设置默认样式,如装饰、文字样式、显示时长、边距等。也可以用TooltipTheme.of获取Tooltip的主题属性。", -// } - -class TooltipThemeDemo extends StatelessWidget { - const TooltipThemeDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return TooltipTheme( - child: const TempTooltip(), - data: TooltipTheme.of(context).copyWith( - preferBelow: false, - padding: const EdgeInsets.all(5), - verticalOffset: 20, - margin: const EdgeInsets.all(2), - textStyle: const TextStyle(color: Colors.red, shadows: [ - Shadow(color: Colors.white, offset: Offset(1, 1)), - ]), - decoration: const BoxDecoration(boxShadow: [ - BoxShadow( - color: Colors.orangeAccent, - offset: Offset(1, 1), - blurRadius: 8) - ]))); - } -} - -class TempTooltip extends StatelessWidget { - const TempTooltip({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: const [ - Tooltip(message: "天王盖地虎", child: Icon(Icons.info_outline)), - Tooltip(message: "宝塔镇河妖", child: Icon(Icons.info_outline)), - ], - ); - } -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/AbsorbPointer/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/AbsorbPointer/node1_base.dart deleted file mode 100644 index e0c7edc81..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/AbsorbPointer/node1_base.dart +++ /dev/null @@ -1,60 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-04-19 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 295, -// "name": 'AbsorbPointer基本使用', -// "priority": 1, -// "subtitle": "【child】 : 孩子组件 【Widget】\n" -// "【absorbing】 : 是否吸收事件 【bool】\n" -// "如下,Switch选中时absorbing为true,按钮事件将被吸收,无法点击。", -// } - -class CustomAbsorbPointer extends StatefulWidget { - const CustomAbsorbPointer({Key? key}) : super(key: key); - - @override - _CustomAbsorbPointerState createState() => _CustomAbsorbPointerState(); -} - -class _CustomAbsorbPointerState extends State { - bool _absorbing = false; - - @override - Widget build(BuildContext context) { - return Wrap( - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - GestureDetector( - onTap: (){ - print('AbsorbPointer'); - }, - child: AbsorbPointer( - absorbing: _absorbing, - child: _buildButton(), - ), - ), - _buildSwitch(), - Text(!_absorbing ? '允许点击' : '事件已被吸收') - ], - ); - } - - Widget _buildButton() => ElevatedButton( - child: const Text( - 'To About', - style: TextStyle(color: Colors.white), - ), - onPressed: () => Navigator.of(context).pushNamed('AboutMePage')); - - Widget _buildSwitch() => Switch( - value: _absorbing, - onChanged: (v) { - setState(() { - _absorbing = v; - }); - }); -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/Align/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/Align/node1_base.dart deleted file mode 100644 index a7f2e4341..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/Align/node1_base.dart +++ /dev/null @@ -1,63 +0,0 @@ - -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-04-19 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 85, -// "name": 'Align基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】\n" -// "【alignment】 : 对齐方式 【AlignmentGeometry】", -// } -class CustomAlign extends StatelessWidget { - const CustomAlign({Key? key}) : super(key: key); - - final List alignments = const [ - Alignment.topLeft, - Alignment.topCenter, - Alignment.topRight, - Alignment.centerLeft, - Alignment.center, - Alignment.centerRight, - Alignment.bottomLeft, - Alignment.bottomCenter, - Alignment.bottomRight, - ]; - - final List alignmentsInfo = const [ - "topLeft", - "topCenter", - "topRight", - "centerLeft", - "center", - "centerRight", - "bottomLeft", - "bottomCenter", - "bottomRight", - ]; - - @override - Widget build(BuildContext context) { - return Wrap( - children: alignments - .toList() - .map((mode) => Column(children: [ - Container( - margin: const EdgeInsets.all(5), - width: 100, - height: 60, - color: Colors.grey.withAlpha(88), - child: Align( - child: Container( - width: 30, - height: 30, - color: Colors.cyanAccent, - ), - alignment: mode)), - Text(alignmentsInfo[alignments.indexOf(mode)]) - ])) - .toList()); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/Align/node2_other.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/Align/node2_other.dart deleted file mode 100644 index c8751eea4..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/Align/node2_other.dart +++ /dev/null @@ -1,81 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-04-19 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 85, -// "name": 'Align其他用法', -// "priority": 2, -// "subtitle": -// "由于Alignment对象可指定在父容器中宽高的分率位置\n" -// "可以使用Align实现一些复杂的排布需求,比如按指定的数学方程变化位置", -// } -class Ball extends StatelessWidget { - const Ball({ - Key? key, - this.radius = 15, - this.color = Colors.blue, - }) : super(key: key); - final double radius; //半径 - final Color color; //颜色 - - @override - Widget build(BuildContext context) { - return Container( - width: radius * 2, - height: radius * 2, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: color, - ), - ); - } -} - -class SinLayout extends StatefulWidget { - const SinLayout({ - Key? key, - }) : super(key: key); - - @override - _SinLayoutState createState() => _SinLayoutState(); -} - -class _SinLayoutState extends State { - double _x = 0.0; //Alignment坐标系上的x坐标 - - @override - Widget build(BuildContext context) { - var item = Container( - width: 300, - height: 120, - color: Colors.black.withAlpha(10), - child: Align( - child: const Ball( - color: Colors.orangeAccent, - ), - alignment: Alignment(_x, f(_x * pi)), - ), - ); - - var slider = Slider( - max: 180, - min: -180, - divisions: 360, - label: "${_x.toStringAsFixed(2)}π", - value: _x * 180, - onChanged: (v) => setState(() => _x = v / 180)); - return Column( - mainAxisSize: MainAxisSize.min, - children: [slider, item], - ); - } - - double f(x) { - //映射函数 -- 可随意指定 - double y = sin(x); - return y; - } -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/AnimatedSize/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/AnimatedSize/node1_base.dart deleted file mode 100644 index 721d122f9..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/AnimatedSize/node1_base.dart +++ /dev/null @@ -1,76 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-04-01 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 201, -// "name": 'AnimatedSize基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】\n" -// "【duration】 : 动画时长 【Duration】\n" -// "【alignment】 : 对齐方式 【AlignmentGeometry】\n" -// "【curve】 : 动画曲线 【Duration】\n" -// "【vsync】 : vsync 【TickerProvider】", -// } - -class CustomAnimatedSize extends StatefulWidget { - const CustomAnimatedSize({Key? key}) : super(key: key); - - @override - _CustomAnimatedSizeState createState() => _CustomAnimatedSizeState(); -} - -class _CustomAnimatedSizeState extends State - with SingleTickerProviderStateMixin { - final double start = 100; - final double end = 200; - - late double _width; - - @override - void initState() { - _width = start; - super.initState(); - } - - @override - Widget build(BuildContext context) { - return Column( - children: [ - _buildSwitch(), - Container( - color: Colors.grey.withAlpha(22), - width: 200, - height: 100, - alignment: Alignment.center, - child: AnimatedSize( - duration: const Duration(seconds: 1), - curve: Curves.fastOutSlowIn, - alignment: const Alignment(0, 0), - child: Container( - height: 40, - width: _width, - alignment: Alignment.center, - color: Colors.blue, - child: const Text( - '张风捷特烈', - style: TextStyle(color: Colors.white), - ), - ), - ), - ), - ], - ); - } - - Widget _buildSwitch() => Switch( - value: _width == end, - onChanged: (v) { - setState(() { - _width = v ? end : start; - }); - }); -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/AnnotatedRegion/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/AnnotatedRegion/node1_base.dart deleted file mode 100644 index a9c5b6959..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/AnnotatedRegion/node1_base.dart +++ /dev/null @@ -1,85 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 288 AnnotatedRegion 有一个泛型,源码中仅适用该组件改变状态量、导航栏样式,泛型通常为SystemUiOverlayStyle。 -// { -// "widgetId": 288, -// "name": 'AnnotatedRegion改变状态量样式', -// "priority": 1, -// "subtitle": -// "【value】 : 值 【T】\n" -// "【sized】 : 是否提供大小 【bool】\n" -// "【child】 : 子组件 【Widget】", -// } - - -class AnnotatedRegionDemo extends StatelessWidget { - const AnnotatedRegionDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - padding: const EdgeInsets.all(10), - child: ElevatedButton( - onPressed: (){ - Navigator.push(context, - MaterialPageRoute(builder: (context) => const AnnotatedRegionTestPage()), - ); - }, - child: const Text("进入 AnnotatedRegion 测试页"), - ), - ); - } -} - - -class AnnotatedRegionTestPage extends StatelessWidget{ - const AnnotatedRegionTestPage({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - const SystemUiOverlayStyle overlayStyle = SystemUiOverlayStyle( - systemNavigationBarColor: Colors.green, - // 导航栏颜色 - systemNavigationBarDividerColor: Colors.red, - statusBarColor: Colors.blue, - systemNavigationBarIconBrightness: Brightness.light, - statusBarIconBrightness: Brightness.light, - statusBarBrightness: Brightness.light, - ); - - return AnnotatedRegion( - value: overlayStyle, - child: Scaffold( - body: Column( - children: [ - Container(height: 56+30.0,color: Colors.blue, - alignment: const Alignment(0,0.55), - child: Row( - children: const [ - BackButton(color: Colors.white,), - Text("AnnotatedRegion测试",style: TextStyle(color: Colors.white,fontSize: 18),) - ], - ), - ), - const SizedBox(height: 30,), - const Text( - "上面标题栏背景颜色为蓝色\n" - "上面标题栏图标为亮调", - - style: TextStyle(color: Colors.black,fontSize: 18),), - const Spacer(), - const Text( - "下面导航栏背景颜色为绿色\n" - "下面导航栏图标为亮调", - - style: TextStyle(color: Colors.black,fontSize: 18),), - const SizedBox(height: 30,), - ], - ), - ), - ); - } -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/AspectRatio/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/AspectRatio/node1_base.dart deleted file mode 100644 index 44e61d9d7..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/AspectRatio/node1_base.dart +++ /dev/null @@ -1,65 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 77, -// "name": 'AspectRatio基本使用', -// "priority": 1, -// "subtitle": "【child】 : 孩子组件 【Widget】\n" -// "【aspectRatio】 : 宽高比例 【double】", -// } -class CustomAspectRatio extends StatefulWidget { - const CustomAspectRatio({Key? key}) : super(key: key); - - @override - _CustomAspectRatioState createState() => _CustomAspectRatioState(); -} - -class _CustomAspectRatioState extends State { - double _ratio = 0.75; - - @override - Widget build(BuildContext context) { - Widget child = Container( - alignment: Alignment.center, - color: Colors.cyanAccent, - width: 50, - height: 50, - child: const Text("Static"), - ); - - Widget box = AspectRatio( - aspectRatio: _ratio, - child: Container( - color: Colors.orange, - child: const Icon( - Icons.android, - color: Colors.white, - )), - ); - - return Column( - children: [ - _buildSlider(), - Container( - color: Colors.grey.withAlpha(22), - width: 300, - height: 100, - child: Row( - children: [child, box, child], - ), - ), - ], - ); - } - - Widget _buildSlider() => Slider( - divisions: 20, - min: 0.1, - max: 2.0, - label: _ratio.toStringAsFixed(2), - value: _ratio, - onChanged: (v) => setState(() => _ratio = v), - ); -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/BackdropFilter/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/BackdropFilter/node1_base.dart deleted file mode 100644 index 59154a5d0..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/BackdropFilter/node1_base.dart +++ /dev/null @@ -1,104 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-04-19 -/// contact me by email 1981462002@qq.com -/// 说明: - - -// { -// "widgetId": 278, -// "name": 'BackdropFilter基本使用', -// "priority": 1, -// "subtitle": "【child】 : 孩子组件 【Widget】\n" -// "【filter】 : 过滤器 【ImageFilter】\n" -// "ImageFilter.blur可以实现高斯模糊,指定x,y模糊因子。", -// } - -class CustomBackdropFilter extends StatefulWidget { - const CustomBackdropFilter({Key? key}) : super(key: key); - - @override - _CustomBackdropFilterState createState() => _CustomBackdropFilterState(); -} - -class _CustomBackdropFilterState extends State { - double _sigmaX = 1.2; - double _sigmaY = 1.2; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Stack( - children: [ - _buildImage(), - Positioned.fill( - child: ClipRect( - child: BackdropFilter( - filter: ImageFilter.blur(sigmaX: _sigmaX, sigmaY: _sigmaY), - child: Container( - color: Colors.black.withAlpha(0), - ), - ), - ), - ) - ], - ), - _buildSliders() - ], - ); - } - - Widget _buildImage() { - return Wrap( - spacing: 20, - children: [ - SizedBox( - height: 150, - width: 150, - child: Image.asset( - 'assets/images/sabar.webp', - fit: BoxFit.cover, - ), - ), - SizedBox( - height: 150, - width: 150, - child: Image.asset( - 'assets/images/wy_200x300.webp', - fit: BoxFit.cover, - ), - ), - ], - ); - } - - Widget _buildSliders() => Column( - children: [ - Slider( - min: 0, - max: 4, - value: _sigmaX, - divisions: 360, - label: 'x:' + _sigmaX.toStringAsFixed(1), - onChanged: (v) { - setState(() { - _sigmaX = v; - }); - }), - Slider( - min: 0, - max: 4, - value: _sigmaY, - divisions: 360, - label: 'beta:' + _sigmaY.toStringAsFixed(1), - onChanged: (v) { - setState(() { - _sigmaY = v; - }); - }) - ], - ); -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/Baseline/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/Baseline/node1_base.dart deleted file mode 100644 index 202321f69..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/Baseline/node1_base.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-04-19 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 75, -// "name": 'Baseline基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】\n" -// "【baseline】 : 基线位置 【double】\n" -// "【baselineType】 : 基线类型 【TextBaseline】", -// } -class CustomBaseline extends StatefulWidget { - const CustomBaseline({Key? key}) : super(key: key); - - @override - _CustomBaselineState createState() => _CustomBaselineState(); -} - -class _CustomBaselineState extends State { - double _baseline=20; - - @override - Widget build(BuildContext context) { - Widget childBox = const Text( - '你好,Flutter', - style: TextStyle(fontSize: 20, fontFamily: "Menlo"), - ); - - - Widget baseline = Baseline( - child: childBox, - baseline: _baseline, - baselineType: TextBaseline.alphabetic); - - return Column( - children: [ - _buildSlider(), - Container( - width: 100/0.618, - height: 100, - color: Colors.grey.withAlpha(22), - child: baseline, - ), - ], - ); - } - - Widget _buildSlider() => Slider( - divisions: 20, - min: 0, - max: 60, - label: _baseline.toString(), - value: _baseline, - onChanged: (v) => setState(() => _baseline = v), - ); -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/Center/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/Center/node1_base.dart deleted file mode 100644 index ff9fbfa6d..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/Center/node1_base.dart +++ /dev/null @@ -1,31 +0,0 @@ - -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-04-19 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 86, -// "name": 'Center基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】", -// } -class CustomCenter extends StatelessWidget { - const CustomCenter({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - margin: const EdgeInsets.all(5), - width: 200, - height: 100, - color: Colors.grey.withAlpha(88), - child: Center( - child: Container( - width: 80, - height: 60, - color: Colors.cyanAccent, - ))); - } -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/ClipOval/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/ClipOval/node1_base.dart deleted file mode 100644 index 83b9626ff..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/ClipOval/node1_base.dart +++ /dev/null @@ -1,41 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-04-19 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 66, -// "name": 'ClipOval基本使用', -// "priority": 1, -// "subtitle": "【child】 : 子组件 【Widget】\n" -// "【clipBehavior】 : 裁剪行为 【Clip】\n" -// "【clipper】 : 裁剪器 【CustomClipper】", -// } -class CustomClipOval extends StatelessWidget { - const CustomClipOval({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 20, - children: [ - ClipOval( - child: Image.asset( - "assets/images/wy_300x200.webp", - width: 150, - height: 100, - ), - ), - ClipOval( - child: Image.asset( - "assets/images/wy_300x200.webp", - width: 100, - height: 100, - fit: BoxFit.cover, - ), - ), - ], - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/ClipPath/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/ClipPath/node1_base.dart deleted file mode 100644 index f83627404..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/ClipPath/node1_base.dart +++ /dev/null @@ -1,71 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-04-19 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 69, -// "name": 'ClipPath基本使用', -// "priority": 1, -// "subtitle": "【child】 : 子组件 【Widget】\n" -// "【clipBehavior】 : 裁剪行为 【Clip】\n" -// "【clipper】 : 裁剪器 【CustomClipper】", -// } -class CustomClipPath extends StatelessWidget { - const CustomClipPath({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return ClipPath( - clipper: ShapeBorderClipper(shape: _StarShapeBorder()), - child: Image.asset( - "assets/images/wy_300x200.webp", - width: 150, - height: 100, - fit: BoxFit.cover, - ), - ); - } -} - -class _StarShapeBorder extends ShapeBorder { - final Path _path = Path(); - - @override - EdgeInsetsGeometry get dimensions => EdgeInsets.zero; - - @override - Path getInnerPath(Rect rect, {TextDirection? textDirection}) { - return Path(); - } - - @override - Path getOuterPath(Rect rect, {TextDirection? textDirection}) => - nStarPath(20, rect.height / 2, rect.height / 2 * 0.85, - dx: rect.width / 2, dy: rect.height / 2); - - @override - void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) {} - - Path nStarPath(int num, double R, double r, {dx = 0, dy = 0}) { - double perRad = 2 * pi / num; - double radA = perRad / 2 / 2; - double radB = 2 * pi / (num - 1) / 2 - radA / 2 + radA; - _path.moveTo(cos(radA) * R + dx, -sin(radA) * R + dy); - for (int i = 0; i < num; i++) { - _path.lineTo( - cos(radA + perRad * i) * R + dx, -sin(radA + perRad * i) * R + dy); - _path.lineTo( - cos(radB + perRad * i) * r + dx, -sin(radB + perRad * i) * r + dy); - } - _path.close(); - return _path; - } - - @override - ShapeBorder scale(double t) { - return this; - } -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/ClipRRect/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/ClipRRect/node1_base.dart deleted file mode 100644 index 7ec3a067e..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/ClipRRect/node1_base.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-04-19 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 68, -// "name": 'ClipRRect基本使用', -// "priority": 1, -// "subtitle": "【child】 : 子组件 【Widget】\n" -// "【borderRadius】 : 边线半径 【BorderRadius】\n" -// "【clipBehavior】 : 裁剪行为 【Clip】\n" -// "【clipper】 : 裁剪器 【CustomClipper】", -// } -class CustomClipRRect extends StatelessWidget { - const CustomClipRRect({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return ClipRRect( - borderRadius: const BorderRadius.all(Radius.elliptical(35, 30)), - child: Image.asset( - "assets/images/wy_300x200.webp", - width: 150, - height: 100, - ), - ); - } -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/ClipRect/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/ClipRect/node1_base.dart deleted file mode 100644 index b4bfe95fd..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/ClipRect/node1_base.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-04-19 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 67, -// "name": 'ClipRect基本使用', -// "priority": 1, -// "subtitle": "【child】 : 子组件 【Widget】\n" -// "【clipBehavior】 : 裁剪行为 【Clip】\n" -// "【clipper】 : 裁剪器 【CustomClipper】", -// } -class CustomClipRect extends StatelessWidget { - const CustomClipRect({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return ClipRect( - child: SizedBox( - height: 100, - width: 100, - child: Image.asset( - "assets/images/wy_300x200.webp", - fit: BoxFit.cover, - ), - ), - ); - } -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/ColorFiltered/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/ColorFiltered/node1_base.dart deleted file mode 100644 index c6f758f43..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/ColorFiltered/node1_base.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:widgets/utils/color_utils.dart'; - -/// create by 张风捷特烈 on 2020-04-19 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 88, -// "name": 'ColorFiltered基本使用', -// "priority": 1, -// "subtitle": "【child】 : 孩子组件 【Widget】\n" -// "【colorFilter】 : 滤色器 【ColorFilter】", -// } -class CustomColorFiltered extends StatefulWidget { - const CustomColorFiltered({Key? key}) : super(key: key); - - @override - _CustomColorFilteredState createState() => _CustomColorFilteredState(); -} - -class _CustomColorFilteredState extends State { - Color _color = Colors.blue.withAlpha(88); - - @override - Widget build(BuildContext context) { - _color = ColorUtils.randomColor(); - return Column( - children: [ - Wrap(spacing: 10, runSpacing: 10, children: [ - _buildRandomColor(), - ...BlendMode.values - .map((mode) => Column( - children: [ - _buildChild(mode), - const SizedBox(height: 10), - Text( - mode.toString().split('.')[1], - style: const TextStyle(fontSize: 10), - ) - ], - )) - .toList() - ]), - ], - ); - } - - Widget _buildChild(m) => SizedBox( - width: 58, - height: 58, - child: ColorFiltered( - child: - const Image(image: AssetImage("assets/images/icon_head.webp")), - colorFilter: ColorFilter.mode(_color, m)), - ); - - Widget _buildRandomColor() => GestureDetector( - onTap: () => setState(() {}), - child: Container( - alignment: Alignment.center, - width: 60, - height: 60, - decoration: BoxDecoration(color: _color, shape: BoxShape.circle), - child: const Text('点我'), - ), - ); -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/ColoredBox/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/ColoredBox/node1_base.dart deleted file mode 100644 index ea3c4d3b4..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/ColoredBox/node1_base.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 267 ColoredBox 在子组件的布局区域上绘制颜色,然后子组件绘制在背景色上。 -// { -// "widgetId": 267, -// "name": 'ColoredBox基本使用', -// "priority": 1, -// "subtitle": "【color】 : 组件 【Color】\n" -// "【child】 : 组件 【Widget】", -// } - -class ColoredBoxDemo extends StatelessWidget { - const ColoredBoxDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return ColoredBox( - color: Colors.red, - child: Container( - margin: const EdgeInsets.all(20), - padding: const EdgeInsets.all(20), - decoration: const BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(10)), - color: Colors.blue - ), - alignment: Alignment.center, - width: 250, - height: 100, - child: const Text( - "蓝色是加了 margin 和圆角的 Container,外层包裹红色的 ColoredBox,注意作用范围。", - style: TextStyle(color: Colors.white), - ), - ), - ); - } -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/CompositedTransformFollower/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/CompositedTransformFollower/node1_base.dart deleted file mode 100644 index 81da16149..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/CompositedTransformFollower/node1_base.dart +++ /dev/null @@ -1,119 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/3/31 -/// contact me by email 1981462002@qq.com -/// -/// 说明: 265 CompositedTransformFollower 2 合成变换跟随者,一般与 CompositedTransformTarget 组件联合使用,可以使 Overlay 伴随目标变换。 -// { -// "widgetId": 265, -// "name": "基本使用", -// "name": "CompositedTransformFollower 使用", -// "priority": 1, -// "subtitle": -// "【child】 : 子组件 【Widget】\n" -// "【link】 : 链接 【LayerLink】\n" -// "【offset】 : 偏移 【Offset】\n" -// "【targetAnchor】 : 目标锚点 【Alignment】\n" -// "【followerAnchor】 : 伴随者锚点 【Alignment】\n" -// "【showWhenUnlinked】 : 为链接是否显示 【bool】", -// } - -class CompositedTransformFollowerDemo extends StatelessWidget { - - const CompositedTransformFollowerDemo({Key? key}) : super(key: key); - - static const List colors =[Colors.red,Colors.yellow,Colors.blue,Colors.green]; - - - @override - Widget build(BuildContext context) { - return Container( - transform: Matrix4.rotationZ(-15/180*pi), - height: 250, - padding: const EdgeInsets.all(50.0), - child: ListView( - scrollDirection: Axis.horizontal, - children: [ - Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [_LogoTips(), const Text('点击图标\n显隐弹框')], - ), - ...colors.map((color) => Container(width: 80, color: color)) - ], - ), - ); - } - - -} - -class _LogoTips extends StatefulWidget { - @override - _LogoTipsState createState() => _LogoTipsState(); -} - -class _LogoTipsState extends State<_LogoTips> { - OverlayEntry? _overlayEntry; - - final LayerLink _layerLink = LayerLink(); - - bool show = false; - - OverlayEntry _createOverlayEntry() { - return OverlayEntry( - builder: (context) => Positioned( - width: 150, - child: CompositedTransformFollower( - link: _layerLink, - showWhenUnlinked: false, - offset: const Offset(0,-10), - targetAnchor: Alignment.topRight, - child: const Card( - child: Padding( - padding: EdgeInsets.all(8.0), - child: Text('我是一个 Overlay,目标组件为图标,当它变换时,我会伴随变换。'), - ), - ), - ), - )); - } - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: _toggleOverlay, - child: CompositedTransformTarget( - link: _layerLink, - child: - const FlutterLogo( - size: 80, - ), - )); - } - - void _toggleOverlay() { - if (!show) { - _showOverlay(); - } else { - _hideOverlay(); - } - show = !show; - } - - void _showOverlay() { - _overlayEntry = _createOverlayEntry(); - Overlay.of(context)?.insert(_overlayEntry!); - } - - void _hideOverlay() { - _overlayEntry?.remove(); - } - - @override - void dispose() { - _hideOverlay(); - super.dispose(); - } -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/CompositedTransformTarget/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/CompositedTransformTarget/node1_base.dart deleted file mode 100644 index 78c311697..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/CompositedTransformTarget/node1_base.dart +++ /dev/null @@ -1,111 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/3/31 -/// contact me by email 1981462002@qq.com -/// -/// 说明: 266 CompositedTransformTarget 2 合成变换目标,一般与 CompositedTransformFollower 组件联合使用,可以使 Overlay 伴随目标变换。 -// { -// "widgetId": 266, -// "name": "CompositedTransformTarget 使用", -// "priority": 1, -// "subtitle": -// "【child】 : 子组件 【Widget】\n" -// "【link】 : 链接 【LayerLink】", -// } - -class CompositedTransformTargetDemo extends StatelessWidget { - - const CompositedTransformTargetDemo({Key? key}) : super(key: key); - - static const List colors =[Colors.red,Colors.yellow,Colors.blue,Colors.green]; - - @override - Widget build(BuildContext context) { - return Container( - transform: Matrix4.rotationZ(-15/180*pi), - height: 250, - padding: const EdgeInsets.all(50.0), - child: ListView( - scrollDirection: Axis.horizontal, - children: [ - Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [_LogoTips(), const Text('点击图标\n显隐弹框')], - ), - ...colors.map((color) => Container(width: 80, color: color)) - ], - ), - ); - } - -} - -class _LogoTips extends StatefulWidget { - @override - _LogoTipsState createState() => _LogoTipsState(); -} - -class _LogoTipsState extends State<_LogoTips> { - OverlayEntry? _overlayEntry; - - final LayerLink _layerLink = LayerLink(); - - bool show = false; - - OverlayEntry _createOverlayEntry() { - return OverlayEntry( - builder: (context) => Positioned( - width: 150, - child: CompositedTransformFollower( - link: _layerLink, - showWhenUnlinked: false, - targetAnchor: Alignment.topRight, - child: const Card( - child: Padding( - padding: EdgeInsets.all(8.0), - child: Text('我是一个 Overlay,目标组件为图标,当它变换时,我会伴随变换。'), - ), - ), - ), - )); - } - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: _toggleOverlay, - child: CompositedTransformTarget( - link: _layerLink, - child: - const FlutterLogo( - size: 80, - ), - )); - } - - void _toggleOverlay() { - if (!show) { - _showOverlay(); - } else { - _hideOverlay(); - } - show = !show; - } - - void _showOverlay() { - _overlayEntry = _createOverlayEntry(); - Overlay.of(context)?.insert(_overlayEntry!); - } - - void _hideOverlay() { - _overlayEntry?.remove(); - } - - @override - void dispose() { - _hideOverlay(); - super.dispose(); - } -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/ConstrainedBox/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/ConstrainedBox/node1_base.dart deleted file mode 100644 index e0be072e8..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/ConstrainedBox/node1_base.dart +++ /dev/null @@ -1,77 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-04-19 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 80, -// "name": 'BoxConstraints基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】\n" -// "【minWidth】 : 最小宽 【double】\n" -// "【minHeight】 : 最小高 【double】\n" -// "【maxHeight】 : 最大高 【double】\n" -// "【maxWidth】 : 最大宽 【double】", -// } -class CustomConstrainedBox extends StatefulWidget { - const CustomConstrainedBox({Key? key}) : super(key: key); - - @override - _CustomConstrainedBoxState createState() => _CustomConstrainedBoxState(); -} - -class _CustomConstrainedBoxState extends State { - String _text = ''; - - @override - Widget build(BuildContext context) { - Widget child = Container( - alignment: Alignment.center, - color: Colors.cyanAccent, - width: 40, - height: 40, - child: const Text("Static"), - ); - - Widget box = ConstrainedBox( - constraints: const BoxConstraints( - minHeight: 50, - minWidth: 20, - maxHeight: 80, - maxWidth: 150, - ), - child: Container(color: Colors.orange, child: Text(_text)), - ); - return Column( - children: [ - Container( - color: Colors.grey.withAlpha(22), - width: 300, - height: 100, - child: Row( - children: [child, UnconstrainedBox(child: box), child], - ), - ), - _buildInput(), - ], - ); - } - - Widget _buildInput() { - return Padding( - padding: const EdgeInsets.all(18.0), - child: TextField( - decoration: const InputDecoration( - border: OutlineInputBorder(), - hintText: '请输入', - ), - onChanged: (v) { - setState(() { - _text = v; - }); - }, - ), - ); - } -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/CupertinoTextSelectionToolbar/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/CupertinoTextSelectionToolbar/node1_base.dart deleted file mode 100644 index d2c648f37..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/CupertinoTextSelectionToolbar/node1_base.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 299 CupertinoTextSelectionToolbar 对文本选择做出响应的 ios 风格的工具栏。 -// { -// "widgetId": 299, -// "name": '该组件无法使用', -// "priority": 1, -// "subtitle": -// "【-】 : - 【-】", -// } - -class CupertinoTextSelectionToolbarDemo extends StatelessWidget { - const CupertinoTextSelectionToolbarDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - alignment: Alignment.center, - padding: const EdgeInsets.all(10), - width: 300, - child: const Text( - "注:此组件私有构造器,外部无法使用,并没有使用价值。", - style: TextStyle(color: Colors.red, fontSize: 18), - ), - ); - } -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/CustomPaint/node1_clock.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/CustomPaint/node1_clock.dart deleted file mode 100644 index 92deaabe0..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/CustomPaint/node1_clock.dart +++ /dev/null @@ -1,205 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-04-19 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 166, -// "name": 'CustomPaint绘线图形', -// "priority": 1, -// "subtitle": -// "【painter】 : 绘画器 【CustomPainter】", -// } -class ClockPage extends StatelessWidget { - const ClockPage({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return SizedBox( - width: MediaQuery.of(context).size.width, - height: 100, - child:RepaintBoundary( - child: CustomPaint(//使用CustomPaint盛放画布 - painter: ClockPainter(), - ), - ), - ) - ; - } -} - -class ClockPainter extends CustomPainter { - late Paint _paint; - final double _radius = 3.0; //小球半径 - final Path _path = Path(); //画笔对象 - ClockPainter () { - _paint = Paint()..color= const Color(0xff45d0fd)..isAntiAlias=true; - _path.addOval(Rect.fromCircle(radius: _radius, center: const Offset(0, 0))); //小球路径 - } - - @override - void paint(Canvas canvas, Size size) { - print(size); - canvas.clipRect(Offset.zero & size); - canvas.translate(size.width/2-65*2, 0); - renderDigit(1, canvas);//渲染数字 - canvas.translate(65, 0);//平移画布 - renderDigit(9, canvas); - canvas.translate(65, 0); renderDigit(9, canvas); - canvas.translate(65, 0); renderDigit(4, canvas); - } - //渲染数字 num :要显示的数字 canvas :画布 - void renderDigit(int num, Canvas canvas) { - if (num > 10) { return; } - for (int i = 0; i < digit[num].length; i++) { - for (int j = 0; j < digit[num][j].length; j++) { - if (digit[num][i][j] == 1) { - canvas.save(); - double rX = j * 2 * (_radius + 1) + (_radius + 1); //第(i,j)个点圆心横坐标 - double rY = i * 2 * (_radius + 1) + (_radius + 1); //第(i,j)个点圆心纵坐标 - canvas.translate(rX, rY); - canvas.drawPath(_path, _paint); - canvas.restore(); - } - } - } - } - @override - bool shouldRepaint(CustomPainter oldDelegate)=> false; -} - -const digit = [ - [ - [0, 0, 1, 1, 1, 0, 0], - [0, 1, 1, 0, 1, 1, 0], - [1, 1, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [0, 1, 1, 0, 1, 1, 0], - [0, 0, 1, 1, 1, 0, 0] - ], //0 - - [ - [0, 0, 0, 1, 1, 0, 0], - [0, 1, 1, 1, 1, 0, 0], - [0, 0, 0, 1, 1, 0, 0], - [0, 0, 0, 1, 1, 0, 0], - [0, 0, 0, 1, 1, 0, 0], - [0, 0, 0, 1, 1, 0, 0], - [0, 0, 0, 1, 1, 0, 0], - [0, 0, 0, 1, 1, 0, 0], - [0, 0, 0, 1, 1, 0, 0], - [1, 1, 1, 1, 1, 1, 1] - ], //1 - [ - [0, 1, 1, 1, 1, 1, 0], - [1, 1, 0, 0, 0, 1, 1], - [0, 0, 0, 0, 0, 1, 1], - [0, 0, 0, 0, 1, 1, 0], - [0, 0, 0, 1, 1, 0, 0], - [0, 0, 1, 1, 0, 0, 0], - [0, 1, 1, 0, 0, 0, 0], - [1, 1, 0, 0, 0, 0, 0], - [1, 1, 0, 0, 0, 1, 1], - [1, 1, 1, 1, 1, 1, 1] - ], //2 - [ - [1, 1, 1, 1, 1, 1, 1], - [0, 0, 0, 0, 0, 1, 1], - [0, 0, 0, 0, 1, 1, 0], - [0, 0, 0, 1, 1, 0, 0], - [0, 0, 1, 1, 1, 0, 0], - [0, 0, 0, 0, 1, 1, 0], - [0, 0, 0, 0, 0, 1, 1], - [0, 0, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [0, 1, 1, 1, 1, 1, 0] - ], //3 - - [ - [0, 0, 0, 0, 1, 1, 0], - [0, 0, 0, 1, 1, 1, 0], - [0, 0, 1, 1, 1, 1, 0], - [0, 1, 1, 0, 1, 1, 0], - [1, 1, 0, 0, 1, 1, 0], - [1, 1, 1, 1, 1, 1, 1], - [0, 0, 0, 0, 1, 1, 0], - [0, 0, 0, 0, 1, 1, 0], - [0, 0, 0, 0, 1, 1, 0], - [0, 0, 0, 1, 1, 1, 1] - ], //4 - [ - [1, 1, 1, 1, 1, 1, 1], - [1, 1, 0, 0, 0, 0, 0], - [1, 1, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 0], - [0, 0, 0, 0, 0, 1, 1], - [0, 0, 0, 0, 0, 1, 1], - [0, 0, 0, 0, 0, 1, 1], - [0, 0, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [0, 1, 1, 1, 1, 1, 0] - ], //5 - [ - [0, 0, 0, 0, 1, 1, 0], - [0, 0, 1, 1, 0, 0, 0], - [0, 1, 1, 0, 0, 0, 0], - [1, 1, 0, 0, 0, 0, 0], - [1, 1, 0, 1, 1, 1, 0], - [1, 1, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [0, 1, 1, 1, 1, 1, 0] - ], //6 - [ - [1, 1, 1, 1, 1, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [0, 0, 0, 0, 1, 1, 0], - [0, 0, 0, 0, 1, 1, 0], - [0, 0, 0, 1, 1, 0, 0], - [0, 0, 0, 1, 1, 0, 0], - [0, 0, 1, 1, 0, 0, 0], - [0, 0, 1, 1, 0, 0, 0], - [0, 0, 1, 1, 0, 0, 0], - [0, 0, 1, 1, 0, 0, 0] - ], //7 - [ - [0, 1, 1, 1, 1, 1, 0], - [1, 1, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [0, 1, 1, 1, 1, 1, 0], - [1, 1, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [0, 1, 1, 1, 1, 1, 0] - ], //8 - [ - [0, 1, 1, 1, 1, 1, 0], - [1, 1, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [0, 1, 1, 1, 0, 1, 1], - [0, 0, 0, 0, 0, 1, 1], - [0, 0, 0, 0, 0, 1, 1], - [0, 0, 0, 0, 1, 1, 0], - [0, 0, 0, 1, 1, 0, 0], - [0, 1, 1, 0, 0, 0, 0] - ], //9 - [ - [0, 0, 0, 0], - [0, 0, 0, 0], - [0, 1, 1, 0], - [0, 1, 1, 0], - [0, 0, 0, 0], - [0, 0, 0, 0], - [0, 1, 1, 0], - [0, 1, 1, 0], - [0, 0, 0, 0], - [0, 0, 0, 0] - ] //: -]; diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/CustomPaint/node2_bezier.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/CustomPaint/node2_bezier.dart deleted file mode 100644 index 24835a5e1..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/CustomPaint/node2_bezier.dart +++ /dev/null @@ -1,175 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-28 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 166, -// "name": 'CustomPaint绘线贝塞尔曲线', -// "priority": 2, -// "subtitle": " Flutter也支持贝塞尔曲线等复杂绘制。", -// } -class PlayBezier3Page extends StatefulWidget { - const PlayBezier3Page({Key? key}) : super(key: key); - - @override - _PlayBezier3PageState createState() => _PlayBezier3PageState(); -} - -class _PlayBezier3PageState extends State { - List _pos = []; - int selectPos=0; - - @override - void initState() { - _initPoints(); - super.initState(); - } - - void _initPoints() { - _pos = []; - _pos.add(const Offset(0, 0)); - _pos.add(const Offset(60, -60)); - _pos.add(const Offset(-90, -90)); - _pos.add(const Offset(-120, -40)); - } - - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 200, - width: MediaQuery.of(context).size.width, - child: RepaintBoundary( - child: CustomPaint( - painter: BezierPainter(pos: _pos, selectPos: selectPos), - ), - ), - ); - } -} - -class BezierPainter extends CustomPainter { - late Paint _gridPaint; - late Path _gridPath; - - late Paint _mainPaint; - late Path _mainPath; - int? selectPos; - late Paint _helpPaint; - - List pos; - - BezierPainter({this.pos=const [], this.selectPos}) { - _gridPaint = Paint()..style = PaintingStyle.stroke; - _gridPath = Path(); - - _mainPaint = Paint() - ..color = Colors.orange - ..style = PaintingStyle.stroke - ..strokeWidth = 2; - _mainPath = Path(); - - _helpPaint = Paint() - ..color = Colors.purple - ..style = PaintingStyle.stroke - ..strokeWidth = 2 - ..strokeCap = StrokeCap.round; - } - - @override - void paint(Canvas canvas, Size size) { - canvas.clipRect(Offset.zero & size); - canvas.translate(size.width / 2, size.height / 2); - _drawGrid(canvas, size); //绘制格线 - _drawAxis(canvas, size); //绘制轴线 - - _mainPath.moveTo(pos[0].dx, pos[0].dy); - _mainPath.cubicTo( - pos[1].dx, pos[1].dy, pos[2].dx, pos[2].dy, pos[3].dx, pos[3].dy); - canvas.drawPath(_mainPath, _mainPaint); - _drawHelp(canvas); - _drawSelectPos(canvas); - } - - @override - bool shouldRepaint(CustomPainter oldDelegate) => false; - - void _drawGrid(Canvas canvas, Size size) { - _gridPaint - ..color = Colors.grey - ..strokeWidth = 0.5; - _gridPath = _buildGridPath(_gridPath, size); - canvas.drawPath(_buildGridPath(_gridPath, size), _gridPaint); - - canvas.save(); - canvas.scale(1, -1); //沿x轴镜像 - canvas.drawPath(_gridPath, _gridPaint); - canvas.restore(); - - canvas.save(); - canvas.scale(-1, 1); //沿y轴镜像 - canvas.drawPath(_gridPath, _gridPaint); - canvas.restore(); - - canvas.save(); - canvas.scale(-1, -1); //沿原点镜像 - canvas.drawPath(_gridPath, _gridPaint); - canvas.restore(); - } - - void _drawAxis(Canvas canvas, Size size) { - canvas.drawPoints( - PointMode.lines, - [ - Offset(-size.width / 2, 0), - Offset(size.width / 2, 0), - Offset(0, -size.height / 2), - Offset(0, size.height / 2), - Offset(0, size.height / 2), - Offset(0 - 7.0, size.height / 2 - 10), - Offset(0, size.height / 2), - Offset(0 + 7.0, size.height / 2 - 10), - Offset(size.width / 2, 0), - Offset(size.width / 2 - 10, 7), - Offset(size.width / 2, 0), - Offset(size.width / 2 - 10, -7), - ], - _gridPaint - ..color = Colors.blue - ..strokeWidth = 1.5); - } - - Path _buildGridPath(Path path, Size size, {step = 20.0}) { - for (int i = 0; i < size.height / 2 / step; i++) { - path.moveTo(0, step * i); - path.relativeLineTo(size.width / 2, 0); - } - for (int i = 0; i < size.width / 2 / step; i++) { - path.moveTo(step * i, 0); - path.relativeLineTo( - 0, - size.height / 2, - ); - } - return path; - } - - void _drawHelp(Canvas canvas) { - canvas.drawPoints(PointMode.lines, pos, _helpPaint..strokeWidth = 1); - canvas.drawPoints(PointMode.points, pos, _helpPaint..strokeWidth = 8); - } - - void _drawSelectPos(Canvas canvas) { - if (selectPos == null) return; - canvas.drawCircle( - pos[selectPos!], - 10, - _helpPaint - ..color = Colors.green - ..strokeWidth = 2); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/CustomSingleChildLayout/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/CustomSingleChildLayout/node1_base.dart deleted file mode 100644 index 4f612fd0a..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/CustomSingleChildLayout/node1_base.dart +++ /dev/null @@ -1,63 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/6/3 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 285, -// "name": 'CustomSingleChildLayout基本使用', -// "priority": 1, -// "subtitle": -// "【delegate】 : 代理 【SingleChildLayoutDelegate】", -// } - -class CustomSingleChildLayoutDemo extends StatelessWidget { - const CustomSingleChildLayoutDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - print('-------CustomSingleChildLayoutDemo------'); - return Container( - width: 300, - height: 200, - color: Colors.grey.withAlpha(11), - child: CustomSingleChildLayout( - delegate: _TolySingleChildLayoutDelegate(), - child: Container( - color: Colors.orange, - ), - ), - ); - } -} - -class _TolySingleChildLayoutDelegate extends SingleChildLayoutDelegate { - @override - bool shouldRelayout(SingleChildLayoutDelegate oldDelegate) { - return true; - } - - @override - Size getSize(BoxConstraints constraints) { - print('----getSize:----constraints:$constraints----'); - return super.getSize(constraints); - } - - @override - Offset getPositionForChild(Size size, Size childSize) { - print('----getPositionForChild: size:$size----childSize:$childSize----'); - return Offset(size.width / 2, 0); - } - - @override - BoxConstraints getConstraintsForChild(BoxConstraints constraints) { - print('----getConstraintsForChild:----constraints:$constraints----'); - return BoxConstraints( - maxWidth: constraints.maxWidth / 2, - maxHeight: constraints.maxHeight / 2, - minHeight: constraints.maxHeight / 4, - minWidth: constraints.maxWidth / 4, - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/CustomSingleChildLayout/node2_offset.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/CustomSingleChildLayout/node2_offset.dart deleted file mode 100644 index f43a815c1..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/CustomSingleChildLayout/node2_offset.dart +++ /dev/null @@ -1,121 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/6/3 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 285, -// "name": 'CustomSingleChildLayout的偏移使用', -// "priority": 2, -// "subtitle": -// "可以利用代理的偏移能力,对子组件进行偏移定位。", -// } - -class OffSetWidgetDemo extends StatelessWidget { - const OffSetWidgetDemo({Key? key}) : super(key: key); - - final List> data = const [ - { - 'offset': Offset(20, 20), - 'direction': Direction.topLeft, - }, - { - 'offset': Offset(20, -15), - 'direction': Direction.topRight, - }, - { - 'offset': Offset(-15, 20), - 'direction': Direction.bottomLeft, - }, - { - 'offset': Offset(-15, 20), - 'direction': Direction.bottomLeft, - }, - { - 'offset': Offset(15, 20), - 'direction': Direction.bottomLeft, - }, - { - 'offset': Offset(-15, -15), - 'direction': Direction.topRight, - }, - ]; - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 20, - runSpacing: 20, - children: data - .map((e) => Container( - width: 150, - height: 100, - alignment: Alignment.topRight, - color: Colors.grey.withAlpha(11), - child: OffSetWidget( - offset: e['offset'], - direction: e['direction'], - child: const Icon( - Icons.android, - size: 30, - color: Colors.green, - ), - ))) - .toList()); - } -} - -class OffSetWidget extends StatelessWidget { - final Offset offset; - final Widget child; - final Direction direction; - - const OffSetWidget( - {Key? key, this.offset = Offset.zero, - required this.child, - this.direction = Direction.topLeft}) : super(key: key); - - @override - Widget build(BuildContext context) { - return CustomSingleChildLayout( - delegate: _OffSetDelegate(offset: offset, direction: direction), - child: child, - ); - } -} - -enum Direction { topLeft, topRight, bottomLeft, bottomRight } - -class _OffSetDelegate extends SingleChildLayoutDelegate { - final Offset offset; - final Direction direction; - - _OffSetDelegate({ - this.offset = Offset.zero, - this.direction = Direction.topLeft, - }); - - @override - bool shouldRelayout(_OffSetDelegate oldDelegate) => - offset != oldDelegate.offset; - - @override - Offset getPositionForChild(Size size, Size childSize) { - double w = size.width; - double h = size.height; - double wc = childSize.width; - double hc = childSize.height; - - switch (direction) { - case Direction.topLeft: - return offset; - case Direction.topRight: - return offset.translate(w - wc - offset.dx * 2, 0); - case Direction.bottomLeft: - return offset.translate(0, h - hc - offset.dy * 2); - case Direction.bottomRight: - return offset.translate(w - wc - offset.dx * 2, h - hc - offset.dy * 2); - } - } -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/DecoratedBox/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/DecoratedBox/node1_base.dart deleted file mode 100644 index 180c3f957..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/DecoratedBox/node1_base.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-04-19 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 70, -// "name": 'DecoratedBox基本使用', -// "priority": 1, -// "subtitle": -// "【decoration】 : 装饰对象 【Decoration】\n" -// "【position】 : 前景色(左)/后景色(右) 【DecorationPosition】", -// } -class BoxDecorationDemo extends StatelessWidget { - const BoxDecorationDemo({Key? key}) : super(key: key); - - final List rainbow = const [ - 0xffff0000, - 0xffFF7F00, - 0xffFFFF00, - 0xff00FF00, - 0xff00FFFF, - 0xff0000FF, - 0xff8B00FF - ]; - - @override - Widget build(BuildContext context) { - return DecoratedBox( - position: DecorationPosition.background, - decoration: BoxDecoration( - gradient: LinearGradient( - stops: const [0.0, 1 / 6, 2 / 6, 3 / 6, 4 / 6, 5 / 6, 1.0], - colors: rainbow.map((e) => Color(e)).toList()), - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(20), - bottomRight: Radius.circular(20), - ), - boxShadow: const [ - BoxShadow( - color: Colors.orangeAccent, - offset: Offset(1, 1), - blurRadius: 10, - spreadRadius: 1), - ]), - child: Icon( - Icons.android, - size: 80, - color: Colors.black.withAlpha(123), - ), - ); - } -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/DecoratedBox/node2_image.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/DecoratedBox/node2_image.dart deleted file mode 100644 index 0170ed81c..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/DecoratedBox/node2_image.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-04-19 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 70, -// "name": 'DecoratedBox形状和图片装饰', -// "priority": 2, -// "subtitle": -// "【shape】 : 形状 【BoxShape】\n" -// "【image】 : 背景图片 【DecorationImage】\n", -// } -class ShapeImageDemo extends StatelessWidget { - const ShapeImageDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return const DecoratedBox( - decoration: BoxDecoration( - shape: BoxShape.circle, - image: DecorationImage( - fit: BoxFit.cover, - image: AssetImage( - 'assets/images/wy_200x300.webp', - ))), - child: SizedBox( - height: 80, - width: 80, - child: Icon( - Icons.ac_unit, - color: Colors.white, - size: 40, - ), - ), - ); - } -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/DecoratedBox/node3_border.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/DecoratedBox/node3_border.dart deleted file mode 100644 index 81a6dc1e0..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/DecoratedBox/node3_border.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 70, -// "name": 'DecoratedBox边线装饰', -// "priority": 3, -// "subtitle": "【border】 : 边线 【BoxBorder】\n", -// } -class BorderDemo extends StatelessWidget { - const BorderDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return DecoratedBox( - position: DecorationPosition.foreground, - decoration: const BoxDecoration( - border: Border( - bottom: BorderSide(color: Colors.orange, width: 2), - top: BorderSide(color: Colors.orange, width: 2)), - ), - child: SizedBox( - height: 80, - width: 100, - child: Image.asset( - 'assets/images/wy_200x300.webp', - fit: BoxFit.cover, - ), - ), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/DecoratedBox/node4_shape.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/DecoratedBox/node4_shape.dart deleted file mode 100644 index c7fe0d740..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/DecoratedBox/node4_shape.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 70, -// "name": 'DecoratedBox形状装饰', -// "priority": 4, -// "subtitle": "通过ShapeDecoration对象可指定边线形状\n", -// } -class ShapeDecorationDemo extends StatelessWidget { - const ShapeDecorationDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return const DecoratedBox( - decoration: ShapeDecoration( - shadows: [ - BoxShadow( - color: Colors.orangeAccent, - offset: Offset(0, 0), - blurRadius: 2, - spreadRadius: 1), - ], - image: DecorationImage( - fit: BoxFit.cover, - image: AssetImage( - 'assets/images/wy_200x300.webp', - )), - shape: CircleBorder( - side: BorderSide(width: 1.0, color: Colors.orangeAccent), - )), - child: SizedBox( - height: 100, - width: 100, - child: Icon( - Icons.ac_unit, - color: Colors.white, - size: 40, - ), - ), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/DecoratedBox/node5_line.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/DecoratedBox/node5_line.dart deleted file mode 100644 index 4befaeeea..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/DecoratedBox/node5_line.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 70, -// "name": 'DecoratedBox底线装饰', -// "priority": 5, -// "subtitle": "通过UnderlineTabIndicator对象可指定底线\n", -// } -class UnderlineTabIndicatorDemo extends StatelessWidget { - const UnderlineTabIndicatorDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return const DecoratedBox( - decoration: UnderlineTabIndicator( - insets: EdgeInsets.symmetric(horizontal: 5, vertical: -5), - borderSide: BorderSide(color: Colors.orange, width: 2)), - child: Icon( - Icons.ac_unit, - color: Colors.blue, - size: 40, - ), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/DecoratedBox/node6_flutterLogo.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/DecoratedBox/node6_flutterLogo.dart deleted file mode 100644 index 665dd42a0..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/DecoratedBox/node6_flutterLogo.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 70, -// "name": 'FlutterLogoDecoration装饰', -// "priority": 6, -// "subtitle": "通过FlutterLogoDecoration对象可指定Flutter图标装饰(并没有什么太大的作用)\n", -// } - -class FlutterLogoDecorationDemo extends StatelessWidget { - const FlutterLogoDecorationDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return const DecoratedBox( - decoration: FlutterLogoDecoration( -// darkColor: Colors.orange, -// lightColor: Colors.deepPurpleAccent, - margin: EdgeInsets.all(8), - style: FlutterLogoStyle.stacked), - child: SizedBox( - width: 100, - height: 100, - ), - ); - } -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/FadeTransition/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/FadeTransition/node1_base.dart deleted file mode 100644 index eacfaad20..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/FadeTransition/node1_base.dart +++ /dev/null @@ -1,56 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-04-19 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 89, -// "name": 'FadeTransition基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】\n" -// "【opacity】 : 动画 【Animation】", -// } -class CustomFadeTransition extends StatefulWidget { - const CustomFadeTransition({Key? key}) : super(key: key); - - @override - _CustomFadeTransitionState createState() => _CustomFadeTransitionState(); -} - -class _CustomFadeTransitionState extends State - with SingleTickerProviderStateMixin { - late AnimationController _ctrl; - - @override - void initState() { - _ctrl = AnimationController( - vsync: this, - duration: const Duration(seconds: 2), - ); - _ctrl.forward(); - super.initState(); - } - - @override - void dispose() { - _ctrl.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: () => _ctrl.forward(from: 0), - child: Container( - color: Colors.grey.withAlpha(22), - width: 100, - height: 100, - child: FadeTransition( - opacity: CurvedAnimation(parent: _ctrl, curve: Curves.linear), - child: const Icon(Icons.android, color: Colors.green, size: 60), - ), - ), - ); - } -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/FittedBox/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/FittedBox/node1_base.dart deleted file mode 100644 index b5b3ab739..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/FittedBox/node1_base.dart +++ /dev/null @@ -1,96 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-04-19 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 87, -// "name": 'FittedBox基本使用', -// "priority": 1, -// "subtitle": "【child】 : 孩子组件 【Widget】\n" -// "【fit】 : 适应模式 【BoxFit】\n" -// "【alignment】 : 对齐方式 【AlignmentGeometry】", -// } -class CustomFittedBox extends StatefulWidget { - const CustomFittedBox({Key? key}) : super(key: key); - - @override - _CustomFittedBoxState createState() => _CustomFittedBoxState(); -} - -class _CustomFittedBoxState extends State { - double _childW = 20; - double _childH = 30; - - final List rainbow = const [ - 0xffff0000, - 0xffFF7F00, - 0xffFFFF00, - 0xff00FF00, - 0xff00FFFF, - 0xff0000FF, - 0xff8B00FF - ]; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Wrap( - spacing: 10, - runSpacing: 10, - children: BoxFit.values - .map((mode) => Column( - children: [ - _buildChild(mode), - const SizedBox(height: 10), - Text(mode.toString().split('.')[1]) - ], - )) - .toList()), - _buildSlider() - ], - ); - } - - Widget _buildChild(BoxFit m) { - return Container( - color: Colors.grey.withAlpha(44), - width: 80, - height: 60, - child: FittedBox( - fit: m, - child: Container( - width: _childW, - height: _childH, - decoration: BoxDecoration( - //添加渐变色 - gradient: LinearGradient( - stops: const[0.0, 1 / 6, 2 / 6, 3 / 6, 4 / 6, 5 / 6, 1.0], - colors: rainbow.map((e) => Color(e)).toList()), - ), - ), - ), - ); - } - - Widget _buildSlider() => Column( - children: [ - Slider( - min: 10, - max: 150, - divisions: 100, - label: '子宽度:' + _childW.toStringAsFixed(1), - value: _childW, - onChanged: (v) => setState(() => _childW = v)), - Slider( - min: 10, - max: 150, - divisions: 100, - label: '子高度:' + _childH.toStringAsFixed(1), - value: _childH, - onChanged: (v) => setState(() => _childH = v)), - ], - ); -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/FractionalTranslation/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/FractionalTranslation/node1_base.dart deleted file mode 100644 index 997bf1a1b..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/FractionalTranslation/node1_base.dart +++ /dev/null @@ -1,69 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/7/22 -/// contact me by email 1981462002@qq.com -/// 说明: 263 FractionalTranslation 通过offset属性将子组件进行偏移,偏移量为OffSet横纵*子组件大小。 -// { -// "widgetId": 263, -// "name": "FractionalTranslation基本使用", -// "priority": 1, -// "subtitle": "【translation】 : 偏移分度值 【Offset】\n" -// "【child】: 子组件 【Widget】", -// } - -class FractionalTranslationDemo extends StatefulWidget { - const FractionalTranslationDemo({Key? key}) : super(key: key); - - @override - _FractionalTranslationDemoState createState() => - _FractionalTranslationDemoState(); -} - -class _FractionalTranslationDemoState extends State { - double dx = 0.0; - double dy = 0.0; - - @override - Widget build(BuildContext context) { - print(dx); - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - Container( - width: 200, - height: 100, - alignment: Alignment.topLeft, - color: Colors.grey.withAlpha(33), - child: FractionalTranslation( - translation: Offset(dx, dy), - child: const Icon( - Icons.android, - color: Colors.green, - ), - ), - ), - _buildSliderX(), - _buildSliderY() - ], - ); - } - - Widget _buildSliderX() => Slider( - min: -2.0, - max: 10.0, - value: dx, - divisions: 100, - label: 'dx:${dx.toStringAsFixed(1)}', - onChanged: (v) => setState(() => dx = v), - ); - - - Widget _buildSliderY() => Slider( - min: -2.0, - max: 6.0, - value: dy, - divisions: 100, - label: 'dy:${dy.toStringAsFixed(1)}', - onChanged: (v) => setState(() => dy = v), - ); -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/FractionallySizedBox/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/FractionallySizedBox/node1_base.dart deleted file mode 100644 index 0485ab65d..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/FractionallySizedBox/node1_base.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-04-19 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 82, -// "name": 'FractionallySizedBox基本使用', -// "priority": 1, -// "subtitle": "【child】 : 孩子组件 【Widget】\n" -// "【widthFactor】 : 宽分率 【double】\n" -// "【heightFactor】 : 高分率 【double】\n" -// "【alignment】 : 对齐方式 【AlignmentGeometry】", -// } -class CustomFractionallySizedBox extends StatefulWidget { - const CustomFractionallySizedBox({Key? key}) : super(key: key); - - @override - _CustomFractionallySizedBoxState createState() => - _CustomFractionallySizedBoxState(); -} - -class _CustomFractionallySizedBoxState - extends State { - double _hf = 0.5; - double _wf = 0.4; - - @override - Widget build(BuildContext context) { - Widget box = FractionallySizedBox( - widthFactor: _wf, - heightFactor: _hf, - alignment: Alignment.center, - child: Container(color: Colors.orange), - ); - return Column( - children: [ - Container( - color: Colors.grey.withAlpha(22), - width: 200, - height: 100, - child: box), - _buildSlider() - ], - ); - } - - Widget _buildSlider() => Column( - children: [ - Slider( - divisions: 20, - min: 0.0, - max: 2, - label: '宽分率:' + _wf.toStringAsFixed(1), - value: _wf, - onChanged: (v) => setState(() => _wf = v)), - Slider( - divisions: 20, - min: 0.0, - max: 2, - label: '高分率:' + _hf.toStringAsFixed(1), - value: _hf, - onChanged: (v) => setState(() => _hf = v)), - ], - ); -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/IgnorePointer/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/IgnorePointer/node1_base.dart deleted file mode 100644 index b819a2be3..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/IgnorePointer/node1_base.dart +++ /dev/null @@ -1,60 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-04-19 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 292, -// "name": 'IgnorePointer基本使用', -// "priority": 1, -// "subtitle": "【child】 : 孩子组件 【Widget】\n" -// "【ignoring】 : 是否忽视事件 【bool】\n" -// "如下,Switch选中时ignoring为true,按钮事件将被锁定,无法点击。", -// } - -class CustomIgnorePointer extends StatefulWidget { - const CustomIgnorePointer({Key? key}) : super(key: key); - - @override - _CustomIgnorePointerState createState() => _CustomIgnorePointerState(); -} - -class _CustomIgnorePointerState extends State { - bool _ignore = false; - - @override - Widget build(BuildContext context) { - return Wrap( - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - GestureDetector( - onTap: (){ - print('IgnorePointer'); - }, - child: IgnorePointer( - ignoring: _ignore, - child: _buildButton(), - ), - ), - _buildSwitch(), - Text(!_ignore ? '允许点击' : '点击已锁定') - ], - ); - } - - Widget _buildButton() => ElevatedButton( - child: const Text( - 'To About', - style: TextStyle(color: Colors.white), - ), - onPressed: () => Navigator.of(context).pushNamed('AboutMePage')); - - Widget _buildSwitch() => Switch( - value: _ignore, - onChanged: (v) { - setState(() { - _ignore = v; - }); - }); -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/ImageFiltered/node1_blur.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/ImageFiltered/node1_blur.dart deleted file mode 100644 index 738743f72..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/ImageFiltered/node1_blur.dart +++ /dev/null @@ -1,124 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2022/04/23 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 357, -// "name": 'ImageFilter 高斯模糊', -// "priority": 1, -// "subtitle": "【imageFilter】 : 图像滤镜 【ImageFilter】\n" -// "【child】 : 子组件 【Widget】", -// } -class ImageFilteredBlur extends StatefulWidget { - const ImageFilteredBlur({Key? key}) : super(key: key); - - @override - State createState() => _ImageFilteredBlurState(); -} - -class _ImageFilteredBlurState extends State { - double _sigmaX = 1.2; - double _sigmaY = 1.2; - TileMode _tileMode = TileMode.decal; - - @override - Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - ImageFiltered( - imageFilter: ImageFilter.blur( - sigmaX: _sigmaX, - sigmaY: _sigmaY, - tileMode: _tileMode, - ), - child: const _TargetContent(), - ), - _buildTools(), - ], - ); - } - - Widget _buildTools() => Column( - children: [ - Row( - children: [ - Expanded( - child: Slider( - min: 0, - max: 4, - value: _sigmaX, - divisions: 360, - label: 'x:' + _sigmaX.toStringAsFixed(1), - onChanged: (v) => setState(() => _sigmaX = v)), - ), - Expanded( - child: Slider( - min: 0, - max: 4, - value: _sigmaY, - divisions: 360, - label: 'y:' + _sigmaY.toStringAsFixed(1), - onChanged: (v) => setState(() => _sigmaY = v)), - ), - ], - ), - buildTileModeCheck() - ], - ); - - Widget buildTileModeCheck() => Wrap( - spacing: 10, - children: TileMode.values.map((e) { - TextStyle style; - if (e == _tileMode) { - Color color = Theme.of(context).primaryColor; - style = TextStyle(fontWeight: FontWeight.bold, color: color); - } else { - style = const TextStyle( - fontWeight: FontWeight.bold, color: Colors.grey); - } - return GestureDetector( - onTap: () => setState(() => _tileMode = e), - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 8), - child: Text( - e.toString().split('.')[1], - style: style, - ), - ), - ); - }).toList(), - ); -} - -class _TargetContent extends StatelessWidget { - const _TargetContent({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 20, - children: [ - SizedBox( - height: 150, - width: 150, - child: Image.asset( - 'assets/images/sabar.webp', - fit: BoxFit.cover, - ), - ), - SizedBox( - height: 150, - width: 150, - child: Image.asset( - 'assets/images/wy_200x300.webp', - fit: BoxFit.cover, - ), - ), - ], - ); - } -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/ImageFiltered/node2_color.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/ImageFiltered/node2_color.dart deleted file mode 100644 index d4b11d7aa..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/ImageFiltered/node2_color.dart +++ /dev/null @@ -1,144 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2022/04/23 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 357, -// "name": 'ImageFilter 滤色效果', -// "priority": 2, -// "subtitle": "通过 ColorFilter 对象实现颜色滤镜。", -// } -class ImageFilteredColor extends StatefulWidget { - const ImageFilteredColor({Key? key}) : super(key: key); - - @override - State createState() => _ImageFilteredColorState(); -} - -class _ImageFilteredColorState extends State { - - String _currentFilter = 'srgbToLinear'; - - ColorFilter greyscale = const ColorFilter.matrix([ - 0.2126, 0.7152, 0.0722, 0, 0, - 0.2126, 0.7152, 0.0722, 0, 0, - 0.2126, 0.7152, 0.0722, 0, 0, - 0, 0, 0, 1, 0, - ]); - ColorFilter sepia = const ColorFilter.matrix([ - 0.393, 0.769, 0.189, 0, 0, - 0.349, 0.686, 0.168, 0, 0, - 0.272, 0.534, 0.131, 0, 0, - 0, 0, 0, 1, 0, - ]); - ColorFilter invert = const ColorFilter.matrix([ - -1, 0, 0, 0, 255, - 0, -1, 0, 0, 255, - 0, 0, -1, 0, 255, - 0, 0, 0, 1, 0, - ]); - ColorFilter identity = const ColorFilter.matrix([ - 1, 0, 0, 0, 0, - 0, 1, 0, 0, 0, - 0, 0, 1, 0, 0, - 0, 0, 0, 1, 0, - ]); - - - ColorFilter darken = const ColorFilter.matrix([ - 1,0,0,0,-126.0, - 0,1,0,0,-126.0, - 0,0,1,0,-126.0, - 0,0,0,1,0 - ]); - - ColorFilter light = const ColorFilter.matrix([ - 1,0,0,0,90, - 0,1,0,0,90, - 0,0,1,0,90, - 0,0,0,1,0 - ]); - - late Map filterMap={ - '原图': identity, - 'light': light, - 'darken': darken, - 'greyscale': greyscale, - 'sepia': sepia, - 'invert': invert, - 'srgbToLinear':const ColorFilter.srgbToLinearGamma(), - 'linearToSrgb':const ColorFilter.linearToSrgbGamma(), - }; - - @override - Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - ImageFiltered( - imageFilter: filterMap[_currentFilter]??identity, - child: const _TargetContent(), - ), - buildTileModeCheck() - ], - ); - } - - - Widget buildTileModeCheck() => Padding( - padding: const EdgeInsets.only(top: 8.0), - child: Wrap( - children: filterMap.keys.map((e) { - TextStyle style; - if (e == _currentFilter) { - Color color = Theme.of(context).primaryColor; - style = TextStyle(fontWeight: FontWeight.bold, color: color); - } else { - style = const TextStyle( - fontWeight: FontWeight.bold, color: Colors.grey); - } - return GestureDetector( - onTap: () => setState(() => _currentFilter = e), - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 8,vertical: 2), - child: Text( - e, - style: style, - ), - ), - ); - }).toList(), - ), - ); -} - -class _TargetContent extends StatelessWidget { - const _TargetContent({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 20, - children: [ - SizedBox( - height: 150, - width: 150, - child: Image.asset( - 'assets/images/sabar.webp', - fit: BoxFit.cover, - ), - ), - SizedBox( - height: 150, - width: 150, - child: Image.asset( - 'assets/images/wy_200x300.webp', - fit: BoxFit.cover, - ), - ), - ], - ); - } -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/ImageFiltered/node3_matrix.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/ImageFiltered/node3_matrix.dart deleted file mode 100644 index 92344b7b4..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/ImageFiltered/node3_matrix.dart +++ /dev/null @@ -1,75 +0,0 @@ -import 'dart:math'; -import 'dart:ui'; - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2022/04/23 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 357, -// "name": 'ImageFilter 变换效果', -// "priority": 3, -// "subtitle": "通过 ImageFilter.matrix 构造,进行矩阵变换,但比较鸡肋。", -// } -class ImageFilteredMatrix extends StatefulWidget { - const ImageFilteredMatrix({Key? key}) : super(key: key); - - @override - State createState() => _ImageFilteredMatrixState(); -} - -class _ImageFilteredMatrixState extends State { - double _sigmaX = 0; - @override - Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - ImageFiltered( - imageFilter: ImageFilter.matrix( - Matrix4.rotationZ(_sigmaX/180*pi).storage - ), - child: const _TargetContent(), - ), - _buildTools(), - ], - ); - } - - Widget _buildTools() => Column( - children: [ - Row( - children: [ - const Text(' 旋转角度:'), - Expanded( - child: Slider( - min: 0, - max: 90, - value: _sigmaX, - divisions: 360, - label: 'x:' + _sigmaX.toStringAsFixed(1), - onChanged: (v) => setState(() => _sigmaX = v)), - ), - ], - ), - // buildTileModeCheck() - ], - ); -} - -class _TargetContent extends StatelessWidget { - const _TargetContent({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 150, - width: 150, - child: Image.asset( - 'assets/images/sabar.webp', - fit: BoxFit.cover, - ), - ); - } -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/IntrinsicHeight/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/IntrinsicHeight/node1_base.dart deleted file mode 100644 index d429de387..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/IntrinsicHeight/node1_base.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/7/21 -/// contact me by email 1981462002@qq.com -/// 说明: 298 IntrinsicHeight 根据子元素的固有高度调整其子元素大小的组件,可解决很多布局的疑难杂症,但相对昂贵。 - -// { -// "widgetId": 298, -// "name": 'IntrinsicHeight基本使用', -// "priority": 1, -// "subtitle": "【child】 : 子组件 【Widget】\n" -// "如示例:左侧高可变动,中间高固定,右侧高取前两者的最高值。", -// } - -class IntrinsicHeightDemo extends StatefulWidget { - const IntrinsicHeightDemo({Key? key}) : super(key: key); - - @override - _IntrinsicHeightDemoState createState() => _IntrinsicHeightDemoState(); -} - -class _IntrinsicHeightDemoState extends State { - double _height =120.0; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - buildChild(_height), - const SizedBox(height: 10), - _buildSlider() - ], - ); - } - - Widget buildChild(double leftHeight) { - return IntrinsicHeight( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - height: leftHeight, - width: 120, - color: Colors.yellow, - alignment: Alignment.center, - child: Text("height:${leftHeight.toStringAsFixed(1)}"), - ), - Container( - color: Colors.blue, - width: 150, - height: 80, - alignment: Alignment.center, - child: const Text("固定高"), - ), - Container( - color: Colors.red, - width: 60, - alignment: Alignment.center, - child: const Text("最高"), - ) - ], - ), - ); - } - - Widget _buildSlider() =>Slider( - value: _height, - max: 200.0, - min: 30.0, - divisions: 17, - onChanged: (v)=> setState(() => _height= v), - ); -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/IntrinsicWidth/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/IntrinsicWidth/node1_base.dart deleted file mode 100644 index 1a3e5130e..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/IntrinsicWidth/node1_base.dart +++ /dev/null @@ -1,75 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/7/21 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 297, -// "name": 'IntrinsicWidth基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 子组件 【Widget】\n" -// "如示例:上面宽可变动,中间宽固定,下面宽取前两者的最高值。", -// } - -class IntrinsicWidthDemo extends StatefulWidget { - const IntrinsicWidthDemo({Key? key}) : super(key: key); - - @override - _IntrinsicWidthDemoState createState() => _IntrinsicWidthDemoState(); -} - -class _IntrinsicWidthDemoState extends State { - double _height =120.0; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - buildChild(_height), - const SizedBox(height: 10), - _buildSlider() - ], - ); - } - - Widget buildChild(double leftWidth) { - return IntrinsicWidth( - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - height: 50, - width: leftWidth, - color: Colors.yellow, - alignment: Alignment.center, - child: Text("width:${leftWidth.toStringAsFixed(1)}"), - ), - Container( - color: Colors.blue, - width: 150, - height: 60, - alignment: Alignment.center, - child: const Text("固定宽"), - ), - Container( - color: Colors.red, - height: 40, - alignment: Alignment.center, - child: const Text("最宽"), - ) - ], - ), - ); - } - - Widget _buildSlider() =>Slider( - value: _height, - max: 200.0, - min: 80.0, - divisions: 17, - onChanged: (v)=> setState(() => _height= v), - ); -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/LayoutBuilder/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/LayoutBuilder/node1_base.dart deleted file mode 100644 index 4fb1a7dae..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/LayoutBuilder/node1_base.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-04-19 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 287, -// "name": 'LayoutBuilder基本认识', -// "priority": 1, -// "subtitle": "【builder】 : 布局构造器 【LayoutWidgetBuilder】", -// } -class CustomLayoutBuilder extends StatelessWidget { - const CustomLayoutBuilder({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - print('CustomLayoutBuild'); - return Container( - alignment: Alignment.center, - height: 80, - width: 150, - color: Colors.green, - child: LayoutBuilder( - builder: (_, zone) { - return Text( - '父容器宽:${zone.maxWidth}\n' - '父容器高:${zone.maxHeight}', - style: const TextStyle(color: Colors.white, fontSize: 16), - ); - }, - ), - ); - } -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/LayoutBuilder/node2_fit.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/LayoutBuilder/node2_fit.dart deleted file mode 100644 index 4ee2ab4da..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/LayoutBuilder/node2_fit.dart +++ /dev/null @@ -1,108 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-04-19 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 287, -// "name": 'LayoutBuilder的适应布局', -// "priority": 2, -// "subtitle": "可以根据区域的大小进行组件展示设计。" -// "比如在不同的宽度区域显示不同的布局结构。" -// "毕竟很多地方不容易获取父组件区域,使用LayoutBuilder就会非常爽口。", -// } - -class FitByLayoutBuilder extends StatefulWidget { - const FitByLayoutBuilder({Key? key}) : super(key: key); - - @override - _FitByLayoutBuilderState createState() => _FitByLayoutBuilderState(); -} - -class _FitByLayoutBuilderState extends State { - double _width = 100; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - SizedBox( - width: _width, - child: LayoutBuilder( - builder: (_, zone) { - if (zone.maxWidth <= 150) { - return _buildType1(); - } else { - return _buildType2(zone); - } - }, - ), - ), - _buildSlider(), - ], - ); - } - - Widget _buildSlider() => Slider( - min: 50, - max: 300, - label: "父宽:${_width.toStringAsFixed(1)}", - value: _width, - onChanged: (v) => setState(() { - _width = v; - })); - - Widget _buildType1() => Container( - color: Colors.blue, - child: Column( - children: [ - _buildTitle(), - Padding( - padding: const EdgeInsets.all(8.0), - child: _buildContent(), - ), - ], - ), - ); - - Widget _buildType2(BoxConstraints zone) => Container( - height: 100, - width: zone.maxWidth, - color: Colors.orange, - child: Row( - children: [ - Container( - margin: const EdgeInsets.all(10), - height: 80, - width: 30, - color: Colors.grey, - ), - Expanded(child: _buildContent()) - ], - ), - ); - - Widget _buildTitle() => Container( - margin: const EdgeInsets.only(left: 10, right: 10, top: 10), - color: Colors.grey, - height: 30, - ); - - Widget _buildContent() => Wrap( - runSpacing: 3, - children: [ - Container( - color: Colors.red, - height: 30, - ), - Container( - color: Colors.yellow, - height: 30, - ), - Container( - color: Colors.green, - height: 30, - ), - ], - ); -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/LayoutBuilder/node3_expend.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/LayoutBuilder/node3_expend.dart deleted file mode 100644 index 40921327b..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/LayoutBuilder/node3_expend.dart +++ /dev/null @@ -1,90 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-04-19 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 287, -// "name": 'LayoutBuilder的展开使用', -// "priority": 3, -// "subtitle": "使用TextPainter来检测文字的行数,实现展开或收起功能。", -// } - -class SimpleExpandableText extends StatefulWidget { - const SimpleExpandableText({Key? key}) : super(key: key); - - @override - createState() => _SimpleExpandableTextState(); -} - -class _SimpleExpandableTextState extends State { - - final String text = '桃树、杏树、梨树,你不让我,我不让你,都开满了花赶趟儿。' - '红的像火,粉的像霞,白的像雪。' - '花里带着甜味儿;闭了眼,树上仿佛已经满是桃儿、杏儿、梨儿。' - '花下成千成百的蜜蜂嗡嗡地闹着,大小的蝴蝶飞来飞去。' - '野花遍地是:杂样儿,有名字的,没名字的,散在草丛里,像眼睛,像星星,还眨呀眨的。'; - - bool expand = false; - int maxLines = 3; - - final TextStyle style = - const TextStyle(fontSize: 15, color: Colors.grey, shadows: [ - Shadow( - color: Colors.white, - offset: Offset(1, 1), - ), - ]); - - BoxDecoration get boxDecoration => BoxDecoration( - color: Colors.cyanAccent.withAlpha(8), - borderRadius: const BorderRadiusDirectional.all( - Radius.circular(20), - ), - ); - - late TextPainter painter; - - @override - void initState() { - super.initState(); - painter = TextPainter( - text: TextSpan(text: text, style: style), - maxLines: maxLines, - textDirection: TextDirection.ltr, - ); - } - - @override - build(context) => Container( - decoration: boxDecoration, - padding: const EdgeInsets.all(15), - child: LayoutBuilder(builder: (context, size) { - painter.layout(maxWidth: size.maxWidth); - if (!painter.didExceedMaxLines) { - return Text(text, style: style); - } - return Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(text, maxLines: expand ? null : 3, style: style), - GestureDetector( - onTap: toggle, - child: Text( - expand ? '<< 收起' : '展开 >>', - style: const TextStyle(color: Colors.blue), - ), - ), - ], - ); - }), - ); - - void toggle() { - setState(() { - expand = !expand; - }); - } -} - diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/LimiteBox/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/LimiteBox/node1_base.dart deleted file mode 100644 index f2889afc6..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/LimiteBox/node1_base.dart +++ /dev/null @@ -1,71 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-04-19 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 79, -// "name": 'LimitedBox基本使用', -// "priority": 1, -// "subtitle": "【child】 : 孩子组件 【Widget】\n" -// "【maxHeight】 : 最大高 【double】\n" -// "【maxWidth】 : 最大宽 【double】", -// } -class CustomLimitedBox extends StatefulWidget { - const CustomLimitedBox({Key? key}) : super(key: key); - - @override - _CustomLimitedBoxState createState() => _CustomLimitedBoxState(); -} - -class _CustomLimitedBoxState extends State { - String _text = ''; - - @override - Widget build(BuildContext context) { - Widget child = Container( - alignment: Alignment.center, - color: Colors.cyanAccent, - width: 50, - height: 50, - child: const Text("Static"), - ); - - Widget box = LimitedBox( - maxHeight: 60, - maxWidth: 100, - child: Container(color: Colors.orange, child: Text(_text)), - ); - - return Column( - children: [ - Container( - color: Colors.grey.withAlpha(22), - width: 300, - height: 100, - child: Row( - children: [child, UnconstrainedBox(child: box), child], - ), - ), - _buildInput() - ], - ); - } - - Widget _buildInput() { - return Padding( - padding: const EdgeInsets.all(18.0), - child: TextField( - decoration: const InputDecoration( - border: OutlineInputBorder(), - hintText: '请输入', - ), - onChanged: (v) { - setState(() { - _text = v; - }); - }, - ), - ); - } -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/OffStage/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/OffStage/node1_base.dart deleted file mode 100644 index 7565ecb68..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/OffStage/node1_base.dart +++ /dev/null @@ -1,55 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-04-19 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 71, -// "name": 'Offstage基本使用', -// "priority": 1, -// "subtitle": "【child】 : 孩子组件 【Widget】\n" -// "【offstage】 : 是否消失 【bool】", -// } -class CustomOffstage extends StatefulWidget { - const CustomOffstage({Key? key}) : super(key: key); - - @override - _CustomOffstageState createState() => _CustomOffstageState(); -} - -class _CustomOffstageState extends State { - bool _off = false; - - @override - Widget build(BuildContext context) { - Widget radBox = Container( - height: 50, - width: 60, - color: Colors.red, - child: Switch( - value: _off, - onChanged: (v) => setState(() => _off = v)), - ); - - return SizedBox( - width: 250, - height: 200, - child: Row( - children: [radBox, _buildOffStage(), radBox], - ), - ); - } - - Widget _buildOffStage() => Offstage( - offstage: _off, - child: Container( - alignment: Alignment.center, - height: 100, - width: 100, - color: Colors.blue, - child: const Text( - "Offstage", - style: TextStyle(fontSize: 20), - ), - )); -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/Opacity/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/Opacity/node1_base.dart deleted file mode 100644 index 41baec52c..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/Opacity/node1_base.dart +++ /dev/null @@ -1,44 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-04-19 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 73, -// "name": 'Opacity基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】\n" -// "【opacity】 : 透明度0~1 【double】", -// } -class CustomOpacity extends StatefulWidget { - const CustomOpacity({Key? key}) : super(key: key); - - @override - _CustomOpacityState createState() => _CustomOpacityState(); -} - -class _CustomOpacityState extends State { - double _opacity = 0.2; - - @override - Widget build(BuildContext context) { - return Column( - children: [_buildSlider(), _buildOpacity()], - ); - } - - // 创建Opacity - Widget _buildOpacity() => Opacity( - opacity: _opacity, - child: Image.asset(// 图片 - 'assets/images/icon_head.webp', - width: 100, - ), - ); - Widget _buildSlider() => Slider( - divisions: 20, - label: _opacity.toString(), - value: _opacity, - onChanged: (v) => setState(() => _opacity = v)); -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/OverflowBox/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/OverflowBox/node1_base.dart deleted file mode 100644 index 145f001e4..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/OverflowBox/node1_base.dart +++ /dev/null @@ -1,69 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-04-19 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 83, -// "name": 'OverflowBox基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】\n" -// "【minWidth】 : 最小宽 【double】\n" -// "【minHeight】 : 最小高 【double】\n" -// "【maxHeight】 : 最大高 【double】\n" -// "【maxWidth】 : 最大宽 【double】\n" -// "【alignment】 : 对齐方式 【AlignmentGeometry】", -// } -class CustomOverflowBox extends StatefulWidget { - const CustomOverflowBox({Key? key}) : super(key: key); - - @override - _CustomOverflowBoxState createState() => _CustomOverflowBoxState(); -} - -class _CustomOverflowBoxState extends State { - String _text = ''; - - @override - Widget build(BuildContext context) { - Widget box = OverflowBox( - alignment: Alignment.center, - minHeight: 50, - minWidth: 50, - maxWidth: 200, - maxHeight: 120, - child: Container( - color: Colors.orange, - child: Text(_text), - ), - ); - return Column( - children: [ - Container( - color: Colors.grey.withAlpha(33), - width: 100, - height: 100, - child: box), - _buildInput() - ], - ); - } - - Widget _buildInput() { - return Padding( - padding: const EdgeInsets.all(18.0), - child: TextField( - decoration: const InputDecoration( - border: OutlineInputBorder(), - hintText: '请输入', - ), - onChanged: (v) { - setState(() { - _text = v; - }); - }, - ), - ); - } -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/Padding/node1_all.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/Padding/node1_all.dart deleted file mode 100644 index aee7c3e9f..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/Padding/node1_all.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/5/3 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 74, -// "name": 'Padding四面等边距', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】\n" -// "【padding】 : 内四边距 【EdgeInsetsGeometry】" -// "EdgeInsets.all用来限定相同的四边边距", -// } -class PaddingAll extends StatelessWidget { - const PaddingAll({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - color: Colors.grey.withAlpha(22), - width: 200, - height: 150, - child: Padding( - padding: const EdgeInsets.all(20), - child: _buildChild(), - ), - ); - } - - Widget _buildChild() => Container( - alignment: Alignment.center, - color: Colors.cyanAccent, - width: 100, - height: 100, - child: const Text("孩子"), - ); -} \ No newline at end of file diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/Padding/node2_only.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/Padding/node2_only.dart deleted file mode 100644 index 9ede5684a..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/Padding/node2_only.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/5/3 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 74, -// "name": 'Padding单独边距边距', -// "priority": 2, -// "subtitle": -// "EdgeInsets.only用来限定相同的四边边距", -// } -class PaddingOnly extends StatelessWidget { - const PaddingOnly({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - color: Colors.grey.withAlpha(22), - width: 200, - height: 150, - child: Padding( - padding: const EdgeInsets.only(top:10,left: 10), - child: _buildChild(), - ), - ); - } - - Widget _buildChild() => Container( - alignment: Alignment.center, - color: Colors.cyanAccent, - width: 100, - height: 100, - child: const Text("孩子"), - ); -} \ No newline at end of file diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/Padding/node3_symmetric.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/Padding/node3_symmetric.dart deleted file mode 100644 index a692b1f77..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/Padding/node3_symmetric.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/5/3 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 74, -// "name": 'Padding方向边距', -// "priority": 3, -// "subtitle": -// "EdgeInsets.symmetric用来限定水平和竖直方向的边距", -// } -class PaddingSymmetric extends StatelessWidget { - const PaddingSymmetric({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - color: Colors.grey.withAlpha(22), - width: 200, - height: 150, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 30,horizontal: 10), - child: _buildChild(), - ), - ); - } - - Widget _buildChild() => Container( - alignment: Alignment.center, - color: Colors.cyanAccent, - width: 100, - height: 100, - child: const Text("孩子"), - ); -} \ No newline at end of file diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/PhysicalModel/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/PhysicalModel/node1_base.dart deleted file mode 100644 index 9e91a1810..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/PhysicalModel/node1_base.dart +++ /dev/null @@ -1,60 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-23 -/// contact me by email 1981462002@qq.com -/// 说明: 296 PhysicalModel 物理模块 可以让子组件按照圆形、方行进行剪裁,并且可以指定背景色、圆角、影深、阴影颜色、剪切行为。 -// { -// "widgetId": 296, -// "name": "PhysicalModel基本使用", -// "priority": 1, -// "subtitle": "【clipBehavior】 : 裁剪行为 【Clip】\n" -// "【borderRadius】 : 圆角 【BorderRadius】\n" -// "【child】 : 子组件 【Widget】\n" -// "【elevation】 : 阴影深 【double】\n" -// "【shadowColor】 : 阴影颜色 【Color】\n" -// "【shape】 : 形状 【BoxShape】\n" -// "【color】: 颜色 【Color】", -// } -class PhysicalModelDemo extends StatelessWidget{ - const PhysicalModelDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - SizedBox( - width: 150, - height: 150, - child: PhysicalModel( - shadowColor: Colors.orange, - elevation: 3, - child: Image.asset( - 'assets/images/caver.webp', - fit: BoxFit.cover, - ), - clipBehavior: Clip.hardEdge, - shape: BoxShape.circle, - color: Colors.deepPurpleAccent), - ), - - SizedBox( - width: 150, - height: 150, - child: PhysicalModel( - shadowColor: Colors.orange, - elevation: 3, - child: Image.asset( - 'assets/images/caver.webp', - fit: BoxFit.cover, - ), - borderRadius: const BorderRadius.all(Radius.circular(20)), - clipBehavior: Clip.hardEdge, - shape: BoxShape.rectangle, - color: Colors.deepPurpleAccent), - ), - ], - ); - } - -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/PhysicalShape/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/PhysicalShape/node1_base.dart deleted file mode 100644 index ee4e255b5..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/PhysicalShape/node1_base.dart +++ /dev/null @@ -1,41 +0,0 @@ - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/7/22 -/// contact me by email 1981462002@qq.com -/// 说明: 279 PhysicalShape 物理形状 可以让子组件按照路径进行剪裁,并且可以指定背景色、影深、阴影颜色、剪切行为。 -// { -// "widgetId": 279, -// "name": "PhysicalShape基本使用", -// "priority": 1, -// "subtitle": "【clipper】 : 裁剪器 【CustomClipper】\n" -// "【clipBehavior】 : 裁剪行为 【Clip】\n" -// "【child】 : 子组件 【Widget】\n" -// "【elevation】 : 阴影深 【double】\n" -// "【shadowColor】 : 阴影颜色 【Color】\n" -// "【color】: 颜色 【Color】", -// } - -class PhysicalShapeDemo extends StatelessWidget { - const PhysicalShapeDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return SizedBox( - width: 200, - height: 200, - child: PhysicalShape( - shadowColor: Colors.orange, - elevation: 3, - child: Image.asset( - 'assets/images/caver.webp', - fit: BoxFit.cover, - ), - clipBehavior: Clip.hardEdge, - clipper: const ShapeBorderClipper( - shape: CircleBorder(side: BorderSide.none), - ), - color: Colors.deepPurpleAccent), - ); - } -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/RepaintBoundary/main.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/RepaintBoundary/main.dart deleted file mode 100644 index 13fecd3ad..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/RepaintBoundary/main.dart +++ /dev/null @@ -1,26 +0,0 @@ -/// create by 张风捷特烈 on 2020/7/22 -/// contact me by email 1981462002@qq.com -/// 说明: - -import 'package:flutter/material.dart'; -import 'node2_save.dart'; - - -void main() => runApp(const MyApp()); - -class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - primarySwatch: Colors.blue, - ), - home: Scaffold( - appBar: AppBar(), - body: Center(child: RepaintBoundarySave()), - )); - } -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/RepaintBoundary/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/RepaintBoundary/node1_base.dart deleted file mode 100644 index 3bb29b5da..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/RepaintBoundary/node1_base.dart +++ /dev/null @@ -1,185 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/7/22 -/// contact me by email 1981462002@qq.com -/// 说明: 264 RepaintBoundary 重绘边界 为子组件创建一个单独的显示列表,提升性能。源码中在TextField、DrawerController、Scrollbar、Sliver等组件中均有应用。 -// { -// "widgetId": 264, -// "name": "RepaintBoundary基本使用", -// "priority": 1, -// "subtitle": "【child】 : 子组件 【Widget】\n" -// "比如上面的绘制视图,即使shouldRepaint为false,在滑动中会也会不断执行paint方法,使用RepaintBoundary可以避免不必要的绘制。", -// } - -class RepaintBoundaryDemo extends StatelessWidget{ - const RepaintBoundaryDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return const RepaintBoundary( - child: TempPlayBezier3Page(), - ); - } -} - -class TempPlayBezier3Page extends StatefulWidget { - const TempPlayBezier3Page({Key? key}) : super(key: key); - - @override - _TempPlayBezier3PageState createState() => _TempPlayBezier3PageState(); -} - -class _TempPlayBezier3PageState extends State { - List _pos = []; - int? selectPos; - - @override - void initState() { - _initPoints(); - super.initState(); - } - - void _initPoints() { - _pos = []; - _pos.add(const Offset(0, 0)); - _pos.add(const Offset(60, -60)); - _pos.add(const Offset(-90, -90)); - _pos.add(const Offset(-120, -40)); - } - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 200, - width: MediaQuery.of(context).size.width, - child: CustomPaint( - painter: TempBezierPainter(pos: _pos, selectPos: selectPos), - ), - ); - } -} - -class TempBezierPainter extends CustomPainter { - late Paint _gridPaint; - late Path _gridPath; - - late Paint _mainPaint; - late Path _mainPath; - int? selectPos; - late Paint _helpPaint; - - List pos; - - TempBezierPainter({this.pos=const [], this.selectPos}) { - _gridPaint = Paint()..style = PaintingStyle.stroke; - _gridPath = Path(); - - _mainPaint = Paint() - ..color = Colors.orange - ..style = PaintingStyle.stroke - ..strokeWidth = 2; - _mainPath = Path(); - - _helpPaint = Paint() - ..color = Colors.purple - ..style = PaintingStyle.stroke - ..strokeWidth = 2 - ..strokeCap = StrokeCap.round; - } - - @override - void paint(Canvas canvas, Size size) { - print('----------Paint-------'); - canvas.clipRect(Offset.zero & size); - canvas.translate(size.width / 2, size.height / 2); - _drawGrid(canvas, size); //绘制格线 - _drawAxis(canvas, size); //绘制轴线 - - _mainPath.moveTo(pos[0].dx, pos[0].dy); - _mainPath.cubicTo( - pos[1].dx, pos[1].dy, pos[2].dx, pos[2].dy, pos[3].dx, pos[3].dy); - canvas.drawPath(_mainPath, _mainPaint); - _drawHelp(canvas); - _drawSelectPos(canvas); - } - - @override - bool shouldRepaint(CustomPainter oldDelegate) => false; - - void _drawGrid(Canvas canvas, Size size) { - _gridPaint - ..color = Colors.grey - ..strokeWidth = 0.5; - _gridPath = _buildGridPath(_gridPath, size); - canvas.drawPath(_buildGridPath(_gridPath, size), _gridPaint); - - canvas.save(); - canvas.scale(1, -1); //沿x轴镜像 - canvas.drawPath(_gridPath, _gridPaint); - canvas.restore(); - - canvas.save(); - canvas.scale(-1, 1); //沿y轴镜像 - canvas.drawPath(_gridPath, _gridPaint); - canvas.restore(); - - canvas.save(); - canvas.scale(-1, -1); //沿原点镜像 - canvas.drawPath(_gridPath, _gridPaint); - canvas.restore(); - } - - void _drawAxis(Canvas canvas, Size size) { - canvas.drawPoints( - PointMode.lines, - [ - Offset(-size.width / 2, 0), - Offset(size.width / 2, 0), - Offset(0, -size.height / 2), - Offset(0, size.height / 2), - Offset(0, size.height / 2), - Offset(0 - 7.0, size.height / 2 - 10), - Offset(0, size.height / 2), - Offset(0 + 7.0, size.height / 2 - 10), - Offset(size.width / 2, 0), - Offset(size.width / 2 - 10, 7), - Offset(size.width / 2, 0), - Offset(size.width / 2 - 10, -7), - ], - _gridPaint - ..color = Colors.blue - ..strokeWidth = 1.5); - } - - Path _buildGridPath(Path path, Size size, {step = 20.0}) { - for (int i = 0; i < size.height / 2 / step; i++) { - path.moveTo(0, step * i); - path.relativeLineTo(size.width / 2, 0); - } - for (int i = 0; i < size.width / 2 / step; i++) { - path.moveTo(step * i, 0); - path.relativeLineTo( - 0, - size.height / 2, - ); - } - return path; - } - - void _drawHelp(Canvas canvas) { - canvas.drawPoints(PointMode.lines, pos, _helpPaint..strokeWidth = 1); - canvas.drawPoints(PointMode.points, pos, _helpPaint..strokeWidth = 8); - } - - void _drawSelectPos(Canvas canvas) { - if (selectPos == null) return; - canvas.drawCircle( - pos[selectPos!], - 10, - _helpPaint - ..color = Colors.green - ..strokeWidth = 2); - } -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/RepaintBoundary/node2_save.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/RepaintBoundary/node2_save.dart deleted file mode 100644 index 953203afe..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/RepaintBoundary/node2_save.dart +++ /dev/null @@ -1,70 +0,0 @@ -import 'dart:io'; -import 'dart:typed_data'; -import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; -import 'package:path_provider/path_provider.dart'; -import 'dart:ui' as ui; -import 'node1_base.dart'; - -/// create by 张风捷特烈 on 2020/7/22 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 264, -// "name": "保存Widget成为图片", -// "priority": 2, -// "subtitle": "通过RenderRepaintBoundary可以获取子组件的Image信息,从而获取字节保存为图片文件。", -// } - -class RepaintBoundarySave extends StatelessWidget { - final GlobalKey _globalKey = GlobalKey(); - - RepaintBoundarySave({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Stack( - children: [ - RepaintBoundary( - key: _globalKey, - child: const TempPlayBezier3Page(), - ), - Positioned(right: -10, child: _buildButton3(context)) - ], - ); - } - - Widget _buildButton3(context) => MaterialButton( - child: const Icon( - Icons.save_alt, - size: 15, - color: Colors.white, - ), - color: Colors.green, - shape: const CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFDFDFDF)), - ), - onPressed: () async { - Uint8List? bits = await _widget2Image(_globalKey); - Directory dir = await getApplicationSupportDirectory(); - File file = File(dir.path + "/save_img.png"); - if(bits==null) return; - var f = await file.writeAsBytes(bits); - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - backgroundColor: Theme.of(context).primaryColor, - content: Text('保存成功后! 路径为:${f.path}'), - )); - }); - - Future _widget2Image(GlobalKey key) async { - RenderObject? boundary = key.currentContext?.findRenderObject(); - if(boundary==null || boundary is! RenderRepaintBoundary) return null; - - //获得 ui.image - ui.Image img = await boundary.toImage(); - //获取图片字节 - var byteData = await img.toByteData(format: ui.ImageByteFormat.png); - Uint8List? bits = byteData?.buffer.asUint8List(); - return bits; - } -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/RotatedBox/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/RotatedBox/node1_base.dart deleted file mode 100644 index 14a53292c..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/RotatedBox/node1_base.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-04-19 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 72, -// "name": 'RotatedBox基本使用', -// "priority": 1, -// "subtitle": "【child】 : 孩子组件 【Widget】\n" -// "【quarterTurns】 : 旋转多少个90° 【int】", -// } -class CustomRotatedBox extends StatefulWidget { - const CustomRotatedBox({Key? key}) : super(key: key); - - @override - _CustomRotatedBoxState createState() => _CustomRotatedBoxState(); -} - -class _CustomRotatedBoxState extends State { - int _quarterTurns = 0; - - @override - Widget build(BuildContext context) { - return RotatedBox( - quarterTurns: _quarterTurns, - child: GestureDetector( - onTap: () => setState(() => _quarterTurns++), - child: const Icon( - Icons.android, - size: 60, - color: Colors.blue, - )), - ); - } -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/ShaderMask/node1_radial.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/ShaderMask/node1_radial.dart deleted file mode 100644 index 5a55953cb..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/ShaderMask/node1_radial.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-04-12 -/// contact me by email 1981462002@qq.com -/// 说明: -/// -// { -// "widgetId": 277, -// "name": '径向渐变着色', -// "priority": 1, -// "subtitle": "【child】 : 孩子组件 【Widget】\n" -// "【shaderCallback】 : 着色器回调 【ShaderCallback】\n" -// "【blendMode】 : 混色模式 【BlendMode】\n" -// " 通过RadialGradient#createShader创建径向渐变着色器。", -// } -class RadialShaderMask extends StatelessWidget { - const RadialShaderMask({Key? key}) : super(key: key); - - final List colors = const [Colors.red, Colors.yellow, Colors.blue]; - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 20, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - ShaderMask( - shaderCallback: _buildShader, - child: Image.asset( - 'assets/images/icon_head.webp', - height: 70, - width: 70, - ), - ), - ShaderMask( - shaderCallback: _buildShader, - child: const Text( - '张风捷特烈', - style: TextStyle(fontSize: 40, color: Colors.white), - ), - ), - ShaderMask( - shaderCallback: _buildShader, - child: Container( - height: 100, - color: Colors.white, - width: 50, - ), - ), - ], - ); - } - - - Shader _buildShader(Rect bounds) => RadialGradient( - center: Alignment.topLeft, - radius: 1.0, - tileMode: TileMode.mirror, - colors: colors) - .createShader(bounds); -} \ No newline at end of file diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/ShaderMask/node2_linear.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/ShaderMask/node2_linear.dart deleted file mode 100644 index 8bf54085c..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/ShaderMask/node2_linear.dart +++ /dev/null @@ -1,57 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-04-12 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 277, -// "name": '线性渐变着色', -// "priority": 2, -// "subtitle": "通过LinearGradient#createShader创建线性渐变着色器\n" -// "着色器相关知识详见【绘制专辑】", -// } -class LinearShaderMask extends StatelessWidget { - const LinearShaderMask({Key? key}) : super(key: key); - - final List colors = const [Colors.red, Colors.yellow, Colors.blue]; - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 20, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - ShaderMask( - shaderCallback: _buildShader, - child: Image.asset( - 'assets/images/icon_head.webp', - height: 70, - width: 70, - ), - ), - ShaderMask( - shaderCallback: _buildShader, - child: const Text( - '张风捷特烈', - style: TextStyle(fontSize: 40, color: Colors.white), - ), - ), - ShaderMask( - shaderCallback: _buildShader, - child: Container( - height: 100, - color: Colors.white, - width: 50, - ), - ), - ], - ); - } - - Shader _buildShader(Rect bounds) => LinearGradient( - begin: Alignment.centerLeft, - end: Alignment.centerRight, - tileMode: TileMode.mirror, - colors: colors) - .createShader(bounds); -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/SizeChangedLayoutNotifier/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/SizeChangedLayoutNotifier/node1_base.dart deleted file mode 100644 index 3e5c2d141..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/SizeChangedLayoutNotifier/node1_base.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 294 SizeChangedLayoutNotifier 尺寸变化通告 使用 SizeChangedLayoutNotifier 可以在子组件布局区域发生变化后,发出通知。使用NotificationListener可以进行监听。 -// { -// "widgetId": 294, -// "name": '基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 组件 【Widget】", -// } - -class SizeChangedLayoutNotifierDemo extends StatefulWidget { - const SizeChangedLayoutNotifierDemo({Key? key}) : super(key: key); - - @override - _SizeChangedLayoutNotifierDemoState createState() => _SizeChangedLayoutNotifierDemoState(); -} - -class _SizeChangedLayoutNotifierDemoState extends State { - @override - Widget build(BuildContext context) { - return NotificationListener( - onNotification: _onNotification, - child: const ChangeableBox(), - ); - } - - bool _onNotification(SizeChangedLayoutNotification notification) { - print('---------SizeChangedLayoutNotification------'); - return false; - } -} - -class ChangeableBox extends StatefulWidget { - const ChangeableBox({Key? key}) : super(key: key); - - @override - _ChangeableBoxState createState() => _ChangeableBoxState(); -} - -class _ChangeableBoxState extends State { - double width = 40; - - @override - Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - SizeChangedLayoutNotifier( - child: Container( - width: width, - height: 100, - color: Colors.blue, - ), - ), - Slider( - max: 200, - min: 20, - divisions: 10, - value: width, - onChanged: _changeWidth, - ) - ], - ); - } - - void _changeWidth(double value) { - setState(() { - width = value; - }); - } -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/SizedBox/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/SizedBox/node1_base.dart deleted file mode 100644 index 09f295256..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/SizedBox/node1_base.dart +++ /dev/null @@ -1,47 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-04-19 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 76, -// "name": 'SizedBox基本使用', -// "priority": 1, -// "subtitle": "【child】 : 孩子组件 【Widget】\n" -// "【width】 : 宽 【double】\n" -// "【height】 : 高 【double】", -// } -class CustomSizedBox extends StatelessWidget { - const CustomSizedBox({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - Widget child = Container( - alignment: Alignment.center, - color: Colors.cyanAccent, - width: 50, - height: 50, - child: const Text("Static"), - ); - - Widget box = SizedBox( - width: 80, - height: 40, - child: Container( - color: Colors.orange, - child: const Icon( - Icons.android, - color: Colors.white, - )), - ); - - return Container( - color: Colors.grey.withAlpha(22), - width: 200, - height: 100, - child: Row( - children: [child, box, child], - ), - ); - } -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/SizedOverflowBox/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/SizedOverflowBox/node1_base.dart deleted file mode 100644 index 6cd73daba..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/SizedOverflowBox/node1_base.dart +++ /dev/null @@ -1,65 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-04-19 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 84, -// "name": 'SizedOverflowBox基本使用', -// "priority": 1, -// "subtitle": "【child】 : 孩子组件 【Widget】\n" -// "【size】 : 尺寸偏移 【Size】\n" -// "【alignment】 : 对齐方式 【AlignmentGeometry】", -// } -class CustomSizedOverflowBox extends StatefulWidget { - const CustomSizedOverflowBox({Key? key}) : super(key: key); - - @override - _CustomSizedOverflowBoxState createState() => _CustomSizedOverflowBoxState(); -} - -class _CustomSizedOverflowBoxState extends State { - double _x = 50; - double _y = 44; - - @override - Widget build(BuildContext context) { - Widget box = SizedOverflowBox( - alignment: Alignment.bottomRight, - size: Size(_x, _y), - child: Container(width: 30, height: 50, color: Colors.orange), - ); - return Column( - children: [ - Container( - alignment: Alignment.topLeft, - color: Colors.grey.withAlpha(88), - width: 250, - height: 60, - child: box), - _buildSlider() - ], - ); - } - - Widget _buildSlider() => - Column( - children: [ - Slider( - divisions: 100, - min: 0, - max: 250, - label: 'x:' + _x.toStringAsFixed(1), - value: _x, - onChanged: (v) => setState(() => _x = v)), - Slider( - divisions: 100, - min: 0, - max: 100, - label: 'y:' + _y.toStringAsFixed(1), - value: _y, - onChanged: (v) => setState(() => _y = v)), - - ], - ); -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/Transform/matrix4_shower.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/Transform/matrix4_shower.dart deleted file mode 100644 index f5dbadef0..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/Transform/matrix4_shower.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: - -class Matrix4Shower extends StatelessWidget { - final Matrix4 matrix4; - - const Matrix4Shower(this.matrix4, {Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Text( - '${matrix4.entry(0, 0).toStringAsFixed(1)},${matrix4.entry(0, 1).toStringAsFixed(1)},${matrix4.entry(0, 2).toStringAsFixed(1)},${matrix4.entry(0, 3).toStringAsFixed(1)},\n' - '${matrix4.entry(1, 0).toStringAsFixed(1)},${matrix4.entry(1, 1).toStringAsFixed(1)},${matrix4.entry(1, 2).toStringAsFixed(1)},${matrix4.entry(1, 3).toStringAsFixed(1)},\n' - '${matrix4.entry(2, 0).toStringAsFixed(1)},${matrix4.entry(2, 1).toStringAsFixed(1)},${matrix4.entry(2, 2).toStringAsFixed(1)},${matrix4.entry(2, 3).toStringAsFixed(1)},\n' - '${matrix4.entry(3, 0).toStringAsFixed(1)},${matrix4.entry(3, 1).toStringAsFixed(1)},${matrix4.entry(3, 2).toStringAsFixed(1)},${matrix4.entry(3, 3).toStringAsFixed(1)}', - style: const TextStyle(fontSize: 20, color: Colors.blue), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/Transform/node1_skew.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/Transform/node1_skew.dart deleted file mode 100644 index 1743e757a..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/Transform/node1_skew.dart +++ /dev/null @@ -1,89 +0,0 @@ -import 'dart:math'; -import 'package:flutter/material.dart'; - -import 'matrix4_shower.dart'; - -/// create by 张风捷特烈 on 2020-04-19 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 78, -// "name": '斜切变换skew', -// "priority": 1, -// "subtitle": "斜切x由R0C1数控制,入参为弧度值,表示斜切角度\n" -// "斜切y由R1C0数控制,入参为弧度值,表示斜切角度", -// } -class SkewTransform extends StatefulWidget { - const SkewTransform({Key? key}) : super(key: key); - - @override - _SkewTransformState createState() => _SkewTransformState(); -} - -class _SkewTransformState extends State { - late Matrix4 _m4; - double _alpha = 0; - double _beta = 0; - - @override - void initState() { - _m4 = Matrix4.identity(); - super.initState(); - } - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [_buildTransform(), Matrix4Shower(_m4)], - ), - _buildSliders() - ], - ); - } - - Widget _buildTransform() { - _m4 = Matrix4.skew(_alpha, _beta); - return Transform( - transform: _m4, - child: Container( - color: Colors.cyanAccent, - width: 100, - height: 100, - child: Image.asset( - 'assets/images/wy_300x200.webp', - fit: BoxFit.cover, - )), - ); - } - - Widget _buildSliders() => Column( - children: [ - Slider( - min: -pi, - max: pi, - value: _alpha, - divisions: 360, - label: 'alpha:' + (_alpha * 180 / pi).toStringAsFixed(1) + "°", - onChanged: (v) { - setState(() { - _alpha = v; - }); - }), - Slider( - min: -pi, - max: pi, - value: _beta, - divisions: 360, - label: 'beta:' + (_beta * 180 / pi).toStringAsFixed(1) + "°", - onChanged: (v) { - setState(() { - _beta = v; - }); - }) - ], - ); -} \ No newline at end of file diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/Transform/node2_translation.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/Transform/node2_translation.dart deleted file mode 100644 index b560fb3fb..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/Transform/node2_translation.dart +++ /dev/null @@ -1,101 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'matrix4_shower.dart'; - -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 78, -// "name": '平移变换translationValues', -// "priority": 2, -// "subtitle": "平移x由R0C3数控制,入参为数值,表示平移长度\n" -// "平移y由R1C3数控制,入参为数值,表示平移长度\n" -// "平移z由R2C3数控制,入参为数值,表示平移长度", -// } -class TranslationTransform extends StatefulWidget { - const TranslationTransform({Key? key}) : super(key: key); - - @override - _TranslationTransformState createState() => _TranslationTransformState(); -} - -class _TranslationTransformState extends State { - late Matrix4 _m4; - double _x = 0; - double _y = 0; - double _z = 0; - - @override - void initState() { - _m4 = Matrix4.identity(); - super.initState(); - } - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [_buildTransform(), Matrix4Shower(_m4)], - ), - _buildSliders() - ], - ); - } - - Widget _buildTransform() { - _m4 = Matrix4.translationValues(_x, _y, _z); - return Transform( - transform: _m4, - child: Container( - color: Colors.cyanAccent, - width: 100, - height: 100, - child: Image.asset( - 'assets/images/wy_300x200.webp', - fit: BoxFit.cover, - )), - ); - } - - Widget _buildSliders() => Column( - children: [ - Slider( - min: -100, - max: 100, - value: _x, - divisions: 360, - label: 'x:${_x.toStringAsFixed(1)}', - onChanged: (v) { - setState(() { - _x = v; - }); - }), - Slider( - min: -100, - max: 100, - value: _y, - divisions: 360, - label: 'y:${_y.toStringAsFixed(1)}', - onChanged: (v) { - setState(() { - _y = v; - }); - }), - Slider( - min: -100, - max: 100, - value: _z, - divisions: 360, - label: 'z:${_z.toStringAsFixed(1)}', - onChanged: (v) { - setState(() { - _z = v; - }); - }) - ], - ); -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/Transform/node3_scale.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/Transform/node3_scale.dart deleted file mode 100644 index dc292ede6..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/Transform/node3_scale.dart +++ /dev/null @@ -1,100 +0,0 @@ -import 'package:flutter/material.dart'; -import 'matrix4_shower.dart'; - -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 78, -// "name": '缩放变换diagonal3Values', -// "priority": 3, -// "subtitle": "缩放x由R0C0数控制,入参为数值,表示缩放分率\n" -// "缩放y由R1C2数控制,入参为数值,表示缩放分率\n" -// "缩放z由R2C2数控制,入参为数值,表示缩放分率", -// } -class ScaleTransform extends StatefulWidget { - const ScaleTransform({Key? key}) : super(key: key); - - @override - _ScaleTransformState createState() => _ScaleTransformState(); -} - -class _ScaleTransformState extends State { - late Matrix4 _m4; - double _x = 1.0; - double _y = 1.0; - double _z = 1.0; - - @override - void initState() { - _m4 = Matrix4.identity(); - super.initState(); - } - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [_buildTransform(), Matrix4Shower(_m4)], - ), - _buildSliders() - ], - ); - } - - Widget _buildTransform() { - _m4 = Matrix4.diagonal3Values(_x, _y, _z); - return Transform( - transform: _m4, - child: Container( - color: Colors.cyanAccent, - width: 100, - height: 100, - child: Image.asset( - 'assets/images/wy_300x200.webp', - fit: BoxFit.cover, - )), - ); - } - - Widget _buildSliders() => Column( - children: [ - Slider( - min: -2, - max: 2, - value: _x, - divisions: 360, - label: 'x:${_x.toStringAsFixed(1)}', - onChanged: (v) { - setState(() { - _x = v; - }); - }), - Slider( - min: -2, - max: 2, - value: _y, - divisions: 360, - label: 'y:${_y.toStringAsFixed(1)}', - onChanged: (v) { - setState(() { - _y = v; - }); - }), - Slider( - min: -2, - max: 2, - value: _z, - divisions: 360, - label: 'z:${_z.toStringAsFixed(1)}', - onChanged: (v) { - setState(() { - _z = v; - }); - }) - ], - ); -} \ No newline at end of file diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/Transform/node4_rotate.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/Transform/node4_rotate.dart deleted file mode 100644 index ac251ab22..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/Transform/node4_rotate.dart +++ /dev/null @@ -1,121 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; - -import 'matrix4_shower.dart'; - -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 78, -// "name": '旋转变换rotation', -// "priority": 4, -// "subtitle": "x旋转由R1C1、R1C2、R2C1、R2C2控制,入参表示弧度\n" -// "y旋转由R0C0、R0C2、R2C0、R2C2控制,入参表示弧度\n" -// "z旋转由R0C0、R0C1、R1C0、R1C1控制,入参表示弧度\n" -// } -class RotateTransform extends StatefulWidget { - const RotateTransform({Key? key}) : super(key: key); - - @override - _RotateTransformState createState() => _RotateTransformState(); -} - -class _RotateTransformState extends State { - late Matrix4 _m4; - double _x = 0; - int _rotateFlag = 1; - - @override - void initState() { - _m4 = Matrix4.identity(); - super.initState(); - } - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [_buildTransform(), Matrix4Shower(_m4)], - ), - _buildSliders() - ], - ); - } - - Widget _buildTransform() { - if (_rotateFlag == 1) { - _m4 = Matrix4.rotationX(_x); - } else if (_rotateFlag == 2) { - _m4 = Matrix4.rotationY(_x); - } else { - _m4 = Matrix4.rotationZ(_x); - } - - return Transform( - transform: _m4, - child: Container( - color: Colors.cyanAccent, - width: 100, - height: 100, - child: Image.asset( - 'assets/images/wy_300x200.webp', - fit: BoxFit.cover, - )), - ); - } - - final Map map = { - 1: 'rotationX', - 2: 'rotationY', - 3: 'rotationZ', - }; - - Widget _buildSliders() => Column( - children: [ - Wrap( - children: map.keys.map((key) => _buildChild(key)).toList(), - ), - Slider( - min: -pi, - max: pi, - value: _x, - divisions: 360, - label: 'x:${_x.toStringAsFixed(1)}', - onChanged: (v) { - setState(() { - _x = v; - }); - }), - ], - ); - - Padding _buildChild(int key) { - return Padding( - padding: const EdgeInsets.all(4.0), - child: FilterChip( - selectedColor: Colors.orange.withAlpha(55), - selectedShadowColor: Colors.blue, - shadowColor: Colors.orangeAccent, - pressElevation: 5, - elevation: 3, - avatar: CircleAvatar(child: Text(key.toString())), - label: Text(map[key]!), - selected: _rotateFlag == key, - onSelected: (bool value) { - print(map[key]); - setState(() { - _x = 0; - if (value) { - _rotateFlag = key; - } - }); - }, - ), - ); - } -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/Transform/node5_perspective.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/Transform/node5_perspective.dart deleted file mode 100644 index 1875ee5e4..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/Transform/node5_perspective.dart +++ /dev/null @@ -1,80 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 78, -// "name": '透视变换rotation', -// "priority": 5, -// "subtitle": "由R3C1、R3C2、R3C3控制透视", -// } -class R3C2 extends StatefulWidget { - const R3C2({Key? key}) : super(key: key); - - @override - _R3C2State createState() => _R3C2State(); -} - -class _R3C2State extends State { - late Matrix4 _m4; - double _value = 0; - double _rad = 0; - - @override - Widget build(BuildContext context) { - _m4 = Matrix4.identity() -// ..setEntry(3, 0, _value) // x -// ..setEntry(3, 1, _value)// y - ..setEntry(3, 2, _value) // z - ..rotateY(_rad) -// ..rotateX(_rad) - ; - return Column( - children: [ - Transform( - transform: _m4, - child: Container( - color: Colors.cyanAccent, - width: 100, - height: 100, - child: Image.asset( - 'assets/images/wy_300x200.webp', - fit: BoxFit.cover, - )), - ), - _buildSliders() - ], - ); - } - - Widget _buildSliders() => Column( - children: [ - Slider( - min: -0.01, - max: 0.01, - value: _value, - divisions: 360, - label: 'x:${_value.toStringAsFixed(5)}', - onChanged: (v) { - setState(() { - _value = v; - }); - }), - Slider( - min: -pi, - max: pi, - value: _rad, - divisions: 360, - label: '角度:' + (_rad * 180 / pi).toStringAsFixed(1) + "°", - onChanged: (v) { - setState(() { - _rad = v; - }); - }), - ], - ); -} \ No newline at end of file diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/Transform/zz_node_op.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/Transform/zz_node_op.dart deleted file mode 100644 index 67770279e..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/Transform/zz_node_op.dart +++ /dev/null @@ -1,98 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: - -class CustomTransform extends StatefulWidget { - const CustomTransform({Key? key}) : super(key: key); - - @override - _CustomTransformState createState() => _CustomTransformState(); -} - -class _CustomTransformState extends State { - double angle = 0.0; - List m = [ - 1.0, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0, - 0.0, - 0.0, - 0.0, - 0.1, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0, - ]; - - @override - Widget build(BuildContext context) { - var transform = Transform( - transform: Matrix4(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], - m[9], m[10], m[11], m[12], m[13], m[14], m[15]), - child: Container( - alignment: Alignment.center, - color: Colors.cyanAccent, - width: 100, - height: 100, - child: Image.asset( - 'assets/images/wy_300x200.webp', - fit: BoxFit.cover, - )), - ); - - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Container( - alignment: Alignment.topLeft, - color: Colors.grey.withAlpha(22), - width: 200, - height: 150, - child: transform, - ), - Text( - '${m[0]},${m[1]},${m[2]},${m[3]},\n' - '${m[4]},${m[5]},${m[6]},${m[7]},\n' - '${m[8]},${m[9]},${m[10]},${m[11]},\n' - '${m[12]},${m[13]},${m[14]},${m[15]}\n', - style: const TextStyle(fontSize: 20), - ) - ], - ), - SizedBox( - width: MediaQuery.of(context).size.width, - height: 200, - child: _buildSliders()) - ], - ); - } - - Widget _buildSliders() => GridView.count( - crossAxisCount: 2, - childAspectRatio: 8, - children: m - .asMap() - .keys - .map((i) => Slider( - value: m[i], - max: 0.01, - min: 0.0, - divisions: 10, - onChanged: (v) { - setState(() { - m[i] = v; - }); - })) - .toList()); -} diff --git a/packages/widgets/lib/SingleChildRenderObjectWidget/UnConstrainedBox/node1_base.dart b/packages/widgets/lib/SingleChildRenderObjectWidget/UnConstrainedBox/node1_base.dart deleted file mode 100644 index b92505bfb..000000000 --- a/packages/widgets/lib/SingleChildRenderObjectWidget/UnConstrainedBox/node1_base.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-04-19 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 81, -// "name": 'UnConstrainedBox基本使用', -// "priority": 1, -// "subtitle": "【child】 : 孩子组件 【Widget】\n" -// "【constrainedAxis】 : 仍受约束的轴*2 【Axis】\n" -// "【alignment】 : 对齐方式 【AlignmentGeometry】", -// } -class CustomUnConstrainedBox extends StatefulWidget { - const CustomUnConstrainedBox({Key? key}) : super(key: key); - - @override - _CustomUnConstrainedBoxState createState() => _CustomUnConstrainedBoxState(); -} - -class _CustomUnConstrainedBoxState extends State { - bool _value = false; - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 20, - children: [_buildUnconstrainedBox(), _buildConstrainedAxis()], - ); - } - - Widget _buildUnconstrainedBox() { - Widget child = Container( - color: Colors.cyanAccent, - width: 60, - height: 60, - child: Switch( - value: _value, - onChanged: (v) { - setState(() { - _value = v; - }); - }, - ), - ); - - return Column( - children: [ - Container( - color: Colors.grey.withAlpha(22), - width: 150, - height: 100, - child: _value - ? UnconstrainedBox(alignment: Alignment.center, child: child) - : child, - ), - Text(_value ? "已解除约束" : "子组件受约束") - ], - ); - } - - Widget _buildConstrainedAxis() { - return Column( - children: [ - Container( - color: Colors.grey.withAlpha(22), - width: 150, - height: 100, - child: UnconstrainedBox( - alignment: Alignment.center, - constrainedAxis: Axis.vertical, - child: Container( - color: Colors.cyanAccent, - width: 60, - height: 60, - )), - ), - const Text("竖直方向仍约束") - ], - ); - } -} diff --git a/packages/widgets/lib/Sliver/CupertinoSliverNavigationBar/node1_base.dart b/packages/widgets/lib/Sliver/CupertinoSliverNavigationBar/node1_base.dart deleted file mode 100644 index bd419ea82..000000000 --- a/packages/widgets/lib/Sliver/CupertinoSliverNavigationBar/node1_base.dart +++ /dev/null @@ -1,101 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 302 CupertinoSliverNavigationBar Sliver导航条 iOS11中导航条效果,展开时largeTitle显示,列表上滑后不显示,如果middle为空,largeTitle会以小字号作为middle。 -// { -// "widgetId": 302, -// "name": '导航条基本使用', -// "priority": 1, -// "subtitle": -// "【leading】 : 左侧组件 【Widget】\n" -// "【middle】 : 中间组件 【Widget】\n" -// "【trailing】 : 尾部组件 【Widget】\n" -// "【largeTitle】 : 底部折展组件 【Widget】\n" -// "【border】 : 边线 【Border】\n" -// "【backgroundColor】 : 背景色 【Color】\n" -// "【padding】 : 内边距 【EdgeInsetsDirectional】", -// } -class CupertinoSliverNavigationBarDemo extends StatelessWidget { - CupertinoSliverNavigationBarDemo({Key? key}) : super(key: key); - - final List data = [ - Colors.orange[50]!, - Colors.orange[100]!, - Colors.orange[200]!, - Colors.orange[300]!, - Colors.orange[400]!, - Colors.orange[500]!, - Colors.orange[600]!, - Colors.orange[700]!, - Colors.orange[800]!, - Colors.orange[900]!, - ]; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 300, - child: CustomScrollView( - slivers: [ - CupertinoSliverNavigationBar( - trailing: const Icon( - CupertinoIcons.share, - size: 25, - ), - leading: _buildLeading(), - backgroundColor: Colors.white, - // middle: Text('张风捷特烈'), - largeTitle: Row( - mainAxisSize: MainAxisSize.min, - children: const [ - Icon( - Icons.ac_unit, - size: 20, - ), - Padding( - padding: EdgeInsets.symmetric(horizontal: 8), - child: Text('张风捷特烈'), - ), - Icon(Icons.ac_unit, size: 20), - ], - ), - ), - _buildSliverList() - ], - ), - ); - } - - Widget _buildSliverList() => SliverPrototypeExtentList( - prototypeItem: Container( - height: 40, - ), - delegate: SliverChildBuilderDelegate( - (_, int index) => Container( - alignment: Alignment.center, - width: 100, - height: 60, - color: data[index], - child: Text( - colorString(data[index]), - style: const TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2) - ]), - ), - ), - childCount: data.length), - ); - - Widget _buildLeading() => Container( - margin: const EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')); - - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/packages/widgets/lib/Sliver/CupertinoSliverRefreshControl/node1_base.dart b/packages/widgets/lib/Sliver/CupertinoSliverRefreshControl/node1_base.dart deleted file mode 100644 index edf3fb4d5..000000000 --- a/packages/widgets/lib/Sliver/CupertinoSliverRefreshControl/node1_base.dart +++ /dev/null @@ -1,130 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 303 CupertinoSliverRefreshControl Sliver刷新控制器 iOS风格的下拉刷新控制器,可执行异步刷新方法、自定义控制器组件、指示器停留高度和触发加载的滑动高度。 -// { -// "widgetId": 303, -// "name": '刷新控制器基本使用', -// "priority": 1, -// "subtitle": -// "【refreshIndicatorExtent】 : 加载中指示器高度 【double】\n" -// "【refreshTriggerPullDistance】 : 触发加载的滑动高度 【double】\n" -// "【onRefresh】 : 下拉事件 【RefreshCallback】\n" -// "【builder】 : 指示器构造器 【RefreshControlIndicatorBuilder】", -// } -class CupertinoSliverRefreshControlDemo extends StatefulWidget { - const CupertinoSliverRefreshControlDemo({Key? key}) : super(key: key); - - @override - _CupertinoSliverRefreshControlDemoState createState() => - _CupertinoSliverRefreshControlDemoState(); -} - -class _CupertinoSliverRefreshControlDemoState - extends State { - final List data = [ - Colors.orange[50]!, - Colors.orange[100]!, - Colors.orange[200]!, - Colors.orange[300]!, - Colors.orange[400]!, - Colors.orange[500]!, - Colors.orange[600]!, - Colors.orange[700]!, - Colors.orange[800]!, - Colors.orange[900]!, - ]; - - final Random r = Random(); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 300, - child: CustomScrollView( - physics: const BouncingScrollPhysics( - parent: AlwaysScrollableScrollPhysics()), - slivers: [ - _buildSliverAppBar(), - CupertinoSliverRefreshControl( - refreshIndicatorExtent: 60, - refreshTriggerPullDistance: 80, - onRefresh: _doRefresh, - ), - _buildSliverList() - ], - ), - ); - } - - Widget _buildSliverList() => SliverFixedExtentList( - itemExtent: 50, - delegate: SliverChildBuilderDelegate( - (_, int index) => Container( - alignment: Alignment.center, - width: 100, - height: 60, - color: data[index], - child: Text( - colorString(data[index]), - style: const TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2) - ]), - ), - ), - childCount: data.length), - ); - - Widget _buildSliverAppBar() { - return SliverAppBar( - expandedHeight: 120.0, - leading: Container( - margin: const EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')), - title: const Text('张风捷特烈'), - actions: _buildActions(), - elevation: 5, - pinned: true, - backgroundColor: Colors.orange, - flexibleSpace: FlexibleSpaceBar( - //伸展处布局 - titlePadding: const EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } - - List _buildActions() => [ - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.star_border, - color: Colors.white, - ), - ) - ]; - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; - - Color randomColor() => Color.fromARGB( - r.nextInt(255), r.nextInt(255), r.nextInt(255), r.nextInt(255)); - - Future _doRefresh() async { - await Future.delayed(const Duration(seconds: 2)); - setState(() { - data.insertAll(0, [randomColor()]); - }); - } -} diff --git a/packages/widgets/lib/Sliver/CustomScrollView/node1_base.dart b/packages/widgets/lib/Sliver/CustomScrollView/node1_base.dart deleted file mode 100644 index 8c1ec6d2d..000000000 --- a/packages/widgets/lib/Sliver/CustomScrollView/node1_base.dart +++ /dev/null @@ -1,99 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 183, -// "name": 'CustomScrollView基本使用', -// "priority": 1, -// "subtitle": "【slivers】 : 子组件列表 【List】\n" -// "【reverse】 : 是否反向 【bool】\n" -// "【scrollDirection】 : 滑动方向 【Axis】\n" -// "【controller】 : 控制器 【ScrollController】", -// } -class CustomScrollViewDemo extends StatelessWidget { - CustomScrollViewDemo({Key? key}) : super(key: key); - - final List data = [ - Colors.purple[50]!, - Colors.purple[100]!, - Colors.purple[200]!, - Colors.purple[300]!, - Colors.purple[400]!, - Colors.purple[500]!, - Colors.purple[600]!, - Colors.purple[700]!, - Colors.purple[800]!, - Colors.purple[900]!, - ]; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 300, - child: CustomScrollView( - anchor: 0, - scrollDirection: Axis.vertical, - reverse: false, - slivers: [_buildSliverAppBar(), _buildSliverFixedExtentList()], - ), - ); - } - - TextStyle get textStyle => const TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2, - ) - ]); - - Widget _buildSliverFixedExtentList() => SliverFixedExtentList( - itemExtent: 60, - delegate: SliverChildBuilderDelegate( - (_, int index) => Container( - alignment: Alignment.center, - width: 100, - height: 50, - color: data[index], - child: Text( - colorString(data[index]), - style: textStyle, - ), - ), - childCount: data.length), - ); - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; - - _buildSliverAppBar() { - return SliverAppBar( - expandedHeight: 190.0, - leading: Container( - margin: const EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')), - flexibleSpace: FlexibleSpaceBar( - //伸展处布局 - titlePadding: const EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - title: const Text( - '张风捷特烈', - style: TextStyle(color: Colors.black, //标题 - shadows: [ - Shadow( - color: Colors.blue, - offset: Offset(1, 1), - blurRadius: 2, - ) - ]), - ), - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } -} diff --git a/packages/widgets/lib/Sliver/DecoratedSliver/node1.dart b/packages/widgets/lib/Sliver/DecoratedSliver/node1.dart deleted file mode 100644 index cbeb023a6..000000000 --- a/packages/widgets/lib/Sliver/DecoratedSliver/node1.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2023/8/18 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 209, -// "name": 'DecoratedSliver 基本使用', -// "priority": 1, -// "subtitle": -// "【sliver】 : 孩子组件 【Widget?】\n" -// "【decoration】 : 装饰对象 【Decoration】\n" -// "【position】 : 装饰位置 【DecorationPosition】", -// } -class DecorationSliverDemo extends StatelessWidget { - const DecorationSliverDemo({super.key}); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 300, - child: CustomScrollView( - slivers: [ - SliverPadding( - padding: const EdgeInsets.all(8), - sliver: DecoratedSliver( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - boxShadow: const [ - BoxShadow( - color: Color(0xFF111133), - blurRadius: 2, - offset: Offset(-2, -1)) - ], - gradient: LinearGradient( - colors: const [ - Color(0xFFEEEEEE), - Color(0xFF111133), - ], - stops: const [0.1, 1.0], - ), - ), - sliver: SliverList( - delegate: SliverChildBuilderDelegate( - (_, index) => Padding( - padding: const EdgeInsets.all(8.0), - child: Center( - child: Text( - '张风捷特烈-$index', - style: TextStyle(fontSize: 24, color: Colors.white), - )), - ), - childCount: 128), - ), - ), - ), - ], - ), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/Sliver/FlexibleSpaceBar/node1_base.dart b/packages/widgets/lib/Sliver/FlexibleSpaceBar/node1_base.dart deleted file mode 100644 index 4e459881a..000000000 --- a/packages/widgets/lib/Sliver/FlexibleSpaceBar/node1_base.dart +++ /dev/null @@ -1,109 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-31 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 196, -// "name": 'FlexibleSpaceBar基本使用', -// "priority": 1, -// "subtitle": "【title】 : 标题组件 【Widget】\n" -// "【titlePadding】 : 标题间距 【EdgeInsetsGeometry】\n" -// "【collapseMode】 : 折叠模式 【CollapseMode】\n" -// "【stretchModes】 : 延伸模式 【List】\n" -// "【background】 : 背景组件 【Widget】\n" -// "【centerTitle】 : 是否居中 【bool】", -// } - -class FlexibleSpaceBarDemo extends StatelessWidget { - FlexibleSpaceBarDemo({Key? key}) : super(key: key); - - final List data =[ - Colors.blue[50]!, - Colors.blue[100]!, - Colors.blue[200]!, - Colors.blue[300]!, - Colors.blue[400]!, - Colors.blue[500]!, - Colors.blue[600]!, - Colors.blue[700]!, - Colors.blue[800]!, - Colors.blue[900]!, - ]; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 300, - child: CustomScrollView( - slivers: [_buildSliverAppBar(), _buildSliverFixedExtentList()], - ), - ); - } - - Widget _buildSliverAppBar() { - return SliverAppBar( - expandedHeight: 190.0, - leading: _buildLeading(), - actions: _buildActions(), - pinned: true, - backgroundColor: Colors.blue, - flexibleSpace: FlexibleSpaceBar(//伸展处布局 - centerTitle: false, - title: const Text( - '张风捷特烈', - style: TextStyle(shadows: [ - Shadow(color: Colors.blue, offset: Offset(1, 1), blurRadius: 2) - ]), - ), - titlePadding: const EdgeInsets.only(left: 55, bottom: 15), - //标题边距 - collapseMode: CollapseMode.parallax, - //视差效果 - stretchModes: const [StretchMode.blurBackground, StretchMode.zoomBackground], - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } - - Widget _buildLeading() => Container( - margin: const EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')); - - List _buildActions() => [ - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.star_border, - color: Colors.white, - ), - ) - ]; - - Widget _buildSliverFixedExtentList() => SliverFixedExtentList( - itemExtent: 60, - delegate: SliverChildBuilderDelegate( - (_, int index) => Container( - alignment: Alignment.center, - width: 100, - height: 50, - color: data[index], - child: Text( - colorString(data[index]), - style: const TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2) - ]), - ), - ), - childCount: data.length), - ); - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/packages/widgets/lib/Sliver/SliverAnimatedList/node1_base.dart b/packages/widgets/lib/Sliver/SliverAnimatedList/node1_base.dart deleted file mode 100644 index a039aec70..000000000 --- a/packages/widgets/lib/Sliver/SliverAnimatedList/node1_base.dart +++ /dev/null @@ -1,199 +0,0 @@ - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 301 SliverAnimatedList Sliver动画列表 在插入或删除项目时使其有动画效果的sliver组件。 -// { -// "widgetId": 301, -// "name": 'SliverAnimatedList基本使用', -// "priority": 1, -// "subtitle": "【itemBuilder】 : item构造器 【AnimatedListItemBuilder】\n" -// "【initialItemCount】 : 初始item个数 【int】", -// } -class SliverAnimatedListDemo extends StatefulWidget { - const SliverAnimatedListDemo({Key? key}) : super(key: key); - - @override - _SliverAnimatedListDemoState createState() => - _SliverAnimatedListDemoState(); -} - -class _SliverAnimatedListDemoState extends State { - - final GlobalKey _listKey = GlobalKey(); - late ListModel _list; - int? _selectedItem; - int _nextItem=0; - - @override - void initState() { - super.initState(); - _list = ListModel( - listKey: _listKey, - initialItems: [0, 1, 2], - removedItemBuilder: _buildRemovedItem, - ); - _nextItem = 3; - } - - Widget _buildItem(BuildContext context, int index, Animation animation) { - return CardItem( - animation: animation, - item: _list[index], - selected: _selectedItem == _list[index], - onTap: () { - setState(() { - _selectedItem = _selectedItem == _list[index] ? null : _list[index]; - }); - }, - ); - } - - Widget _buildRemovedItem(int item, BuildContext context, Animation animation) { - return CardItem( - animation: animation, - item: item, - selected: false, - ); - } - - void _insert() { - final int index = _selectedItem == null ? _list.length : _list.indexOf(_selectedItem!); - _list.insert(index, _nextItem++); - } - - void _remove() { - if (_selectedItem != null) { - _list.removeAt(_list.indexOf(_selectedItem!)); - setState(() { - _selectedItem = null; - }); - } else { - if(_list.length>0){ - _list.removeAt(0); - setState(() { - _selectedItem = null; - }); - } - } - } - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 300, - child: CustomScrollView( - slivers: [ - SliverAppBar( - title: const Text( - 'SliverAnimatedList', - style: TextStyle(fontSize: 20), - ), - expandedHeight: 60, - centerTitle: true, - leading: IconButton( - icon: const Icon(Icons.add_circle), - onPressed: _insert, - tooltip: '插入一个item', - iconSize: 32, - ), - actions: [ - IconButton( - icon: const Icon(Icons.remove_circle), - onPressed: _remove, - tooltip: '删除选中的item', - iconSize: 32, - ), - ], - ), - SliverAnimatedList( - key: _listKey, - initialItemCount: _list.length, - itemBuilder: _buildItem, - ), - ], - ), - ); - } - - -} - -class ListModel { - ListModel({ - required this.listKey, - required this.removedItemBuilder, - required Iterable? initialItems, - }) : assert(removedItemBuilder != null), - _items = List.from(initialItems ?? []); - final GlobalKey listKey; - final dynamic removedItemBuilder; - final List _items; - SliverAnimatedListState? get _animatedList => listKey.currentState; - void insert(int index, E item) { - _items.insert(index, item); - _animatedList?.insertItem(index); - } - E removeAt(int index) { - final E removedItem = _items.removeAt(index); - if (removedItem != null) { - _animatedList?.removeItem( - index, - (BuildContext context, Animation animation) => removedItemBuilder(removedItem, context, animation), - ); - } - return removedItem; - } - int get length => _items.length; - E operator [](int index) => _items[index]; - int indexOf(E item) => _items.indexOf(item); -} - - -class CardItem extends StatelessWidget { - const CardItem({ - Key? key, - required this.animation, - required this.item, - this.onTap, - this.selected = false, - }) :super(key: key); - final Animation animation; - final VoidCallback? onTap; - final int item; - final bool selected; - @override - Widget build(BuildContext context) { - return Padding( - padding: - const EdgeInsets.only( - left: 2.0, - right: 2.0, - top: 2.0, - bottom: 0.0, - ), - child: SizeTransition( - axis: Axis.vertical, - sizeFactor: animation, - child: GestureDetector( - onTap: onTap, - child: SizedBox( - height: 60.0, - child: Card( - color: selected - ? Colors.black12 - : Colors.primaries[item % Colors.primaries.length], - child: Center( - child: Text( - 'Item $item', - style: const TextStyle(color: Colors.white,fontSize: 16), - ), - ), - ), - ), - ), - ), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/Sliver/SliverAppBar/node1_base.dart b/packages/widgets/lib/Sliver/SliverAppBar/node1_base.dart deleted file mode 100644 index ffe8d7809..000000000 --- a/packages/widgets/lib/Sliver/SliverAppBar/node1_base.dart +++ /dev/null @@ -1,173 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 184, -// "name": 'SliverAppBar基本使用', -// "priority": 1, -// "subtitle": "【leading】 : 左侧组件 【Widget】\n" -// "【title】 : 中间组件 【Widget】\n" -// "【actions】 : 尾部组件列表 【List】\n" -// "【floating】 : 是否浮动 【bool】\n" -// "【pinned】 : 是否顶部停留 【bool】\n" -// "【snap】 : 是否半收展 【bool】\n" -// "【bottom】 : 底部组件 【PreferredSizeWidget】\n" -// "【expandedHeight】 : 延展高度 【double】\n" -// "【elevation】 : 影深 【double】\n" -// "【flexibleSpace】 : 延展空间 【FlexibleSpaceBar】\n" -// "【backgroundColor】 : 背景色 【Color】\n" -// "【controller】 : 控制器 【ScrollController】\n" -// " snap为true时必需floating为true", -// } -class SliverAppBarDemo extends StatefulWidget { - const SliverAppBarDemo({Key? key}) : super(key: key); - - @override - _SliverAppBarDemoState createState() => _SliverAppBarDemoState(); -} - -class _SliverAppBarDemoState extends State { - bool _floating = false; - bool _pinned = false; - bool _snap = false; - - final List data = [ - Colors.purple[50]!, - Colors.purple[100]!, - Colors.purple[200]!, - Colors.purple[300]!, - Colors.purple[400]!, - Colors.purple[500]!, - Colors.purple[600]!, - Colors.purple[700]!, - Colors.purple[800]!, - Colors.purple[900]!, - ]; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - _buildTool(), - SizedBox( - height: 300, - child: CustomScrollView( - slivers: [ - _buildSliverAppBar(), - _buildSliverFixedExtentList() - ], - ), - ), - ], - ); - } - - Widget _buildSliverAppBar() { - print(_floating); - return SliverAppBar( - expandedHeight: 190.0, - leading: _buildLeading(), - title: const Text('张风捷特烈'), - actions: _buildActions(), - elevation: 5, - floating: _floating, - pinned: _pinned, - snap: _snap, - backgroundColor: Colors.orange, - flexibleSpace: FlexibleSpaceBar(//伸展处布局 - titlePadding: const EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } - - Widget _buildLeading() => Container( - margin: const EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')); - - List _buildActions() => [ - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.star_border, - color: Colors.white, - ), - ) - ]; - - Widget _buildSliverFixedExtentList() => SliverFixedExtentList( - itemExtent: 60, - delegate: SliverChildBuilderDelegate( - (_, int index) => Container( - alignment: Alignment.center, - width: 100, - height: 50, - color: data[index], - child: Text( - colorString(data[index]), - style: const TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2) - ]), - ), - ), - childCount: data.length), - ); - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; - - Widget _buildTool() { - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Wrap( - direction: Axis.vertical, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - const Text('floating'), - Switch( - value: _floating, - onChanged: (v) { - if(_snap&&!v){ - _snap =false; - } - setState(() => _floating = v); - }), - ], - ), - Wrap( - direction: Axis.vertical, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - const Text('pinned'), - Switch( - value: _pinned, - onChanged: (v) => setState(() => _pinned = v)), - ], - ) ,Wrap( - direction: Axis.vertical, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - const Text('snap'), - Switch( - value: _snap, - onChanged: (v) { - if(_floating){ - setState(() => _snap = v); - } - }), - ], - ) - ], - ); - } -} diff --git a/packages/widgets/lib/Sliver/SliverConstrainedCrossAxis/node1.dart b/packages/widgets/lib/Sliver/SliverConstrainedCrossAxis/node1.dart deleted file mode 100644 index e49d2c39b..000000000 --- a/packages/widgets/lib/Sliver/SliverConstrainedCrossAxis/node1.dart +++ /dev/null @@ -1,94 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2023/8/18 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 270, -// "name": 'SliverConstrainedCrossAxis 基本使用', -// "priority": 1, -// "subtitle": -// "【maxExtent】 : 大小 【double】\n" -// "【sliver】 : 子组件 【Widget?】", -// } -class SliverConstrainedCrossAxisDemo extends StatelessWidget { - const SliverConstrainedCrossAxisDemo({super.key}); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 300, - child: CustomScrollView( - slivers: [ - SliverCrossAxisGroup( - slivers: [ - SliverConstrainedCrossAxis( - maxExtent: 100, - sliver: _SliverColorList( - height: 100.0, - fontSize: 24, - count: 8, - color1: Colors.amber[300], - color2: Colors.blue[300], - ), - ), - SliverCrossAxisExpanded( - flex: 1, - sliver: _SliverColorList( - height: 80.0, - fontSize: 18, - count: 15, - color1: Colors.green[300], - color2: Colors.red[300], - ), - ), - SliverCrossAxisExpanded( - flex: 1, - sliver: _SliverColorList( - height: 50.0, - fontSize: 20, - count: 6, - color1: Colors.purple[300], - color2: Colors.orange[300], - )), - ], - ), - ], - ), - ); - } -} - -class _SliverColorList extends StatelessWidget { - final double height; - final double fontSize; - final Color? color1; - final Color? color2; - final int count; - const _SliverColorList( - {super.key, - required this.height, - required this.fontSize, - required this.count, - this.color1, - this.color2}); - - @override - Widget build(BuildContext context) { - return SliverList.builder( - itemBuilder: (BuildContext context, int index) { - return Container( - color: index.isEven ? color1 : color2, - height: height, - child: Center( - child: Text( - 'Item ${index}', - style: TextStyle(fontSize: fontSize), - ), - ), - ); - }, - itemCount: count, - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/Sliver/SliverCrossAxisExpanded/node1.dart b/packages/widgets/lib/Sliver/SliverCrossAxisExpanded/node1.dart deleted file mode 100644 index 2ff5359ea..000000000 --- a/packages/widgets/lib/Sliver/SliverCrossAxisExpanded/node1.dart +++ /dev/null @@ -1,84 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2023/8/18 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 271, -// "name": 'SliverCrossAxisExpanded 基本使用', -// "priority": 1, -// "subtitle": -// "【flex】 : 占比 【int】\n" -// "【sliver】 : 子组件 【Widget?】", -// } -class SliverCrossAxisExpandedDemo extends StatelessWidget { - const SliverCrossAxisExpandedDemo({super.key}); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 300, - child: CustomScrollView( - slivers: [ - SliverCrossAxisGroup( - slivers: [ - SliverCrossAxisExpanded( - flex: 1, - sliver: _SliverColorList( - height: 80.0, - fontSize: 18, - count: 15, - color1: Colors.green[300], - color2: Colors.red[300], - ), - ), - SliverCrossAxisExpanded( - flex: 1, - sliver: _SliverColorList( - height: 50.0, - fontSize: 20, - count: 6, - color1: Colors.purple[300], - color2: Colors.orange[300], - )), - ], - ), - ], - ), - ); - } -} - -class _SliverColorList extends StatelessWidget { - final double height; - final double fontSize; - final Color? color1; - final Color? color2; - final int count; - const _SliverColorList( - {super.key, - required this.height, - required this.fontSize, - required this.count, - this.color1, - this.color2}); - - @override - Widget build(BuildContext context) { - return SliverList.builder( - itemBuilder: (BuildContext context, int index) { - return Container( - color: index.isEven ? color1 : color2, - height: height, - child: Center( - child: Text( - 'Item ${index}', - style: TextStyle(fontSize: fontSize), - ), - ), - ); - }, - itemCount: count, - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/Sliver/SliverCrossAxisGroup/node1.dart b/packages/widgets/lib/Sliver/SliverCrossAxisGroup/node1.dart deleted file mode 100644 index 954accbcd..000000000 --- a/packages/widgets/lib/Sliver/SliverCrossAxisGroup/node1.dart +++ /dev/null @@ -1,86 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2023/8/18 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 269, -// "name": 'SliverCrossAxisGroup 基本使用', -// "priority": 1, -// "subtitle": -// "【slivers】 : 子组件列表 【List】", -// } -class SliverCrossAxisGroupDemo extends StatelessWidget { - const SliverCrossAxisGroupDemo({super.key}); - - @override - Widget build(BuildContext context) { - double width = MediaQuery.of(context).size.width; - return SizedBox( - height: 300, - child: CustomScrollView( - slivers: [ - SliverCrossAxisGroup( - slivers: [ - SliverConstrainedCrossAxis( - maxExtent: 100, - sliver: _SliverColorList( - height: 100.0, - fontSize: 24, - count: 8, - color1: Colors.amber[300], - color2: Colors.blue[300], - ), - ), - SliverConstrainedCrossAxis( - maxExtent: width - 100, - sliver: _SliverColorList( - height: 80.0, - fontSize: 18, - count: 15, - color1: Colors.green[300], - color2: Colors.red[300], - ), - ), - ], - ), - ], - ), - ); - - } -} - -class _SliverColorList extends StatelessWidget { - final double height; - final double fontSize; - final Color? color1; - final Color? color2; - final int count; - const _SliverColorList( - {super.key, - required this.height, - required this.fontSize, - required this.count, - this.color1, - this.color2}); - - @override - Widget build(BuildContext context) { - return SliverList.builder( - itemBuilder: (BuildContext context, int index) { - return Container( - color: index.isEven ? color1 : color2, - height: height, - child: Center( - child: Text( - 'Item ${index}', - style: TextStyle(fontSize: fontSize), - ), - ), - ); - }, - itemCount: count, - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/Sliver/SliverFillRemaining/node1_base.dart b/packages/widgets/lib/Sliver/SliverFillRemaining/node1_base.dart deleted file mode 100644 index f4428881f..000000000 --- a/packages/widgets/lib/Sliver/SliverFillRemaining/node1_base.dart +++ /dev/null @@ -1,158 +0,0 @@ -import 'dart:math'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 306 SliverFillRemaining Sliver填补剩余 一个包含单个box子元素的sliver,它填充了视窗中的剩余空间。 -// { -// "widgetId": 306, -// "name": 'SliverFillRemaining基本使用', -// "priority": 1, -// "subtitle": "【hasScrollBody】 : 是否具有滚动主体 【bool】\n" -// "【fillOverscroll】 : 是否可填充滚动区域 【bool】\n" -// "【child】 : 子组件 【Widget】", -// } -class SliverFillRemainingDemo extends StatefulWidget { - const SliverFillRemainingDemo({Key? key}) : super(key: key); - - @override - _SliverFillRemainingDemoState createState() => - _SliverFillRemainingDemoState(); -} - -class _SliverFillRemainingDemoState extends State { - final List data = [ - Colors.orange[50]!, - Colors.orange[100]!, - Colors.orange[200]!, - Colors.orange[300]!, - Colors.orange[400]!, - Colors.orange[500]!, - Colors.orange[600]!, - Colors.orange[700]!, - Colors.orange[800]!, - Colors.orange[900]!, - ]; - - final Random r = Random(); - - bool hasScrollBody = false; - bool fillOverscroll = true; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 300, - child: CustomScrollView( - physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), - slivers: [ - _buildSliverAppBar(), - _buildSliverList(), - SliverFillRemaining( - hasScrollBody: hasScrollBody, - fillOverscroll: fillOverscroll, - child: Container( - decoration: const BoxDecoration( - image: DecorationImage( - fit: BoxFit.cover, - image: AssetImage("assets/images/sabar_bar.webp"))), - // // color: Colors.teal[100], - child: _buildBottomChild(), - ), - ), - ], - ), - ); - } - - Widget _buildBottomChild() => Align( - alignment: Alignment.bottomCenter, - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Wrap( - spacing: 10, - children: [ - ElevatedButton( - style: TextButton.styleFrom( - backgroundColor: Colors.blue, - ), - onPressed: () { - setState(() { - hasScrollBody = !hasScrollBody; - }); - }, - child: Text('hasScrollBody:$hasScrollBody',style: const TextStyle(color: Colors.white),), - ), - ElevatedButton( - style: TextButton.styleFrom( - backgroundColor: Colors.blue, - ), - onPressed: () { - setState(() { - fillOverscroll = !fillOverscroll; - }); - }, - child: Text('fillOverscroll:$fillOverscroll',style: const TextStyle(color: Colors.white)), - ), - ], - ), - ), - ); - - Widget _buildSliverList() => SliverFixedExtentList( - itemExtent: 50, - delegate: SliverChildBuilderDelegate( - (_, int index) => Container( - alignment: Alignment.center, - width: 100, - height: 60, - color: data[index], - child: Text( - colorString(data[index]), - style: const TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2) - ]), - ), - ), - childCount: data.length), - ); - - Widget _buildSliverAppBar() { - return SliverAppBar( - expandedHeight: 120.0, - leading: Container( - margin: const EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')), - title: const Text('张风捷特烈'), - actions: _buildActions(), - elevation: 5, - pinned: true, - backgroundColor: Colors.orange, - flexibleSpace: FlexibleSpaceBar( - titlePadding: const EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } - - List _buildActions() => [ - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.star_border, - color: Colors.white, - ), - ) - ]; - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; - -} diff --git a/packages/widgets/lib/Sliver/SliverFillViewport/node1_base.dart b/packages/widgets/lib/Sliver/SliverFillViewport/node1_base.dart deleted file mode 100644 index a609de578..000000000 --- a/packages/widgets/lib/Sliver/SliverFillViewport/node1_base.dart +++ /dev/null @@ -1,119 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-31 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 187, -// "name": 'SliverFillViewport基本使用', -// "priority": 1, -// "subtitle": "【viewportFraction】 : 视口分率 【double】\n" -// "【delegate】 : 孩子代理 【SliverChildDelegate】", -// } -class SliverFillViewportDemo extends StatefulWidget { - const SliverFillViewportDemo({Key? key}) : super(key: key); - - @override - _SliverFillViewportDemoState createState() => _SliverFillViewportDemoState(); -} - -class _SliverFillViewportDemoState extends State { - final List data = [ - Colors.orange[50]!, - Colors.orange[100]!, - Colors.orange[200]!, - Colors.orange[300]!, - Colors.orange[400]!, - Colors.orange[500]!, - Colors.orange[600]!, - Colors.orange[700]!, - Colors.orange[800]!, - Colors.orange[900]!, - ]; - - double _viewportFraction = 0.5; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - _buildTool(), - SizedBox( - height: 300, - child: CustomScrollView( - slivers: [_buildSliverAppBar(), _buildSliverList()], - ), - ), - ], - ); - } - - Widget _buildSliverList() => SliverFillViewport( - viewportFraction: _viewportFraction, - delegate: SliverChildBuilderDelegate( - (_, int index) => Container( - alignment: Alignment.center, - width: 100, - height: 60, - color: data[index], - child: Text( - colorString(data[index]), - style: const TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2) - ]), - ), - ), - childCount: data.length), - ); - - Widget _buildSliverAppBar() { - return SliverAppBar( - expandedHeight: 190.0, - leading: _buildLeading(), - title: const Text('张风捷特烈'), - actions: _buildActions(), - elevation: 5, - pinned: true, - backgroundColor: Colors.orange, - flexibleSpace: FlexibleSpaceBar( - //伸展处布局 - titlePadding: const EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } - - Widget _buildLeading() => Container( - margin: const EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')); - - List _buildActions() => [ - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.star_border, - color: Colors.white, - ), - ) - ]; - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; - - _buildTool() { - return Slider( - value: _viewportFraction, - min: 0.01, - divisions: 20, - label: _viewportFraction.toStringAsFixed(1), - max: 2.0, - onChanged: (v) => setState(() => _viewportFraction = v)); - } -} diff --git a/packages/widgets/lib/Sliver/SliverFixedExtentList/node1_base.dart b/packages/widgets/lib/Sliver/SliverFixedExtentList/node1_base.dart deleted file mode 100644 index ebcf3bbf6..000000000 --- a/packages/widgets/lib/Sliver/SliverFixedExtentList/node1_base.dart +++ /dev/null @@ -1,103 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-31 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 186, -// "name": 'SliverFixedExtentList基本使用', -// "priority": 1, -// "subtitle": "【itemExtent】 : 主轴方向强迫长度 【double】\n" -// "【delegate】 : 孩子代理 【SliverChildDelegate】", -// } -class SliverFixedExtentListDemo extends StatefulWidget { - const SliverFixedExtentListDemo({Key? key}) : super(key: key); - - @override - _SliverFixedExtentListDemoState createState() => _SliverFixedExtentListDemoState(); -} - -class _SliverFixedExtentListDemoState extends State { - final List data = [ - Colors.orange[50]!, - Colors.orange[100]!, - Colors.orange[200]!, - Colors.orange[300]!, - Colors.orange[400]!, - Colors.orange[500]!, - Colors.orange[600]!, - Colors.orange[700]!, - Colors.orange[800]!, - Colors.orange[900]!, - ]; - - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 300, - child: CustomScrollView( - slivers: [_buildSliverAppBar(), _buildSliverList()], - ), - ); - } - - Widget _buildSliverList() => SliverFixedExtentList( - itemExtent: 50, - delegate: SliverChildBuilderDelegate( - (_, int index) => Container( - alignment: Alignment.center, - width: 100, - height: 60, - color: data[index], - child: Text( - colorString(data[index]), - style: const TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2) - ]), - ), - ), - childCount: data.length), - ); - - Widget _buildSliverAppBar() { - return SliverAppBar( - expandedHeight: 190.0, - leading: _buildLeading(), - title: const Text('张风捷特烈'), - actions: _buildActions(), - elevation: 5, - pinned: true, - backgroundColor: Colors.orange, - flexibleSpace: FlexibleSpaceBar( - //伸展处布局 - titlePadding: const EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } - - Widget _buildLeading() => Container( - margin: const EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')); - - List _buildActions() => [ - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.star_border, - color: Colors.white, - ), - ) - ]; - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/packages/widgets/lib/Sliver/SliverGrid/node1_base.dart b/packages/widgets/lib/Sliver/SliverGrid/node1_base.dart deleted file mode 100644 index a6cb09d1c..000000000 --- a/packages/widgets/lib/Sliver/SliverGrid/node1_base.dart +++ /dev/null @@ -1,90 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 188, -// "name": 'SliverList基本使用', -// "priority": 1, -// "subtitle": "SliverGrid.count 指定轴向数量构造\n" -// "SliverGrid.extent 指定轴向长度构造\n" -// "属性特征同GridView,可详见之", -// } -class SliverGirdDemo extends StatelessWidget { - SliverGirdDemo({Key? key}) : super(key: key); - - final List data = List.generate(128, (i) => Color(0xFF6600FF - 2 * i)); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 300, - child: CustomScrollView( - slivers: [_buildSliverAppBar(), _buildSliverList()], - ), - ); - } - - Widget _buildSliverList() => SliverGrid.extent( - childAspectRatio: 1 / 0.618, - maxCrossAxisExtent: 180, - crossAxisSpacing: 5, - mainAxisSpacing: 5, - children: data - .map((e) => Container( - alignment: Alignment.center, - width: 100, - height: 60, - color: e, - child: Text( - colorString(e), - style: const TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2) - ]), - ), - )) - .toList(), - ); - - Widget _buildSliverAppBar() { - return SliverAppBar( - expandedHeight: 190.0, - leading: _buildLeading(), - title: const Text('张风捷特烈'), - actions: _buildActions(), - elevation: 5, - pinned: true, - backgroundColor: Colors.orange, - flexibleSpace: FlexibleSpaceBar( - //伸展处布局 - titlePadding: const EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } - - Widget _buildLeading() => Container( - margin: const EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')); - - List _buildActions() => [ - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.star_border, - color: Colors.white, - ), - ) - ]; - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/packages/widgets/lib/Sliver/SliverIgnorePointer/node1_base.dart b/packages/widgets/lib/Sliver/SliverIgnorePointer/node1_base.dart deleted file mode 100644 index 0efb5c3f3..000000000 --- a/packages/widgets/lib/Sliver/SliverIgnorePointer/node1_base.dart +++ /dev/null @@ -1,158 +0,0 @@ -import 'dart:math'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 305 SliverIgnorePointer Sliver忽略事件 可以包裹一个sliver组件,通过ignoring来控制该sliver组件是否可以响应事件。 -// { -// "widgetId": 305, -// "name": 'SliverIgnorePointer基本使用', -// "priority": 1, -// "subtitle": "【sliver】 : sliver组件 【Widget】\n" -// "【ignoring】 : 是否忽略事件 【bool】\n", -// } -class SliverIgnorePointerDemo extends StatefulWidget { - const SliverIgnorePointerDemo({Key? key}) : super(key: key); - - @override - _SliverIgnorePointerDemoState createState() => - _SliverIgnorePointerDemoState(); -} - -class _SliverIgnorePointerDemoState extends State { - final List data = [ - Colors.orange[50]!, - Colors.orange[100]!, - Colors.orange[200]!, - Colors.orange[300]!, - Colors.orange[400]!, - Colors.orange[500]!, - Colors.orange[600]!, - Colors.orange[700]!, - Colors.orange[800]!, - Colors.orange[900]!, - ]; - final Random r = Random(); - bool hasScrollBody = false; - bool fillOverscroll = true; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 300, - child: CustomScrollView( - physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), - slivers: [ - _buildSliverAppBar(), - _buildSliverList(), - SliverIgnorePointer( - ignoring: true, - sliver: SliverFillRemaining( - hasScrollBody: hasScrollBody, - fillOverscroll: fillOverscroll, - child: Container( - decoration: const BoxDecoration( - image: DecorationImage( - fit: BoxFit.cover, - image: AssetImage("assets/images/sabar_bar.webp"))), - // // color: Colors.teal[100], - child: _buildBottomChild(), - ), - ), - ), - ], - ), - ); - } - - Widget _buildBottomChild() => Align( - alignment: Alignment.bottomCenter, - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Wrap( - spacing: 10, - children: [ - ElevatedButton( - style: TextButton.styleFrom( - backgroundColor: Colors.blue, - ), - onPressed: () { - setState(() { - hasScrollBody = !hasScrollBody; - }); - }, - child: Text('hasScrollBody:$hasScrollBody',style: const TextStyle(color: Colors.white),), - ), - ElevatedButton( - style: TextButton.styleFrom( - backgroundColor: Colors.blue, - ), - onPressed: () { - setState(() { - fillOverscroll = !fillOverscroll; - }); - }, - child: Text('fillOverscroll:$fillOverscroll',style: const TextStyle(color: Colors.white)), - ), - ], - ), - ), - ); - - Widget _buildSliverList() => SliverFixedExtentList( - itemExtent: 50, - delegate: SliverChildBuilderDelegate( - (_, int index) => Container( - alignment: Alignment.center, - width: 100, - height: 60, - color: data[index], - child: Text( - colorString(data[index]), - style: const TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2) - ]), - ), - ), - childCount: data.length), - ); - - Widget _buildSliverAppBar() { - return SliverAppBar( - expandedHeight: 120.0, - leading: Container( - margin: const EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')), - title: const Text('张风捷特烈'), - actions: _buildActions(), - elevation: 5, - pinned: true, - backgroundColor: Colors.orange, - flexibleSpace: FlexibleSpaceBar( - titlePadding: const EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } - - List _buildActions() => [ - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.star_border, - color: Colors.white, - ), - ) - ]; - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; - -} diff --git a/packages/widgets/lib/Sliver/SliverLayoutBuilder/node1_base.dart b/packages/widgets/lib/Sliver/SliverLayoutBuilder/node1_base.dart deleted file mode 100644 index 7e36248d2..000000000 --- a/packages/widgets/lib/Sliver/SliverLayoutBuilder/node1_base.dart +++ /dev/null @@ -1,123 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; - - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 304 SliverLayoutBuilder Sliver布局构造器 Sliver家族一员,在滑动过程中可以通过回调出的 SliverConstraints 对象进行子组件的构造。 -// { -// "widgetId": 304, -// "name": 'SliverLayoutBuilder基本使用', -// "priority": 1, -// "subtitle": "【builder】 : 组件构造器 【SliverLayoutWidgetBuilder】", -// } -class SliverLayoutBuilderDemo extends StatefulWidget { - const SliverLayoutBuilderDemo({Key? key}) : super(key: key); - - @override - _SliverLayoutBuilderDemoState createState() => - _SliverLayoutBuilderDemoState(); -} - -class _SliverLayoutBuilderDemoState extends State { - final List data = [ - Colors.orange[50]!, - Colors.orange[100]!, - Colors.orange[200]!, - Colors.orange[300]!, - Colors.orange[400]!, - Colors.orange[500]!, - Colors.orange[600]!, - Colors.orange[700]!, - Colors.orange[800]!, - Colors.orange[900]!, - ]; - - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 300, - child: CustomScrollView( - physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), - slivers: [ - _buildSliverAppBar(), - SliverLayoutBuilder( - builder: _buildSliver, - ), - _buildSliverList(), - ], - ), - ); - } - - Widget _buildSliverList() => SliverFixedExtentList( - itemExtent: 50, - delegate: SliverChildBuilderDelegate( - (_, int index) => Container( - alignment: Alignment.center, - width: 100, - height: 60, - color: data[index], - child: Text( - colorString(data[index]), - style: const TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2) - ]), - ), - ), - childCount: data.length), - ); - - Widget _buildSliverAppBar() { - return SliverAppBar( - expandedHeight: 120.0, - leading: Container( - margin: const EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')), - title: const Text('张风捷特烈'), - actions: _buildActions(), - elevation: 5, - pinned: true, - backgroundColor: Colors.orange, - flexibleSpace: FlexibleSpaceBar( - titlePadding: const EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } - - List _buildActions() => [ - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.star_border, - color: Colors.white, - ), - ) - ]; - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; - - Widget _buildSliver(BuildContext context, SliverConstraints constraints) { - return SliverToBoxAdapter( - child: Container( - alignment: Alignment.center, - height: constraints.remainingPaintExtent / 3, - color: Colors.red, - child: const Text( - "SliverLayoutBuilder", - style: TextStyle(color: Colors.white, fontSize: 20), - ), - ), - ); - } -} diff --git a/packages/widgets/lib/Sliver/SliverList/node1_base.dart b/packages/widgets/lib/Sliver/SliverList/node1_base.dart deleted file mode 100644 index 1be3ef687..000000000 --- a/packages/widgets/lib/Sliver/SliverList/node1_base.dart +++ /dev/null @@ -1,95 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 185, -// "name": 'SliverList基本使用', -// "priority": 1, -// "subtitle": "【delegate】 : 孩子代理 【SliverChildDelegate】", -// } -class SliverListDemo extends StatelessWidget { - SliverListDemo({Key? key}) : super(key: key); - - final List data =[ - Colors.purple[50]!, - Colors.purple[100]!, - Colors.purple[200]!, - Colors.purple[300]!, - Colors.purple[400]!, - Colors.purple[500]!, - Colors.purple[600]!, - Colors.purple[700]!, - Colors.purple[800]!, - Colors.purple[900]!, - ]; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 300, - child: CustomScrollView( - slivers: [_buildSliverAppBar(), _buildSliverList()], - ), - ); - } - - Widget _buildSliverList() => SliverList( - delegate: SliverChildBuilderDelegate( - (_, int index) => Container( - alignment: Alignment.center, - width: 100, - height: 60, - color: data[index], - child: Text( - colorString(data[index]), - style: const TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2) - ]), - ), - ), - childCount: data.length), - ); - - Widget _buildSliverAppBar() { - return SliverAppBar( - expandedHeight: 190.0, - leading: _buildLeading(), - title: const Text('张风捷特烈'), - actions: _buildActions(), - elevation: 5, - pinned: true, - backgroundColor: Colors.orange, - flexibleSpace: FlexibleSpaceBar( - //伸展处布局 - titlePadding: const EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } - - Widget _buildLeading() => Container( - margin: const EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')); - - List _buildActions() => [ - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.star_border, - color: Colors.white, - ), - ) - ]; - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/packages/widgets/lib/Sliver/SliverMainAxisGroup/node1.dart b/packages/widgets/lib/Sliver/SliverMainAxisGroup/node1.dart deleted file mode 100644 index 0126e8892..000000000 --- a/packages/widgets/lib/Sliver/SliverMainAxisGroup/node1.dart +++ /dev/null @@ -1,103 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2023/8/18 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 268, -// "name": 'SliverMainAxisGroup 基本使用', -// "priority": 1, -// "subtitle": -// "【slivers】 : 子组件列表 【List】\n" -// "可结合 SliverPersistentHeader 实现分组,标题吸顶效果。", -// } -class ItemData { - final String groupName; - final List users; - - ItemData({required this.groupName, this.users = const []}); - - static List get testData => [ - ItemData(groupName: '幻将术士', users: ['梦小梦', '梦千']), - ItemData( - groupName: '幻将剑客', users: ['捷特', '龙少', '莫向阳', '何解连', '浪封', '梦飞烟']), - ItemData(groupName: '幻将弓者', users: ['巫缨', '巫妻孋', '摄王', '裔王', '梦童']), - ItemData( - groupName: '其他', users: List.generate(20, (index) => '小兵$index')), - ]; -} - -class SliverMainAxisGroupDemo extends StatelessWidget{ - const SliverMainAxisGroupDemo({super.key}); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 300, - child: CustomScrollView( - slivers: ItemData.testData.map(_buildGroup).toList(), - ), - ); - } - - Widget _buildGroup(ItemData itemData) { - return SliverMainAxisGroup(slivers: [ - SliverPersistentHeader( - pinned: true, - delegate: _HeaderDelegate(itemData.groupName), - ), - SliverList( - // tag2 - delegate: SliverChildBuilderDelegate( - (_, index) => _buildItemByUser(itemData.users[index]), - childCount: itemData.users.length, - ), - ), - ]); - } - - Widget _buildItemByUser(String user) { - return Container( - alignment: Alignment.center, - height: 56, - child: Row( - children: [ - const Padding( - padding: EdgeInsets.only(left: 20, right: 10.0), - child: FlutterLogo(size: 30), - ), - Text( - user, - style: const TextStyle(fontSize: 16), - ), - ], - ), - ); - } -} - -class _HeaderDelegate extends SliverPersistentHeaderDelegate { - const _HeaderDelegate(this.title); - - final String title; - - @override - Widget build( - BuildContext context, double shrinkOffset, bool overlapsContent) { - return Container( - alignment: Alignment.centerLeft, - color: const Color(0xffF6F6F6), - padding: const EdgeInsets.only(left: 20), - height: 40, - child: Text(title)); - } - - @override - double get maxExtent => minExtent; - - @override - double get minExtent => 40; - - @override - bool shouldRebuild(covariant _HeaderDelegate oldDelegate) => title!=oldDelegate.title; -} \ No newline at end of file diff --git a/packages/widgets/lib/Sliver/SliverOpacity/node1_base.dart b/packages/widgets/lib/Sliver/SliverOpacity/node1_base.dart deleted file mode 100644 index 0bd0136f8..000000000 --- a/packages/widgets/lib/Sliver/SliverOpacity/node1_base.dart +++ /dev/null @@ -1,94 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-31 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 192, -// "name": 'SliverOpacity基本使用', -// "priority": 1, -// "subtitle": "【opacity】 : 透明度 【double】\n" -// "【sliver】 : 子组件 【Function()】", -// } -class SliverOpacityDemo extends StatelessWidget { - SliverOpacityDemo({Key? key}) : super(key: key); - - final List data = List.generate(128, (i) => Color(0xFF6600FF - 2 * i)); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 300, - child: CustomScrollView( - slivers: [ - _buildSliverAppBar(), - SliverPadding( - padding: const EdgeInsets.only(top: 10), - sliver: SliverOpacity(opacity: 0.2, sliver: _buildSliverGrid())) - ], - ), - ); - } - - Widget _buildSliverGrid() => SliverGrid.extent( - childAspectRatio: 1 / 0.618, - maxCrossAxisExtent: 180, - crossAxisSpacing: 5, - mainAxisSpacing: 5, - children: data - .map((e) => Container( - alignment: Alignment.center, - width: 100, - height: 60, - color: e, - child: Text( - colorString(e), - style: const TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2) - ]), - ), - )) - .toList(), - ); - - Widget _buildSliverAppBar() { - return SliverAppBar( - expandedHeight: 190.0, - leading: _buildLeading(), - title: const Text('张风捷特烈'), - actions: _buildActions(), - elevation: 5, - pinned: true, - backgroundColor: Colors.orange, - flexibleSpace: FlexibleSpaceBar( - //伸展处布局 - titlePadding: const EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } - - Widget _buildLeading() => Container( - margin: const EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')); - - List _buildActions() => [ - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.star_border, - color: Colors.white, - ), - ) - ]; - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/packages/widgets/lib/Sliver/SliverOverlapAbsorber/node1_base.dart b/packages/widgets/lib/Sliver/SliverOverlapAbsorber/node1_base.dart deleted file mode 100644 index 8b1ea2c08..000000000 --- a/packages/widgets/lib/Sliver/SliverOverlapAbsorber/node1_base.dart +++ /dev/null @@ -1,101 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/6/16 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 307, -// "name": 'SliverOverlapAbsorber基本使用', -// "priority": 1, -// "subtitle": "【sliver】 : 子组件 【Widget】\n" -// "【handle】 : *处理器 【SliverOverlapAbsorberHandle】\n" -// "如果不使用SliverOverlapAbsorber和SliverOverlapInjector组件,NestedScrollView的内容会和头部栏重叠。", -// } - -class SliverOverlapAbsorberDemo extends StatelessWidget { - const SliverOverlapAbsorberDemo({Key? key}) : super(key: key); - - final List _tabs = const ['风神传', '封妖志', "幻将录", "永恒传说"]; - - @override - Widget build(BuildContext context) { - return SizedBox( - width: MediaQuery.of(context).size.width, - height: MediaQuery.of(context).size.height - 200, - child: Scaffold( - body: DefaultTabController( - length: _tabs.length, - child: NestedScrollView( - headerSliverBuilder: - (BuildContext context, bool innerBoxIsScrolled) { - return [ - SliverOverlapAbsorber( - handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), - sliver: SliverAppBar( - title: const Text('旷古奇书'), - pinned: true, - elevation: 6, //影深 - expandedHeight: 220.0, - forceElevated: innerBoxIsScrolled, //为true时展开有阴影 - flexibleSpace: FlexibleSpaceBar( - background: Image.asset( - "assets/images/wy_300x200_filter.webp", - fit: BoxFit.cover, - ), - ), - bottom: TabBar( - tabs: _tabs - .map((String name) => Tab(text: name,)) - .toList(), - ), - ), - ), - ]; - }, - body: _buildTabBarView(), - ), - ), - )); - } - - Widget _buildTabBarView() { - return TabBarView( - children: _tabs.map((String name) { - return SafeArea( - top: false, - bottom: false, - child: Builder( - builder: (BuildContext context) { - return CustomScrollView( - key: PageStorageKey(name), - slivers: [ - SliverOverlapInjector( - handle: NestedScrollView.sliverOverlapAbsorberHandleFor( - context), - ), - buildContent(name), - ], - ); - }, - ), - ); - }).toList(), - ); - } - - Widget buildContent(String name) => SliverPadding( - padding: const EdgeInsets.all(8.0), - sliver: SliverFixedExtentList( - itemExtent: 48.0, - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) { - return ListTile( - title: Text('《$name》 第 $index章'), - ); - }, - childCount: 50, - ), - ), - ); -} diff --git a/packages/widgets/lib/Sliver/SliverOverlapInjector/node1_base.dart b/packages/widgets/lib/Sliver/SliverOverlapInjector/node1_base.dart deleted file mode 100644 index 4bd83083b..000000000 --- a/packages/widgets/lib/Sliver/SliverOverlapInjector/node1_base.dart +++ /dev/null @@ -1,101 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/6/16 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 308, -// "name": 'SliverOverlapInjector基本使用', -// "priority": 1, -// "subtitle": "【sliver】 : 子组件 【Widget】\n" -// "【handle】 : *处理器 【SliverOverlapAbsorberHandle】\n" -// "如果不使用SliverOverlapAbsorber和SliverOverlapInjector组件,NestedScrollView的内容会和头部栏重叠。", -// } - -class SliverOverlapInjectorDemo extends StatelessWidget { - const SliverOverlapInjectorDemo({Key? key}) : super(key: key); - - final List _tabs = const ['风神传', '封妖志', "幻将录", "永恒传说"]; - - @override - Widget build(BuildContext context) { - return SizedBox( - width: MediaQuery.of(context).size.width, - height: MediaQuery.of(context).size.height - 200, - child: Scaffold( - body: DefaultTabController( - length: _tabs.length, - child: NestedScrollView( - headerSliverBuilder: - (BuildContext context, bool innerBoxIsScrolled) { - return [ - SliverOverlapAbsorber( - handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), - sliver: SliverAppBar( - title: const Text('旷古奇书'), - pinned: true, - elevation: 6, //影深 - expandedHeight: 220.0, - forceElevated: innerBoxIsScrolled, //为true时展开有阴影 - flexibleSpace: FlexibleSpaceBar( - background: Image.asset( - "assets/images/wy_300x200_filter.webp", - fit: BoxFit.cover, - ), - ), - bottom: TabBar( - tabs: _tabs - .map((String name) => Tab(text: name,)) - .toList(), - ), - ), - ), - ]; - }, - body: _buildTabBarView(), - ), - ), - )); - } - - Widget _buildTabBarView() { - return TabBarView( - children: _tabs.map((String name) { - return SafeArea( - top: false, - bottom: false, - child: Builder( - builder: (BuildContext context) { - return CustomScrollView( - key: PageStorageKey(name), - slivers: [ - SliverOverlapInjector( - handle: NestedScrollView.sliverOverlapAbsorberHandleFor( - context), - ), - buildContent(name), - ], - ); - }, - ), - ); - }).toList(), - ); - } - - Widget buildContent(String name) => SliverPadding( - padding: const EdgeInsets.all(8.0), - sliver: SliverFixedExtentList( - itemExtent: 48.0, - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) { - return ListTile( - title: Text('《$name》 第 $index章'), - ); - }, - childCount: 50, - ), - ), - ); -} diff --git a/packages/widgets/lib/Sliver/SliverPadding/node1_base.dart b/packages/widgets/lib/Sliver/SliverPadding/node1_base.dart deleted file mode 100644 index 88343772e..000000000 --- a/packages/widgets/lib/Sliver/SliverPadding/node1_base.dart +++ /dev/null @@ -1,94 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-31 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 191, -// "name": 'SliverPadding基本使用', -// "priority": 1, -// "subtitle": "【sliver】 : 子组件 【Widget】\n" -// "【padding】 : 内边距 【EdgeInsetsGeometry】", -// } -class SliverPaddingDemo extends StatelessWidget { - SliverPaddingDemo({Key? key}) : super(key: key); - - final data = List.generate(128, (i) => Color(0xFF6600FF - 2 * i)); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 300, - child: CustomScrollView( - slivers: [ - _buildSliverAppBar(), - SliverPadding( - padding: const EdgeInsets.only(top: 10), - sliver: _buildSliverGrid()) - ], - ), - ); - } - - Widget _buildSliverGrid() => SliverGrid.extent( - childAspectRatio: 1 / 0.618, - maxCrossAxisExtent: 180, - crossAxisSpacing: 5, - mainAxisSpacing: 5, - children: data - .map((e) => Container( - alignment: Alignment.center, - width: 100, - height: 60, - color: e, - child: Text( - colorString(e), - style: const TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2) - ]), - ), - )) - .toList(), - ); - - Widget _buildSliverAppBar() { - return SliverAppBar( - expandedHeight: 190.0, - leading: _buildLeading(), - title: const Text('张风捷特烈'), - actions: _buildActions(), - elevation: 5, - pinned: true, - backgroundColor: Colors.orange, - flexibleSpace: FlexibleSpaceBar( - //伸展处布局 - titlePadding: const EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } - - Widget _buildLeading() => Container( - margin: const EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')); - - List _buildActions() => [ - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.star_border, - color: Colors.white, - ), - ) - ]; - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/packages/widgets/lib/Sliver/SliverPersistentHeader/node1_base.dart b/packages/widgets/lib/Sliver/SliverPersistentHeader/node1_base.dart deleted file mode 100644 index bed043a1c..000000000 --- a/packages/widgets/lib/Sliver/SliverPersistentHeader/node1_base.dart +++ /dev/null @@ -1,167 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-31 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 190, -// "name": 'SliverPersistentHeader基本使用', -// "priority": 1, -// "subtitle": -// "【delegate】 : 代理 【SliverPersistentHeaderDelegate】\n" -// "【floating】 : 是否浮动 【bool】\n" -// "【pinned】 : 是否顶部停留 【bool】", -// } -class SliverPersistentHeaderDemo extends StatelessWidget { - SliverPersistentHeaderDemo({Key? key}) : super(key: key); - - final List data = [ - Colors.purple[50]!, - Colors.purple[100]!, - Colors.purple[200]!, - Colors.purple[300]!, - Colors.purple[400]!, - Colors.purple[500]!, - Colors.purple[600]!, - Colors.purple[700]!, - Colors.purple[800]!, - Colors.purple[900]!, - ]; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 500, - child: CustomScrollView( - slivers: [ - _buildSliverAppBar(), - _buildPersistentHeader('袅缈岁月,青丝银发',const Color(0xffe7fcc9)), - _buildCommonWidget(), - _buildPersistentHeader('以梦为马,不负韶华',const Color(0xffcca4ff)), - _buildSliverList() - ], - ), - ); - } - - Widget _buildCommonWidget() => SliverToBoxAdapter( - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 10), - color: Colors.grey.withAlpha(22), - child: ListTile( - leading: Image.asset("assets/images/icon_head.webp"), - title: const Text("以梦为马"), - subtitle: const Text("海子"), - selected: true, - contentPadding: const EdgeInsets.all(5), - trailing: const Icon(Icons.more_vert), - ), - ), - ); - Widget _buildPersistentHeader(String text,Color color) => SliverPersistentHeader( - pinned: true, - delegate: _SliverDelegate( - minHeight: 40.0, - maxHeight: 100.0, - child: Container( - color: color, - child: Center( - child: Text(text, style: const TextStyle( - fontSize: 18, - shadows: [Shadow(color: Colors.white, offset: Offset(1, 1))]), - ), - )), - )); - - Widget _buildSliverList() => SliverList( - delegate: SliverChildBuilderDelegate( - (_, int index) => Container( - alignment: Alignment.center, - width: 100, - height: 60, - color: data[index], - child: Text( - colorString(data[index]), - style: const TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2) - ]), - ), - ), - childCount: data.length), - ); - - Widget _buildSliverAppBar() { - return SliverAppBar( - expandedHeight: 190.0, - leading: _buildLeading(), - title: const Text('张风捷特烈'), - actions: _buildActions(), - elevation: 2, - pinned: true, - backgroundColor: Colors.orange, - flexibleSpace: FlexibleSpaceBar( - //伸展处布局 - titlePadding: const EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } - - Widget _buildLeading() => Container( - margin: const EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')); - - List _buildActions() => [ - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.star_border, - color: Colors.white, - ), - ) - ]; - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} - - -class _SliverDelegate extends SliverPersistentHeaderDelegate { - _SliverDelegate({ - required this.minHeight, - required this.maxHeight, - required this.child, - }); - - final double minHeight; //最小高度 - final double maxHeight; //最大高度 - final Widget child; //孩子 - - @override - double get minExtent => minHeight; - - @override - double get maxExtent => max(maxHeight, minHeight); - - @override - Widget build( - BuildContext context, double shrinkOffset, bool overlapsContent) { - return SizedBox.expand(child: child); - } - - @override //是否需要重建 - bool shouldRebuild(_SliverDelegate oldDelegate) { - return maxHeight != oldDelegate.maxHeight || - minHeight != oldDelegate.minHeight || - child != oldDelegate.child; - } -} \ No newline at end of file diff --git a/packages/widgets/lib/Sliver/SliverPrototypeExtentList/node1_base.dart b/packages/widgets/lib/Sliver/SliverPrototypeExtentList/node1_base.dart deleted file mode 100644 index c28798641..000000000 --- a/packages/widgets/lib/Sliver/SliverPrototypeExtentList/node1_base.dart +++ /dev/null @@ -1,107 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 314 SliverPrototypeExtentList Sliver原型延伸列表 其中prototypeItem属性是一个Widget,该Widget负责在主轴方向上约束item尺寸,但会不显示出来。delegate接受一个SliverChildDelegate完成item的创建。 -// { -// "widgetId": 314, -// "name": 'SliverPrototypeExtentList基本使用', -// "priority": 1, -// "subtitle": -// "【prototypeItem】 : 主轴方向尺寸组件 【Widget】\n" -// "【delegate】 : 孩子代理 【SliverChildDelegate】", -// } -class SliverPrototypeExtentListDemo extends StatefulWidget { - const SliverPrototypeExtentListDemo({Key? key}) : super(key: key); - - @override - _SliverPrototypeExtentListDemoState createState() => - _SliverPrototypeExtentListDemoState(); -} - -class _SliverPrototypeExtentListDemoState - extends State { - final List data = [ - Colors.orange[50]!, - Colors.orange[100]!, - Colors.orange[200]!, - Colors.orange[300]!, - Colors.orange[400]!, - Colors.orange[500]!, - Colors.orange[600]!, - Colors.orange[700]!, - Colors.orange[800]!, - Colors.orange[900]!, - ]; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 300, - child: CustomScrollView( - slivers: [_buildSliverAppBar(), _buildSliverList()], - ), - ); - } - - Widget _buildSliverList() => SliverPrototypeExtentList( - prototypeItem: Container( - height: 80, - ), - delegate: SliverChildBuilderDelegate( - (_, int index) => Container( - alignment: Alignment.center, - width: 100, - height: 60, - color: data[index], - child: Text( - colorString(data[index]), - style: const TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2) - ]), - ), - ), - childCount: data.length), - ); - - Widget _buildSliverAppBar() { - return SliverAppBar( - expandedHeight: 150.0, - leading: _buildLeading(), - title: const Text('张风捷特烈'), - actions: _buildActions(), - elevation: 5, - pinned: true, - backgroundColor: Colors.orange, - flexibleSpace: FlexibleSpaceBar( - //伸展处布局 - titlePadding: const EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } - - Widget _buildLeading() => Container( - margin: const EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')); - - List _buildActions() => [ - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.star_border, - color: Colors.white, - ), - ) - ]; - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/packages/widgets/lib/Sliver/SliverToBoxAdapter/node1_base.dart b/packages/widgets/lib/Sliver/SliverToBoxAdapter/node1_base.dart deleted file mode 100644 index ea2839654..000000000 --- a/packages/widgets/lib/Sliver/SliverToBoxAdapter/node1_base.dart +++ /dev/null @@ -1,114 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-31 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 189, -// "name": 'SliverToBoxAdapter基本使用', -// "priority": 1, -// "subtitle": "【child】 : 子组件 【Widget】", -// } -class SliverToBoxAdapterDemo extends StatelessWidget { - SliverToBoxAdapterDemo({Key? key}) : super(key: key); - - final List data = [ - Colors.purple[50]!, - Colors.purple[100]!, - Colors.purple[200]!, - Colors.purple[300]!, - Colors.purple[400]!, - Colors.purple[500]!, - Colors.purple[600]!, - Colors.purple[700]!, - Colors.purple[800]!, - Colors.purple[900]!, - ]; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 300, - child: CustomScrollView( - slivers: [ - _buildSliverAppBar(), - _buildCommonWidget(), - _buildSliverList() - ], - ), - ); - } - - Widget _buildCommonWidget() => SliverToBoxAdapter( - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 10), - color: Colors.grey.withAlpha(22), - child: ListTile( - leading: Image.asset("assets/images/icon_head.webp"), - title: const Text("以梦为马"), - subtitle: const Text("海子"), - selected: true, - contentPadding: const EdgeInsets.all(5), - trailing: const Icon(Icons.more_vert), - ), - ), - ); - - Widget _buildSliverList() => SliverList( - delegate: SliverChildBuilderDelegate( - (_, int index) => Container( - alignment: Alignment.center, - width: 100, - height: 60, - color: data[index], - child: Text( - colorString(data[index]), - style: const TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2) - ]), - ), - ), - childCount: data.length), - ); - - Widget _buildSliverAppBar() { - return SliverAppBar( - expandedHeight: 190.0, - leading: _buildLeading(), - title: const Text('张风捷特烈'), - actions: _buildActions(), - elevation: 2, - pinned: true, - backgroundColor: Colors.orange, - flexibleSpace: FlexibleSpaceBar( - //伸展处布局 - titlePadding: const EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } - - Widget _buildLeading() => Container( - margin: const EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')); - - List _buildActions() => [ - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.star_border, - color: Colors.white, - ), - ) - ]; - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/packages/widgets/lib/Sliver/SliverWithKeepAliveWidget/node1_base.dart b/packages/widgets/lib/Sliver/SliverWithKeepAliveWidget/node1_base.dart deleted file mode 100644 index 1e2ddc309..000000000 --- a/packages/widgets/lib/Sliver/SliverWithKeepAliveWidget/node1_base.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 348 SliverWithKeepAliveWidget Sliver保活容器 -/// 它是抽象类,不能单独使用。只有其子类才可以容纳 KeepAlive 的孩子。 -/// link 316,239,188,185,314,186 -/// -// { -// "widgetId": 348, -// "name": 'SliverWithKeepAliveWidget 介绍', -// "priority": 1, -// "subtitle": -// "【key】 : 键 【Key】", -// } - - -class SliverWithKeepAliveWidgetDemo extends StatelessWidget { - const SliverWithKeepAliveWidgetDemo({Key? key}) : super(key: key); - - final String info = - '只有 SliverWithKeepAliveWidget 之下才可以包含 KeepAlive 组件, 由于其为抽象类,不能直接使用。其子类 SliverMultiBoxAdaptorWidget 也说抽象类,' - '用于容纳多个孩子,帮助它的子类使用 SliverChildDelegate 构建懒加载 children。' - '最终实现类为 SliverGrid、SliverList、SliverPrototypeExtentList、SliverFixedExtentList,表示他们都可以支持 item 的状态保持。' - '除此之外还有 _SliverFillViewportRenderObjectWidget 的私有实现类,这是 PageView 的底层实现,这也是为什么 PageView 也支持保活的原因。'; - - @override - Widget build(BuildContext context) { - - return Container( - color: Theme.of(context).primaryColor.withOpacity(0.1), - padding: const EdgeInsets.all(10), - margin: const EdgeInsets.all(10), - child: Text(info), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatefulWidget/AlignTransition/node1_base.dart b/packages/widgets/lib/StatefulWidget/AlignTransition/node1_base.dart deleted file mode 100644 index 6a692e586..000000000 --- a/packages/widgets/lib/StatefulWidget/AlignTransition/node1_base.dart +++ /dev/null @@ -1,62 +0,0 @@ - -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 111, -// "name": 'AlignTransition基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】\n" -// "【alignment】 : 对齐动画 【Animation】", -// } -class CustomAlignTransition extends StatefulWidget { - const CustomAlignTransition({Key? key}) : super(key: key); - - @override - _CustomAlignTransitionState createState() => _CustomAlignTransitionState(); -} - -class _CustomAlignTransitionState extends State - with SingleTickerProviderStateMixin { - late AnimationController _ctrl; - - @override - void initState() { - _ctrl = AnimationController( - vsync: this, - duration: const Duration(seconds: 1), - ); - _ctrl.forward(); - super.initState(); - } - - @override - void dispose() { - _ctrl.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: () => _ctrl.forward(from: 0), - child: Container( - width: MediaQuery.of(context).size.width, - color: Colors.grey.withAlpha(33), - height: 100, - child: AlignTransition( - alignment: AlignmentTween( - begin: Alignment.topLeft, - end: Alignment.bottomRight, - ).animate(_ctrl), - child: const Icon( - Icons.android, - color: Colors.green, - size: 60, - ), - ), - )); - } -} diff --git a/packages/widgets/lib/StatefulWidget/AnimatedAlign/node1_base.dart b/packages/widgets/lib/StatefulWidget/AnimatedAlign/node1_base.dart deleted file mode 100644 index b51d8614e..000000000 --- a/packages/widgets/lib/StatefulWidget/AnimatedAlign/node1_base.dart +++ /dev/null @@ -1,73 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-23 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 120, -// "name": 'AnimatedAlign基本使用', -// "priority": 1, -// "subtitle": "【child】 : 孩子组件 【Widget】\n" -// "【duration】 : 动画时长 【Duration】\n" -// "【onEnd】 : 动画结束回调 【Function()】\n" -// "【alignment】 : 对齐方式 【AlignmentGeometry】\n" -// "【curve】 : 动画曲线 【Duration】\n" -// "【padding】 : 内边距 【EdgeInsetsGeometry】", -// } -class CustomAnimatedAlign extends StatefulWidget { - const CustomAnimatedAlign({Key? key}) : super(key: key); - - @override - _CustomAnimatedAlignState createState() => _CustomAnimatedAlignState(); -} - -class _CustomAnimatedAlignState extends State { - final Alignment start = const Alignment(0, 0); - final Alignment end = Alignment.bottomRight; - - late Alignment _alignment; - - @override - void initState() { - _alignment = start; - super.initState(); - } - - @override - Widget build(BuildContext context) { - return Column( - children: [ - _buildSwitch(), - Container( - color: Colors.grey.withAlpha(22), - width: 200, - height: 100, - child: AnimatedAlign( - duration: const Duration(seconds: 1), - curve: Curves.fastOutSlowIn, - alignment: _alignment, - onEnd: () => print('End'), - child: Container( - height: 40, - width: 80, - alignment: Alignment.center, - color: Colors.blue, - child: const Text( - '张风捷特烈', - style: TextStyle(color: Colors.white), - ), - ), - ), - ), - ], - ); - } - - Widget _buildSwitch() => Switch( - value: _alignment == end, - onChanged: (v) { - setState(() { - _alignment = v ? end : start; - }); - }); -} diff --git a/packages/widgets/lib/StatefulWidget/AnimatedBuilder/node1_base.dart b/packages/widgets/lib/StatefulWidget/AnimatedBuilder/node1_base.dart deleted file mode 100644 index 2f616934d..000000000 --- a/packages/widgets/lib/StatefulWidget/AnimatedBuilder/node1_base.dart +++ /dev/null @@ -1,73 +0,0 @@ - -import 'package:flutter/material.dart'; - - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 228 AnimatedBuilder 动画构造器 -/// 通过 builder 使动画对应的节点变为局部更新,并且可避免子组件刷新,减少构建的时间,提高动画性能。 -/// -// { -// "widgetId": 228, -// "name": 'AnimatedBuilder 使用案例', -// "priority": 1, -// "subtitle": -// "【animation】 : *可监听对象 【Listenable】\n" -// "【builder】 : *组件构造器 【TransitionBuilder】\n" -// "【child】 : 子组件 【Widget】", -// } - -class AnimatedBuilderDemo extends StatefulWidget { - const AnimatedBuilderDemo({Key? key}) : super(key: key); - - @override - _AnimatedBuilderDemoState createState() => _AnimatedBuilderDemoState(); -} - -class _AnimatedBuilderDemoState extends State - with SingleTickerProviderStateMixin { - late AnimationController controller; - - @override - void initState() { - super.initState(); - controller = AnimationController( - vsync: this, - lowerBound: 0.3, - upperBound: 1.0, - duration: const Duration(milliseconds: 500)) - ..forward(); - } - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: () { - controller.forward(from: 0); - }, - child: AnimatedBuilder( - animation: controller, - builder: (ctx, child) { - return Transform.scale( - scale: controller.value, - child: Opacity(opacity: controller.value, child: child), - ); - }, - child: buildChild()), - ); - } - - Widget buildChild() => Container( - height: 100, - width: 100, - decoration: const BoxDecoration( - color: Colors.orange, - shape: BoxShape.circle, - ), - alignment: Alignment.center, - child: const Text( - 'Toly', - style: TextStyle(fontSize: 40, color: Colors.white), - ), - ); -} diff --git a/packages/widgets/lib/StatefulWidget/AnimatedContainer/node1_base.dart b/packages/widgets/lib/StatefulWidget/AnimatedContainer/node1_base.dart deleted file mode 100644 index 274cb01f2..000000000 --- a/packages/widgets/lib/StatefulWidget/AnimatedContainer/node1_base.dart +++ /dev/null @@ -1,113 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-23 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 123, -// "name": 'AnimatedContainer基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】\n" -// "【duration】 : 动画时长 【Duration】\n" -// "【onEnd】 : 动画结束回调 【Function()】\n" -// "【curve】 : 动画曲线 【Duration】\n" -// "【color】 : 颜色 【Color】\n" -// "【width】 : 宽 【double】\n" -// "【height】 : 高 【double】\n" -// "【alignment】 : 对齐 【AlignmentGeometry】\n" -// "【decoration】 : 装饰 【Decoration】\n" -// "【constraints】 : 约束 【BoxConstraints】\n" -// "【transform】 : 变化 【Matrix4】\n" -// "【margin】 : 外边距 【EdgeInsetsGeometry】\n" -// "【padding】 : 内边距 【EdgeInsetsGeometry】", -// } -class CustomAnimatedContainer extends StatefulWidget { - const CustomAnimatedContainer({Key? key}) : super(key: key); - - @override - _CustomAnimatedContainerState createState() => - _CustomAnimatedContainerState(); -} - -class _CustomAnimatedContainerState extends State { - final Decoration startDecoration = const BoxDecoration( - color: Colors.deepPurple, - borderRadius: BorderRadius.all(Radius.circular(30)), - boxShadow: [ - BoxShadow( - offset: Offset(1, 1), - color: Colors.purple, - blurRadius: 5, - spreadRadius: 2) - ]); - final Decoration endDecoration = const BoxDecoration( - color: Colors.blue, - borderRadius: BorderRadius.all(Radius.circular(10)), - boxShadow: [ - BoxShadow( - offset: Offset(1, 1), - color: Colors.blue, - blurRadius: 10, - spreadRadius: 0) - ]); - - final Alignment startAlignment = Alignment.topLeft + const Alignment(0.2, 0.2); - final Alignment endAlignment = Alignment.center; - - final double startHeight = 150.0; - final double endHeight = 100.0; - - late Decoration _decoration; - late double _height; - late Alignment _alignment; - - @override - void initState() { - super.initState(); - _decoration = startDecoration; - _height = startHeight; - _alignment = startAlignment; - } - - bool get selected => _height == endHeight; - @override - Widget build(BuildContext context) { - return Wrap( - direction: Axis.vertical, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Switch( - value: selected, - onChanged: onChanged, - ), - buildAnim() - ], - ); - } - Widget _buildChild() => const Icon( - Icons.camera_outlined, - size: 30, - color: Colors.white, - ); - Widget buildAnim() => AnimatedContainer( - duration: const Duration(seconds: 1), - curve: Curves.fastOutSlowIn, - decoration: _decoration, - alignment: _alignment, - onEnd: onEnd, - height: _height, - width: _height, - child: _buildChild(), - ); - void onChanged(bool value) { - setState(() { - _height = value ? endHeight : startHeight; - _decoration = value ? endDecoration : startDecoration; - _alignment = value ? endAlignment : startAlignment; - }); - } - void onEnd() { - print('End'); - } -} diff --git a/packages/widgets/lib/StatefulWidget/AnimatedCrossFade/node1_base.dart b/packages/widgets/lib/StatefulWidget/AnimatedCrossFade/node1_base.dart deleted file mode 100644 index 766d12fa4..000000000 --- a/packages/widgets/lib/StatefulWidget/AnimatedCrossFade/node1_base.dart +++ /dev/null @@ -1,70 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-20 -/// contact me by email 1981462002@qq.com -/// 说明: - - -// { -// "widgetId": 100, -// "name": 'AnimatedCrossFade基本使用', -// "priority": 1, -// "subtitle": -// "【firstChild】 : 第一孩子 【Widget】\n" -// "【secondChild】 : 第二孩子 【Widget】\n" -// "【crossFadeState】 : 显示第几个 【CrossFadeState】\n" -// "【duration】 : 时长 【Duration】", -// } -class CustomAnimatedCrossFade extends StatefulWidget { - const CustomAnimatedCrossFade({Key? key}) : super(key: key); - - @override - _CustomAnimatedCrossFadeState createState() => - _CustomAnimatedCrossFadeState(); -} - -class _CustomAnimatedCrossFadeState extends State { - CrossFadeState _crossFadeState = CrossFadeState.showFirst; - - bool get isFirst => _crossFadeState == CrossFadeState.showFirst; - - @override - Widget build(BuildContext context) { - return Wrap( - children: [ - AnimatedCrossFade( - firstChild: Container( - alignment: Alignment.center, - width: 200, - height: 150, - color: Colors.orange, - child: const FlutterLogo(textColor: Colors.blue, size: 100,), - ), - secondChild: Container( - width: 200, - height: 150, - alignment: Alignment.center, - color: Colors.blue, - child: const FlutterLogo( - textColor: Colors.white, -// colors: Colors.orange, - size: 100, - style: FlutterLogoStyle.stacked,), - ), - duration: const Duration(milliseconds: 600), - crossFadeState: _crossFadeState, - ), - _buildSwitch(), - ], - - ); - } - - Widget _buildSwitch() => - Switch(value: isFirst, onChanged: (v) { - setState(() { - _crossFadeState = - v ? CrossFadeState.showFirst : CrossFadeState.showSecond; - }); - }); -} \ No newline at end of file diff --git a/packages/widgets/lib/StatefulWidget/AnimatedCrossFade/node2_curve.dart b/packages/widgets/lib/StatefulWidget/AnimatedCrossFade/node2_curve.dart deleted file mode 100644 index eab906174..000000000 --- a/packages/widgets/lib/StatefulWidget/AnimatedCrossFade/node2_curve.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-20 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 100, -// "name": 'AnimatedCrossFade动画曲线', -// "priority": 2, -// "subtitle": -// "【firstCurve】 : 第一曲线 【Curve】\n" -// "【secondCurve】 : 第二曲线 【Curve】\n" -// "【sizeCurve】 : 尺寸变化曲线 【CrossFadeState】", -// } -class CurveAnimatedCrossFade extends StatefulWidget { - const CurveAnimatedCrossFade({Key? key}) : super(key: key); - - @override - _CurveAnimatedCrossFadeState createState() => _CurveAnimatedCrossFadeState(); -} - -class _CurveAnimatedCrossFadeState extends State { - var _crossFadeState = CrossFadeState.showFirst; - - bool get isFirst=> _crossFadeState == CrossFadeState.showFirst; - - @override - Widget build(BuildContext context) { - return Wrap( - children: [ - AnimatedCrossFade( - firstCurve: Curves.easeInCirc, - secondCurve: Curves.easeInToLinear, - sizeCurve: Curves.bounceOut, - firstChild: Container( - alignment: Alignment.center, - width: 200, - height: 80, - color: Colors.orange , - child: const FlutterLogo(textColor: Colors.blue,size: 50,), - ), - secondChild: Container( - width: 200, - height: 150, - alignment: Alignment.center, - color: Colors.blue, - child: const FlutterLogo( - textColor: Colors.white, -// colors: Colors.orange, - size: 100,style: FlutterLogoStyle.stacked,), - ), - duration: const Duration(milliseconds: 1000), - crossFadeState: _crossFadeState, - ), - _buildSwitch(), - ], - ); - } - - Widget _buildSwitch() => Switch(value: isFirst, onChanged: (v){ - setState(() { - _crossFadeState= v?CrossFadeState.showFirst:CrossFadeState.showSecond; - }); - }); -} diff --git a/packages/widgets/lib/StatefulWidget/AnimatedDefaultTextStyle/node1_base.dart b/packages/widgets/lib/StatefulWidget/AnimatedDefaultTextStyle/node1_base.dart deleted file mode 100644 index 71ccd188c..000000000 --- a/packages/widgets/lib/StatefulWidget/AnimatedDefaultTextStyle/node1_base.dart +++ /dev/null @@ -1,88 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-23 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 124, -// "name": 'AnimatedDefaultTextStyle基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】\n" -// "【duration】 : 动画时长 【Duration】\n" -// "【onEnd】 : 动画结束回调 【Function()】\n" -// "【curve】 : 动画曲线 【Duration】\n" -// "【textAlign】 : 文字对齐方式 【TextAlign】\n" -// "【softWrap】 : 是否包裹 【bool】\n" -// "【maxLines】 : 最大行数 【int】\n" -// "【overflow】 : 溢出模式 【TextOverflow】\n" -// "【style】 : 文字样式 【TextStyle】", -// } -class CustomAnimatedDefaultTextStyle extends StatefulWidget { - const CustomAnimatedDefaultTextStyle({Key? key}) : super(key: key); - - @override - _CustomAnimatedDefaultTextStyleState createState() => - _CustomAnimatedDefaultTextStyleState(); -} - -class _CustomAnimatedDefaultTextStyleState - extends State { - final TextStyle start = const TextStyle( - color: Colors.blue, - fontSize: 50, - shadows: [ - Shadow(offset: Offset(1, 1), color: Colors.black, blurRadius: 3) - ]); - final TextStyle end = const TextStyle( - color: Colors.white, - fontSize: 20, - shadows: [ - Shadow(offset: Offset(1, 1), color: Colors.purple, blurRadius: 3) - ]); - - late TextStyle _style; - - @override - void initState() { - _style = start; - super.initState(); - } - - @override - Widget build(BuildContext context) { - return Column( - children: [ - _buildSwitch(), - Container( - alignment: Alignment.center, - color: Colors.grey.withAlpha(22), - width: 300, - height: 100, - child: AnimatedDefaultTextStyle( - textAlign: TextAlign.start, - softWrap: true, - maxLines: 1, - overflow: TextOverflow.ellipsis, - duration: const Duration(seconds: 1), - curve: Curves.fastOutSlowIn, - style: _style, - onEnd: () => print('End'), - child: const Text( - '张风捷特烈', - style: TextStyle(color: Colors.white), - ), - ), - ), - ], - ); - } - - Widget _buildSwitch() => Switch( - value: _style == end, - onChanged: (v) { - setState(() { - _style = v ? end : start; - }); - }); -} diff --git a/packages/widgets/lib/StatefulWidget/AnimatedList/node1_base.dart b/packages/widgets/lib/StatefulWidget/AnimatedList/node1_base.dart deleted file mode 100644 index 057304679..000000000 --- a/packages/widgets/lib/StatefulWidget/AnimatedList/node1_base.dart +++ /dev/null @@ -1,192 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-23 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 117, -// "name": 'AnimatedList基本使用', -// "priority": 1, -// "subtitle": "【itemBuilder】 : 组件构造器 【AnimatedListItemBuilder】\n" -// "【initialItemCount】 : 子组件数量 【int】\n" -// "【scrollDirection】 : 滑动方向 【Axis】\n" -// "【controller】 : 滑动控制器 【ScrollController】\n" -// "【reverse】 : 数据是否反向 【bool】\n" -// "【padding】 : 内边距 【EdgeInsetsGeometry】", -// } -class CustomAnimatedList extends StatefulWidget { - const CustomAnimatedList({Key? key}) : super(key: key); - - @override - _CustomAnimatedListState createState() => _CustomAnimatedListState(); -} - -class _CustomAnimatedListState extends State { - final GlobalKey _listKey = GlobalKey(); - late ListModel _list; - int? _selectedItem; - int _nextItem = 0; - - @override - void initState() { - super.initState(); - _list = ListModel( - listKey: _listKey, - initialItems: [0, 1, 2, 3], - removedItemBuilder: _buildRemovedItem, - ); - _nextItem = 4; - } - - Widget _buildItem( - BuildContext context, int index, Animation animation) { - return CardItem( - animation: animation, - item: _list[index], - selected: _selectedItem == _list[index], - onTap: () { - setState(() { - _selectedItem = _selectedItem == _list[index] ? null : _list[index]; - }); - }, - ); - } - - Widget _buildRemovedItem( - int item, BuildContext context, Animation animation) { - return CardItem( - animation: animation, - item: item, - selected: false, - ); - } - - void _insert() { - final int index = - _selectedItem == null ? _list.length : _list.indexOf(_selectedItem!); - _list.insert(index, _nextItem++); - } - - void _remove() { - if (_selectedItem != null) { - _list.removeAt(_list.indexOf(_selectedItem!)); - setState(() { - _selectedItem = null; - }); - } - } - - @override - Widget build(BuildContext context) { - return Container( - color: Colors.grey.withAlpha(33), - width: MediaQuery.of(context).size.width/2, - child: Column( - children: [ - _buildBtn(), - SizedBox( - width: MediaQuery.of(context).size.width/2, - height: 300, - child: AnimatedList( - padding: const EdgeInsets.all(10.0), - key: _listKey, - initialItemCount: _list.length, - itemBuilder: _buildItem, - ), - ) - ], - )); - } - - Widget _buildBtn() => Row( - children: [ - IconButton( - icon: const Icon( - Icons.add_circle, - color: Colors.blue, - ), - onPressed: _insert, - ), - IconButton( - icon: const Icon(Icons.remove_circle, color: Colors.blue), - onPressed: _remove, - ), - ], - ); -} - -class ListModel { - ListModel({ - required this.listKey, - required this.removedItemBuilder, - required Iterable initialItems, - }) : assert(removedItemBuilder != null), - _items = List.from(initialItems); - final GlobalKey listKey; - final dynamic removedItemBuilder; - final List _items; - - AnimatedListState? get _animatedList => listKey.currentState; - - void insert(int index, E item) { - _items.insert(index, item); - _animatedList?.insertItem(index); - } - - E removeAt(int index) { - final E removedItem = _items.removeAt(index); - if (removedItem != null) { - _animatedList?.removeItem( - index, - (BuildContext context, Animation animation) => - removedItemBuilder(removedItem, context, animation), - ); - } - return removedItem; - } - - int get length => _items.length; - - E operator [](int index) => _items[index]; - - int indexOf(E item) => _items.indexOf(item); -} - -class CardItem extends StatelessWidget { - const CardItem( - {Key? key, - required this.animation, - this.onTap, - required this.item, - this.selected = false}) - : assert(item >= 0), - super(key: key); - final Animation animation; - final VoidCallback? onTap; - final int item; - final bool selected; - - @override - Widget build(BuildContext context) { - return SizeTransition( - axis: Axis.vertical, - sizeFactor: animation, - child: Card( - child: Container( - color: Colors.primaries[item % Colors.primaries.length], - child: CheckboxListTile( - dense: true, - title: Text( - 'Item $item', - style: const TextStyle(color: Colors.white, fontSize: 18), - ), - value: selected, - onChanged: (v) { - onTap?.call(); - }), - ), - ), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/AnimatedModalBarrier/node1_base.dart b/packages/widgets/lib/StatefulWidget/AnimatedModalBarrier/node1_base.dart deleted file mode 100644 index 99289c481..000000000 --- a/packages/widgets/lib/StatefulWidget/AnimatedModalBarrier/node1_base.dart +++ /dev/null @@ -1,65 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-04-01 -/// contact me by email 1981462002@qq.com -/// 说明: 227 AnimatedModalBarrier 动画屏障模 -/// 内部依赖 ModalBarrier 实现,功能一致,只不过该组件可以传入一个颜色动画,进行过渡展现。 -/// link: 212 -// { -// "widgetId": 227, -// "name": 'AnimatedModalBarrier 介绍', -// "priority": 1, -// "subtitle": -// "【dismissible】 : 点击是否返回 【bool】\n" -// "【color】 : 颜色 【Animation】", -// } -class AnimatedModalBarrierDemo extends StatefulWidget { - const AnimatedModalBarrierDemo({Key? key}) : super(key: key); - - @override - _AnimatedModalBarrierDemoState createState() => - _AnimatedModalBarrierDemoState(); -} - -class _AnimatedModalBarrierDemoState extends State - with SingleTickerProviderStateMixin { - late AnimationController _controller; - late Animation _color; - - @override - void initState() { - super.initState(); - _controller = AnimationController( - vsync: this, - duration: const Duration(seconds: 2), - )..forward(); - _color = ColorTween( - begin: Colors.blue, - end: Colors.purple, - ).animate(_controller); - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return SizedBox( - width: 200, - height: 100, - child: Stack(alignment: Alignment.center, children: [ - AnimatedModalBarrier( - dismissible: true, - color: _color, - ), - const Text( - '点击背景返回', - style: TextStyle(color: Colors.white), - ) - ]), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/AnimatedOpacity/node1_base.dart b/packages/widgets/lib/StatefulWidget/AnimatedOpacity/node1_base.dart deleted file mode 100644 index f22efe950..000000000 --- a/packages/widgets/lib/StatefulWidget/AnimatedOpacity/node1_base.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-23 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 118, -// "name": 'AnimatedOpacity基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】\n" -// "【duration】 : 动画时长 【Duration】\n" -// "【onEnd】 : 动画结束回调 【Function()】\n" -// "【curve】 : 动画曲线 【Duration】\n" -// "【opacity】 : 透明度 【double】", -// } -class CustomAnimatedOpacity extends StatefulWidget { - const CustomAnimatedOpacity({Key? key}) : super(key: key); - - @override - _CustomAnimatedOpacityState createState() => _CustomAnimatedOpacityState(); -} - -class _CustomAnimatedOpacityState extends State { - double _opacity = 1.0; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Switch( - value: _opacity == 0, - onChanged: (v) { - setState(() { - _opacity = v ? 0 : 1.0; - }); - }), - Container( - color: Colors.grey.withAlpha(22), - width: 200, - height: 100, - child: AnimatedOpacity( - duration: const Duration(seconds: 1), - curve: Curves.fastOutSlowIn, - opacity: _opacity, - onEnd: () => print('End'), - child: const Icon(Icons.android, color: Colors.green, size: 60), - ), - ), - ], - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/AnimatedPadding/node1_base.dart b/packages/widgets/lib/StatefulWidget/AnimatedPadding/node1_base.dart deleted file mode 100644 index 0fb58e3e2..000000000 --- a/packages/widgets/lib/StatefulWidget/AnimatedPadding/node1_base.dart +++ /dev/null @@ -1,69 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-23 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 119, -// "name": 'AnimatedPadding基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】\n" -// "【duration】 : 动画时长 【Duration】\n" -// "【onEnd】 : 动画结束回调 【Function()】\n" -// "【curve】 : 动画曲线 【Duration】\n" -// "【padding】 : 内边距 【EdgeInsetsGeometry】", -// } -class CustomAnimatedPadding extends StatefulWidget { - const CustomAnimatedPadding({Key? key}) : super(key: key); - - @override - _CustomAnimatedPaddingState createState() => _CustomAnimatedPaddingState(); -} - -class _CustomAnimatedPaddingState extends State { - final EdgeInsets startPadding = const EdgeInsets.all(10); - final EdgeInsets endPadding = const EdgeInsets.all(30); - - late EdgeInsets _padding; - - @override - void initState() { - _padding = startPadding; - super.initState(); - } - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Switch( - value: _padding == endPadding, - onChanged: (v) { - setState(() { - _padding = v ? endPadding : startPadding; - }); - }), - Container( - color: Colors.grey.withAlpha(22), - width: 200, - height: 100, - child: AnimatedPadding( - duration: const Duration(seconds: 1), - curve: Curves.fastOutSlowIn, - padding: _padding, - onEnd: () => print('End'), - child: Container( - alignment: Alignment.center, - color: Colors.blue, - child: const Text( - '张风捷特烈', - style: TextStyle(color: Colors.white), - ), - ), - ), - ), - ], - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/AnimatedPhysicalModel/node1_base.dart b/packages/widgets/lib/StatefulWidget/AnimatedPhysicalModel/node1_base.dart deleted file mode 100644 index f9e671240..000000000 --- a/packages/widgets/lib/StatefulWidget/AnimatedPhysicalModel/node1_base.dart +++ /dev/null @@ -1,67 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-23 -/// contact me by email 1981462002@qq.com -/// 说明: 225 相关属性变化时具有动画效果的PhysicalModel组件,本质是PhysicalModel和动画结合的产物。可指定阴影、影深、圆角、动画时长、结束回调等属性。 -// { -// "widgetId": 225 , -// "name": 'AnimatedPhysicalModel基本使用', -// "priority": 1, -// "subtitle": -// "【color】 : 背景色 【Color】\n" -// "【duration】 : 动画时长 【Duration】\n" -// "【onEnd】 : 动画结束回调 【Function()】\n" -// "【curve】 : 动画曲线 【Duration】\n" -// "【shape】 : 形状 【BoxShape】\n" -// "【elevation】 : 影深 【double】\n" -// "【borderRadius】 : 圆角 【BorderRadius】\n" -// "【shadowColor】 : 阴影色 【Color】\n" -// "【child】 : 子组件 【Widget】", -// } -class AnimatedPhysicalModelDemo extends StatefulWidget { - const AnimatedPhysicalModelDemo({Key? key}) : super(key: key); - - @override - _AnimatedPhysicalModelDemoState createState() => - _AnimatedPhysicalModelDemoState(); -} - -class _AnimatedPhysicalModelDemoState extends State { - bool flag = false; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - _buildSwitch(), - SizedBox( - width: 150, - height: 150, - child: AnimatedPhysicalModel( - duration: const Duration(seconds: 2), - curve: Curves.fastOutSlowIn, - shadowColor: flag?Colors.orange:Colors.purple, - elevation: flag?10:5, - child: Image.asset( - 'assets/images/caver.webp', - fit: BoxFit.cover, - ), - borderRadius: BorderRadius.all(Radius.circular(flag? 10:75)), - clipBehavior: Clip.hardEdge, - shape: BoxShape.rectangle, - color: Colors.deepPurpleAccent, - onEnd: () => print('----onEnd---'), - ), - ), - ], - ); - } - - Widget _buildSwitch() => Switch( - value: flag, - onChanged: (v) { - setState(() { - flag = v; - }); - }); -} \ No newline at end of file diff --git a/packages/widgets/lib/StatefulWidget/AnimatedPositioned/node1_base.dart b/packages/widgets/lib/StatefulWidget/AnimatedPositioned/node1_base.dart deleted file mode 100644 index 3e1f4343b..000000000 --- a/packages/widgets/lib/StatefulWidget/AnimatedPositioned/node1_base.dart +++ /dev/null @@ -1,87 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-23 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 121, -// "name": 'AnimatedPositioned基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】\n" -// "【duration】 : 动画时长 【Duration】\n" -// "【onEnd】 : 动画结束回调 【Function()】\n" -// "【curve】 : 动画曲线 【Duration】\n" -// "【top】 : 到父顶距离 【double】\n" -// "【right】 : 到父右距离 【double】\n" -// "【left】 : 到父左距离 【double】\n" -// "【bottom】 : 到父底距离 【double】", -// } -class CustomAnimatedPositioned extends StatefulWidget { - const CustomAnimatedPositioned({Key? key}) : super(key: key); - - @override - _CustomAnimatedPositionedState createState() => - _CustomAnimatedPositionedState(); -} - -class _CustomAnimatedPositionedState extends State { - final double startTop = 0.0; - final double endTop = 30.0; - - double _top = 0.0; - - @override - void initState() { - _top = startTop; - super.initState(); - } - - @override - Widget build(BuildContext context) { - return Column( - children: [ - _buildSwitch(), - Container( - color: Colors.grey.withAlpha(33), - width: 200, - height: 100, - child: Stack( - children: _buildChildren(), - ), - ), - ], - ); - } - - List _buildChildren() => [ - AnimatedPositioned( - duration: const Duration(seconds: 1), - top: _top, - left: _top * 4, - child: const Icon( - Icons.android, - color: Colors.green, - size: 50, - ), - ), - AnimatedPositioned( - duration: const Duration(seconds: 1), - top: 50 - _top, - left: 150 - _top * 4, - child: const Icon( - Icons.android, - color: Colors.red, - size: 50, - ), - ) - ]; - - Widget _buildSwitch() => Switch( - value: _top == endTop, - onChanged: (v) { - setState(() { - _top = v ? endTop : startTop; - }); - }); -} diff --git a/packages/widgets/lib/StatefulWidget/AnimatedPositionedDirectional/node1_base.dart b/packages/widgets/lib/StatefulWidget/AnimatedPositionedDirectional/node1_base.dart deleted file mode 100644 index 9b9f320b0..000000000 --- a/packages/widgets/lib/StatefulWidget/AnimatedPositionedDirectional/node1_base.dart +++ /dev/null @@ -1,88 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-23 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 122, -// "name": 'AnimatedPositionedDirectional基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】\n" -// "【duration】 : 动画时长 【Duration】\n" -// "【onEnd】 : 动画结束回调 【Function()】\n" -// "【curve】 : 动画曲线 【Duration】\n" -// "【top】 : 到父顶距离 【double】\n" -// "【end】 : 到父右距离 【double】\n" -// "【start】 : 到父左距离 【double】\n" -// "【bottom】 : 到父底距离 【double】", -// } -class CustomAnimatedPositionedDirectional extends StatefulWidget { - const CustomAnimatedPositionedDirectional({Key? key}) : super(key: key); - - @override - _CustomAnimatedPositionedDirectionalState createState() => - _CustomAnimatedPositionedDirectionalState(); -} - -class _CustomAnimatedPositionedDirectionalState - extends State { - final double startTop = 0.0; - final double endTop = 30.0; - - double _top = 0.0; - - @override - void initState() { - _top = startTop; - super.initState(); - } - - @override - Widget build(BuildContext context) { - return Column( - children: [ - _buildSwitch(), - Container( - color: Colors.grey.withAlpha(33), - width: 200, - height: 100, - child: Stack( - children: _buildChildren(), - ), - ), - ], - ); - } - - List _buildChildren() => [ - AnimatedPositionedDirectional( - duration: const Duration(seconds: 1), - top: _top, - start: _top * 4, - child: const Icon( - Icons.android, - color: Colors.green, - size: 50, - ), - ), - AnimatedPositionedDirectional( - duration: const Duration(seconds: 1), - top: 50 - _top, - start: 150 - _top * 4, - child: const Icon( - Icons.android, - color: Colors.red, - size: 50, - ), - ) - ]; - - Widget _buildSwitch() => Switch( - value: _top == endTop, - onChanged: (v) { - setState(() { - _top = v ? endTop : startTop; - }); - }); -} diff --git a/packages/widgets/lib/StatefulWidget/AnimatedSwitcher/node1_base.dart b/packages/widgets/lib/StatefulWidget/AnimatedSwitcher/node1_base.dart deleted file mode 100644 index 6e7eca933..000000000 --- a/packages/widgets/lib/StatefulWidget/AnimatedSwitcher/node1_base.dart +++ /dev/null @@ -1,89 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-23 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 116, -// "name": 'AnimatedSwitcher基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】\n" -// "【duration】 : 动画时长 【Duration】\n" -// "【switchOutCurve】 : 切出曲线 【Curves】\n" -// "【switchInCurve】 : 切入曲线 【Curves】\n" -// "【switchInCurve】 : 切入曲线 【Curves】\n" -// "【transitionBuilder】 : 动画构造器 【Widget Function(Widget, Animation)】", -// } - -class CustomAnimatedSwitcher extends StatefulWidget { - const CustomAnimatedSwitcher({Key? key}) : super(key: key); - - @override - _CustomAnimatedSwitcherState createState() => _CustomAnimatedSwitcherState(); -} - -class _CustomAnimatedSwitcherState extends State { - int _count = 0; - - @override - Widget build(BuildContext context) { - return Wrap( - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - _buildMinusBtn(), - SizedBox(width:80,child: _buildAnimatedSwitcher(context)), - _buildAddBtn() - ], - ); - } - - Widget _buildAnimatedSwitcher(BuildContext context) => - AnimatedSwitcher( - duration: const Duration(milliseconds: 400), - transitionBuilder: (Widget child, Animation animation) => - ScaleTransition( - child: RotationTransition(turns: animation, child: child), - scale: animation), - child: Text( - '$_count', - key: ValueKey(_count), - style: Theme.of(context).textTheme.headline3, - ), - ); - - Widget _buildMinusBtn() { - return MaterialButton( - padding: const EdgeInsets.all(0), - textColor: const Color(0xffFfffff), - elevation: 3, - color: Colors.red, - highlightColor: const Color(0xffF88B0A), - splashColor: Colors.red, - child: const Icon( - Icons.remove, - color: Colors.white, - ), - shape: const CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFDFDFDF)), - ), - onPressed: () => setState(() => _count -= 1)); - } - - Widget _buildAddBtn() => MaterialButton( - padding: const EdgeInsets.all(0), - textColor: const Color(0xffFfffff), - elevation: 3, - color: Colors.blue, - highlightColor: const Color(0xffF88B0A), - splashColor: Colors.red, - child: const Icon( - Icons.add, - color: Colors.white, - ), - shape: const CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFDFDFDF)), - ), - onPressed: () => setState(() => _count += 1)); -} diff --git a/packages/widgets/lib/StatefulWidget/AnimatedTheme/node1_base.dart b/packages/widgets/lib/StatefulWidget/AnimatedTheme/node1_base.dart deleted file mode 100644 index d6aee269a..000000000 --- a/packages/widgets/lib/StatefulWidget/AnimatedTheme/node1_base.dart +++ /dev/null @@ -1,101 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-23 -/// contact me by email 1981462002@qq.com -/// 说明: 224 主题变化时具有动画效果的组件,本质是Theme组件和动画结合的产物。可指定ThemeData、动画时长、曲线、结束回调等。相当于增强版的Theme组件。 -// { -// "widgetId": 224 , -// "name": 'AnimatedTheme基本使用', -// "priority": 1, -// "subtitle": "【data】 : 主题数据 【ThemeData】\n" -// "【duration】 : 动画时长 【Duration】\n" -// "【onEnd】 : 动画结束回调 【Function()】\n" -// "【curve】 : 动画曲线 【Duration】\n" -// "【child】 : 子组件 【Widget】", -// } -class AnimatedThemeDemo extends StatefulWidget { - const AnimatedThemeDemo({Key? key}) : super(key: key); - - @override - _AnimatedThemeDemoState createState() => _AnimatedThemeDemoState(); -} - -class _AnimatedThemeDemoState extends State { - ThemeData startThem = ThemeData( - primaryColor: Colors.blue, - textTheme: const TextTheme( - headline1: TextStyle( - color: Colors.white, - fontSize: 24, - fontWeight: FontWeight.bold, - ), - )); - - ThemeData endThem = ThemeData( - primaryColor: Colors.red, - textTheme: const TextTheme( - headline1: TextStyle( - color: Colors.black, - fontSize: 16, - fontWeight: FontWeight.normal, - ))); - - late ThemeData them; - - @override - void initState() { - super.initState(); - them = startThem; - } - - @override - Widget build(BuildContext context) { - return Column( - children: [ - _buildSwitch(), - AnimatedTheme( - data: them, - duration: const Duration(seconds: 2), - curve: Curves.fastOutSlowIn, - onEnd: () { - print('----onEnd---'); - }, - child: const ChildContent(), - ), - ], - ); - } - - Widget _buildSwitch() { - print(them == endThem); - return Switch( - value: them == endThem, - onChanged: (v) { - setState(() { - them = v ? endThem : startThem; - }); - }); - } -} - -class ChildContent extends StatelessWidget { - const ChildContent({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - width: 250, - height: 60, - alignment: Alignment.center, - decoration: BoxDecoration( - borderRadius: const BorderRadius.all(Radius.circular(5)), - color: Theme.of(context).primaryColor, - ), - padding: const EdgeInsets.all(10), - child: Text( - 'Flutter Unit', - style: Theme.of(context).textTheme.headline1, - ), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/AppBar/node1_base.dart b/packages/widgets/lib/StatefulWidget/AppBar/node1_base.dart deleted file mode 100644 index 35e450fe0..000000000 --- a/packages/widgets/lib/StatefulWidget/AppBar/node1_base.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../PopupMenuButton/node1_base.dart'; - -/// create by 张风捷特烈 on 2020-03-23 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 57, -// "name": 'AppBar基本使用', -// "priority": 1, -// "subtitle": -// "【leading】 : 左侧组件 【Widget】\n" -// "【title】 : 中间组件 【Widget】\n" -// "【actions】 : 右侧组件 【List】\n" -// "【elevation】 : 影深 【double】\n" -// "【shape】 : 形状 【ShapeBorder】\n" -// "【backgroundColor】 : 影深 【背景色】\n" -// "【centerTitle】 : 中间是否居中 【bool】", -// } - -class CustomAppBar extends StatelessWidget { - const CustomAppBar({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return AppBar( - title: const Text('风雅六社'), - leading: const BackButton(), - backgroundColor: Colors.amber[500], - elevation: 2, - centerTitle: true, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(20), - bottomRight: Radius.circular(20), - topRight: Radius.circular(5), - bottomLeft: Radius.circular(5), - )), - actions: [ - IconButton( - icon: const Icon(Icons.star), - tooltip: 'liked_widget_bloc', - onPressed: () { - // do nothing - }), - const CustomPopupMenuButton() - ], - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/AppBar/node2_tab.dart b/packages/widgets/lib/StatefulWidget/AppBar/node2_tab.dart deleted file mode 100644 index 29301e14a..000000000 --- a/packages/widgets/lib/StatefulWidget/AppBar/node2_tab.dart +++ /dev/null @@ -1,88 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../PopupMenuButton/node1_base.dart'; -/// create by 张风捷特烈 on 2020-03-23 -/// contact me by email 1981462002@qq.com -/// 详情: -// { -// "widgetId": 57, -// "name": 'AppBar与TabBar、TabBarView联用', -// "priority": 2, -// "subtitle": -// "【bottom】 : 底部组件 【PreferredSizeWidget】", -// } -class TabAppBar extends StatefulWidget { - const TabAppBar({Key? key}) : super(key: key); - - @override - _TabAppBarState createState() => _TabAppBarState(); -} - -class _TabAppBarState extends State - with SingleTickerProviderStateMixin { - final List tabs = ['风画庭', '雨韵舍', '雷鸣殿', '电疾堂', '霜寒阁', '雪月楼']; - late TabController _tabController; - - @override - void initState() { - super.initState(); - _tabController = TabController(vsync: this, length: tabs.length); - } - - @override - void dispose() { - _tabController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 220, - child: Scaffold( - appBar: _buildAppBar(), - body: _buildTableBarView(), - ), - ); - } - - PreferredSizeWidget _buildAppBar() => AppBar( - title: const Text('风雅六社'), - elevation: 1, - leading: const BackButton(), - centerTitle: true, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(20), - topRight: Radius.circular(20), - )), - actions: [ - IconButton( - icon: const Icon(Icons.star), - tooltip: 'liked_widget_bloc', - onPressed: () { - // do nothing - }), - const CustomPopupMenuButton() - ], - bottom: TabBar( - isScrollable: true, - controller: _tabController, - indicatorColor: Colors.orangeAccent, - tabs: tabs.map((e) => Tab(text: e)).toList(), - ), - ); - - Widget _buildTableBarView() => TabBarView( - controller: _tabController, - children: tabs - .map((e) => ColoredBox( - color: Colors.purple, - child:Center( - child: Text( - e, - style: const TextStyle(color: Colors.white, fontSize: 20), - ), - ))) - .toList()); -} diff --git a/packages/widgets/lib/StatefulWidget/AutomaticKeepAlive/node1_base.dart b/packages/widgets/lib/StatefulWidget/AutomaticKeepAlive/node1_base.dart deleted file mode 100644 index 2af44f572..000000000 --- a/packages/widgets/lib/StatefulWidget/AutomaticKeepAlive/node1_base.dart +++ /dev/null @@ -1,125 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 239 AutomaticKeepAlive 自动保活 在懒加载的列表中,允许子树请求保持状态,单独使用无效果,需要配合 KeepAliveNotification 使用。 -/// link 59,162,163,165,185,188 -/// -// { -// "widgetId": 239, -// "name": 'AutomaticKeepAlive 介绍', -// "priority": 1, -// "subtitle": -// "【child】 : 子组件 【Widget】\n" -// "在 ListView、SliverList、GridView、SliverGrid、PageView、TabBarView 等列表、切页组件源码中都有使用到 AutomaticKeepAlive 组件。在保活某个 State 时,可以使用 AutomaticKeepAliveClientMixin 进行操作,它是对 KeepAliveNotification 使用的一个简易封装。该示例展示出 ListView 条目的状态保活。", -// } - -class AutomaticKeepAliveDemo extends StatelessWidget { - AutomaticKeepAliveDemo({Key? key}) : super(key: key); - - final List data = [ - Colors.purple[50]!, - Colors.purple[100]!, - Colors.purple[200]!, - Colors.purple[300]!, - Colors.purple[400]!, - Colors.purple[500]!, - Colors.purple[600]!, - Colors.purple[700]!, - Colors.purple[800]!, - Colors.purple[900]!, - Colors.red[50]!, - Colors.red[100]!, - Colors.red[200]!, - Colors.red[300]!, - Colors.red[400]!, - Colors.red[500]!, - Colors.red[600]!, - Colors.red[700]!, - Colors.red[800]!, - Colors.red[900]!, - ]; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 300, - child: ListView.builder( - itemCount: data.length, - itemBuilder: (_, index) => ColorBox( - color: data[index], - index: index, - ), - ), - ); - } -} - -class ColorBox extends StatefulWidget { - final Color color; - final int index; - - const ColorBox({Key? key, required this.color, required this.index}) - : super(key: key); - - @override - _ColorBoxState createState() => _ColorBoxState(); -} - -class _ColorBoxState extends State with AutomaticKeepAliveClientMixin { - bool _checked = false; - - @override - void initState() { - super.initState(); - _checked = false; - print('-----_ColorBoxState#initState---${widget.index}-------'); - } - - @override - void dispose() { - print('-----_ColorBoxState#dispose---${widget.index}-------'); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - super.build(context); - return Container( - alignment: Alignment.center, - height: 50, - color: widget.color, - child: Row( - children: [ - const SizedBox( - width: 60, - ), - Checkbox( - value: _checked, - onChanged: (bool? v) { - setState(() { - _checked = v ?? false; - }); - }, - ), - Text( - "index ${widget.index}: ${colorString(widget.color)}", - style: const TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2, - ) - ]), - ), - ], - ), - ); - } - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; - - @override - bool get wantKeepAlive => true; -} diff --git a/packages/widgets/lib/StatefulWidget/BottomAppBar/node1_base.dart b/packages/widgets/lib/StatefulWidget/BottomAppBar/node1_base.dart deleted file mode 100644 index c65adf685..000000000 --- a/packages/widgets/lib/StatefulWidget/BottomAppBar/node1_base.dart +++ /dev/null @@ -1,118 +0,0 @@ -import 'package:flutter/material.dart'; - - -/// create by 张风捷特烈 on 2020-03-23 -/// contact me by email 1981462002@qq.com -/// 详情: -// { -// "widgetId": 61, -// "name": 'BottomAppBar基本用法', -// "priority": 1, -// "subtitle": "【elevation】 : 影深 【double】\n" -// "【shape】 : 形状 【NotchedShape】\n" -// "【notchMargin】 : 间隔距离 【double】\n" -// "【color】 : 颜色 【Color】\n" -// "【child】 : 孩子 【Widget】", -// } -class CustomBottomAppBar extends StatefulWidget { - const CustomBottomAppBar({Key? key}) : super(key: key); - - @override - _CustomBottomAppBarState createState() => _CustomBottomAppBarState(); -} - -class _CustomBottomAppBarState extends State { - int _position = 0; - FloatingActionButtonLocation _location = - FloatingActionButtonLocation.centerDocked; - final Map iconsMap = { - "图鉴": Icons.home, - "动态": Icons.toys, - "喜欢": Icons.favorite, - "手册": Icons.class_, - }; - Color activeColor = Colors.blue.withAlpha(240); - - @override - Widget build(BuildContext context) { - return SizedBox( - width: MediaQuery.of(context).size.width, - height: 180, - child: Scaffold( - backgroundColor: Colors.purple.withAlpha(22), - floatingActionButton: FloatingActionButton( - onPressed: () => Navigator.of(context).pushNamed('AboutMePage'), - child: const Icon(Icons.add), - ), - bottomNavigationBar: _buildBottomAppBar(), - floatingActionButtonLocation: _location, - body: _buildContent(), - ), - ); - } - - Widget _buildBottomAppBar() { - return BottomAppBar( - elevation: 1, - shape: const CircularNotchedRectangle(), - notchMargin: 5, - color: Colors.red, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: info.asMap().keys.map((i) => _buildChild(i)).toList() - ..insertAll(isCenter ? 2 : 4, [const SizedBox(width: 30)])), - ); - } - - Container _buildContent() { - return Container( - alignment: Alignment.center, - child: Wrap( - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Text( - '当前页索引:$_position', - style: const TextStyle(color: Colors.blue, fontSize: 18), - ), - Switch( - value: isCenter, - onChanged: (v) { - setState(() { - _location = v - ? FloatingActionButtonLocation.centerDocked - : FloatingActionButtonLocation.endDocked; - }); - }), - ], - ), - ); - } - - List get info => iconsMap.keys.toList(); - - bool get isCenter => _location == FloatingActionButtonLocation.centerDocked; - - Widget _buildChild(int i) { - bool active = i == _position; - return Padding( - padding: const EdgeInsets.all(8.0), - child: GestureDetector( - onTap: () => setState(() => _position = i), - child: Wrap( - direction: Axis.vertical, - alignment: WrapAlignment.center, - children: [ - Icon( - iconsMap[info[i]], - color: active ? activeColor : Colors.white, - size: 30, - ), - Text(info[i], - style: TextStyle( - color: active ? activeColor : Colors.white, fontSize: 14)), - ], - ), - ), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/BottomNavigationBar/node1_base.dart b/packages/widgets/lib/StatefulWidget/BottomNavigationBar/node1_base.dart deleted file mode 100644 index 4d706211c..000000000 --- a/packages/widgets/lib/StatefulWidget/BottomNavigationBar/node1_base.dart +++ /dev/null @@ -1,99 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-03-23 -/// contact me by email 1981462002@qq.com -/// 详情: -// { -// "widgetId": 60, -// "name": 'BottomNavigationBar基本使用', -// "priority": 1, -// "subtitle": -// "【currentIndex】 : 当前索引 【int】\n" -// "【elevation】 : 影深 【double】\n" -// "【type】 : 类型*2 【BottomNavigationBarType】\n" -// "【fixedColor】 : type为fix的颜色 【Color】\n" -// "【backgroundColor】 : 背景色 【Color】\n" -// "【iconSize】 : 图标大小 【double】\n" -// "【selectedLabelStyle】 : 选中文字样式 【TextStyle】\n" -// "【unselectedLabelStyle】 : 未选中文字样式 【TextStyle】\n" -// "【showUnselectedLabels】 : 显示未选中标签 【bool】\n" -// "【showSelectedLabels】 : 显示选中标签 【bool】\n" -// "【items】 : 条目 【List】\n" -// "【onTap】 : 点击事件 【Function(int)】", -// } -class CustomBottomNavigationBar extends StatefulWidget { - const CustomBottomNavigationBar({Key? key}) : super(key: key); - - @override - _CustomBottomNavigationBarState createState() => - _CustomBottomNavigationBarState(); -} - -class _CustomBottomNavigationBarState extends State { - int _position = 0; - BottomNavigationBarType _type = BottomNavigationBarType.shifting; - final Map iconsMap = { //底栏图标 - "图鉴": Icons.home, "动态": Icons.toys, - "喜欢": Icons.favorite, "手册": Icons.class_, - "我的": Icons.account_circle, - }; - final List _colors = [ - Colors.red, - Colors.yellow, - Colors.blue, - Colors.green, - Colors.purple, - ]; - - @override - Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - _buildOp(), - _buildBottomNavigationBar(), - ], - ); - } - - bool get isShifting => _type == BottomNavigationBarType.shifting; - - BottomNavigationBar _buildBottomNavigationBar() { - return BottomNavigationBar( - onTap: (position) => setState(() => _position = position), - currentIndex: _position, - elevation: 1, - type: _type, - fixedColor: isShifting ? Colors.white : _colors[_position], - backgroundColor: Colors.white, - iconSize: 25, - selectedLabelStyle: const TextStyle(fontWeight: FontWeight.bold), - showUnselectedLabels: false, - showSelectedLabels: true, - items: iconsMap.keys - .map((key) => BottomNavigationBarItem( - label:key, - icon: Icon(iconsMap[key]), - backgroundColor: _colors[_position])) - .toList(), - ); - } - - Widget _buildOp() { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Text( - _type.toString(), - style: const TextStyle(fontWeight: FontWeight.bold, color: Colors.blue), - ), - Switch( - value: _type == BottomNavigationBarType.shifting, - onChanged: (b) { - setState(() => _type = b - ? BottomNavigationBarType.shifting - : BottomNavigationBarType.fixed); - }), - ], - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/BottomNavigationBar/node2_page.dart b/packages/widgets/lib/StatefulWidget/BottomNavigationBar/node2_page.dart deleted file mode 100644 index c1b4e46c0..000000000 --- a/packages/widgets/lib/StatefulWidget/BottomNavigationBar/node2_page.dart +++ /dev/null @@ -1,102 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-03-23 -/// contact me by email 1981462002@qq.com -/// 详情: -// { -// "widgetId": 60, -// "name": '可结合PageView进行切页', -// "priority": 2, -// "subtitle": -// "在onTap时进行使用控制器进行切页", -// } -class BottomNavigationBarWithPageView extends StatefulWidget { - const BottomNavigationBarWithPageView({Key? key}) : super(key: key); - - @override - _BottomNavigationBarWithPageViewState createState() => - _BottomNavigationBarWithPageViewState(); -} - -class _BottomNavigationBarWithPageViewState - extends State { - int _position = 0; - final Map iconsMap = { - //底栏图标 - "图鉴": Icons.home, "动态": Icons.toys, - "喜欢": Icons.favorite, "手册": Icons.class_, - "我的": Icons.account_circle, - }; - final List _colors = [ - Colors.red, - Colors.yellow, - Colors.blue, - Colors.green, - Colors.purple, - ]; - late PageController _controller; //页面控制器,初始0 - - @override - void initState() { - _controller = PageController( - initialPage: _position, - ); - super.initState(); - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Container( - color: Colors.orange.withAlpha(88), - width: MediaQuery.of(context).size.width, - height: 150, - child: PageView( - controller: _controller, - children: iconsMap.keys - .map((e) => Center( - child: Text( - e, - style: const TextStyle( - color: Colors.white, - fontSize: 20, - ), - ), - )) - .toList(), - ), - ), - _buildBottomNavigationBar() - ], - ); - } - - BottomNavigationBar _buildBottomNavigationBar() { - return BottomNavigationBar( - onTap: (position) { - _controller.jumpToPage(position); - setState(() => _position = position); - }, - currentIndex: _position, - elevation: 1, - type: BottomNavigationBarType.shifting, - fixedColor: Colors.white, - iconSize: 25, - selectedLabelStyle: const TextStyle(fontWeight: FontWeight.bold), - showUnselectedLabels: false, - showSelectedLabels: true, - items: iconsMap.keys - .map((key) => BottomNavigationBarItem( - label: key, - icon: Icon(iconsMap[key]), - backgroundColor: _colors[_position])) - .toList(), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/Checkbox/node1_base.dart b/packages/widgets/lib/StatefulWidget/Checkbox/node1_base.dart deleted file mode 100644 index 9ef6fab75..000000000 --- a/packages/widgets/lib/StatefulWidget/Checkbox/node1_base.dart +++ /dev/null @@ -1,49 +0,0 @@ - -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 39, -// "name": 'Checkbox基础用法', -// "priority": 1, -// "subtitle": -// "【value】 : 是否选中 【double】\n" -// "【checkColor】: 选中时✔️gou颜色 【Color】\n" -// "【activeColor】: 选中时框内颜色 【Color】\n" -// "【onChanged】: 状态改变事件 【Function(bool)】\n", -// } - -class CustomCheckbox extends StatefulWidget { - const CustomCheckbox({Key? key}) : super(key: key); - - @override - _CustomCheckboxState createState() => _CustomCheckboxState(); -} - -class _CustomCheckboxState extends State { - bool _checked = false; - final List colors = [ - Colors.red, - Colors.yellow, - Colors.blue, - Colors.green - ]; - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 10, - children: colors - .map((e) => Checkbox( - value: _checked, - checkColor: Colors.white, - activeColor: e, - onChanged: (bool? value) => - setState(() => _checked = value??false))) - .toList(), - ); - } -} - - diff --git a/packages/widgets/lib/StatefulWidget/Checkbox/node2_tristate.dart b/packages/widgets/lib/StatefulWidget/Checkbox/node2_tristate.dart deleted file mode 100644 index 5ed154722..000000000 --- a/packages/widgets/lib/StatefulWidget/Checkbox/node2_tristate.dart +++ /dev/null @@ -1,46 +0,0 @@ - -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: -/// { -// "widgetId": 39, -// "name": 'Checkbox的三态', -// "priority": 2, -// "subtitle": -// "【tristate】 : 是否是三态 【double】\n" -// " onChanged时,回调true、null、false三种状态" -// } -class TristateCheckBok extends StatefulWidget { - const TristateCheckBok({Key? key}) : super(key: key); - - @override - _TristateCheckBokState createState() => _TristateCheckBokState(); -} - -class _TristateCheckBokState extends State { - bool _checked = false; - final colors = [Colors.red, Colors.yellow, Colors.blue, Colors.green]; - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 10, - children: colors - .map((e) => - Checkbox( - value: _checked, - tristate: true, - checkColor: Colors.white, - activeColor: e, - onChanged: (bool? value) { - print(value); - setState(() => _checked = value??false); - })) - .toList(), - ); - } -} - diff --git a/packages/widgets/lib/StatefulWidget/CircularProgressIndicator/node1_base.dart b/packages/widgets/lib/StatefulWidget/CircularProgressIndicator/node1_base.dart deleted file mode 100644 index 302ffe7c0..000000000 --- a/packages/widgets/lib/StatefulWidget/CircularProgressIndicator/node1_base.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 46, -// "name": 'CircularProgressIndicator基本使用', -// "priority": 1, -// "subtitle": -// "【value】 : 进度 【double】\n" -// "【backgroundColor】 : 背景色 【Color】\n" -// "【valueColor】 : 进度颜色 【Animation】\n" -// "【strokeWidth】 : 线宽 【double】", -// } -class CustomCircularProgressIndicator extends StatefulWidget { - const CustomCircularProgressIndicator({Key? key}) : super(key: key); - - @override - _CustomCircularProgressIndicatorState createState() => - _CustomCircularProgressIndicatorState(); -} - -class _CustomCircularProgressIndicatorState - extends State { - - List data = [0.2,0.4,0.6,0.8,null]; - - @override - Widget build(BuildContext context) { - - return Wrap( - spacing: 10, - children:data.map((e)=>SizedBox( - width: 50, - height: 50, - child: CircularProgressIndicator( - value: e, - backgroundColor: Colors.grey.withAlpha(33), - valueColor: const AlwaysStoppedAnimation(Colors.orange), - strokeWidth: 5, - ), - )).toList(), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/CupertinoActivityIndicator/node1_base.dart b/packages/widgets/lib/StatefulWidget/CupertinoActivityIndicator/node1_base.dart deleted file mode 100644 index a3061e0e9..000000000 --- a/packages/widgets/lib/StatefulWidget/CupertinoActivityIndicator/node1_base.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:flutter/cupertino.dart'; -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 48, -// "name": 'CupertinoActivityIndicator基本使用', -// "priority": 1, -// "subtitle": -// "【animating】 : 是否loading动画 【bool】\n" -// "【radius】 : 半径 【double】", -// } -class CustomCupertinoActivityIndicator extends StatelessWidget { - const CustomCupertinoActivityIndicator({Key? key}) : super(key: key); - - - @override - Widget build(BuildContext context) { - - return Wrap( - spacing: 20, - children: const [ - CupertinoActivityIndicator( - animating: true, - radius: 25, - ), - CupertinoActivityIndicator( - animating: false, - radius: 25, - ) - ], - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/CupertinoApp/node1_base.dart b/packages/widgets/lib/StatefulWidget/CupertinoApp/node1_base.dart deleted file mode 100644 index eb710ee76..000000000 --- a/packages/widgets/lib/StatefulWidget/CupertinoApp/node1_base.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'package:flutter/cupertino.dart'; - -/// create by 张风捷特烈 on 2020-03-17 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 156, -// "name": 'CupertinoApp基本用法', -// "priority": 1, -// "subtitle": "【theme】 : 主题 【ThemeData】\n" -// "【title】 : 任务栏标题 【String】\n" -// "【onGenerateRoute】 : 路由生成器 【RouteFactory】\n" -// "【home】 : 主页 【Widget】", -// } -class CustomCupertinoApp extends StatelessWidget { - const CustomCupertinoApp({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return SizedBox( - width: MediaQuery.of(context).size.width, - height: MediaQuery.of(context).size.height - 200, - child: const CupertinoApp( - title: 'Flutter Demo', - theme: CupertinoThemeData( - primaryColor: CupertinoColors.white, - ), - home: CupertinoPageScaffold( - navigationBar: CupertinoNavigationBar( - leading: Icon( - CupertinoIcons.reply, - color: CupertinoColors.black, - ), - trailing: Icon( - CupertinoIcons.share, - color: CupertinoColors.black, - ), - middle: Text('Flutter Unit'), - ), - backgroundColor: CupertinoColors.systemBackground, - child: Center( - child: Text('Hello, World!'), - ), - ), - ), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/CupertinoButton/node1_base.dart b/packages/widgets/lib/StatefulWidget/CupertinoButton/node1_base.dart deleted file mode 100644 index 0958538a8..000000000 --- a/packages/widgets/lib/StatefulWidget/CupertinoButton/node1_base.dart +++ /dev/null @@ -1,44 +0,0 @@ -/// create by 张风捷特烈 on 2020-03-24 -/// contact me by email 1981462002@qq.com -/// 说明: -/// -// { -// "widgetId": 24, -// "priority": 1, -// "name": "CupertinoButton点击事件", -// "subtitle": "【color】: 颜色 【Color】\n" -// "【pressedOpacity】: 按下时透明度 【double】\n" -// "【child】: 子组件 【Widget】\n" -// "【padding】: 内边距 【EdgeInsetsGeometry】\n" -// "【borderRadius】: 圆角半径 【BorderRadius】\n" -// "【onPressed】: 点击事件 【Function】", -// } -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -class CustomCupertinoButton extends StatelessWidget { - CustomCupertinoButton({Key? key}) : super(key: key); - - final Map data = { - CupertinoColors.activeBlue:4.0, - Colors.blue:6.0, - CupertinoColors.activeOrange:8.0, - }; - - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 20, - children:data.keys.map((e)=> CupertinoButton( - padding: EdgeInsets.zero, - onPressed: () => Navigator.of(context).pushNamed('AboutMePage'), - color: e, - pressedOpacity: 0.4, - borderRadius: BorderRadius.all(Radius.circular(data[e]!)), - child: const Text("iOS"), - )).toList() - ); - } -} - diff --git a/packages/widgets/lib/StatefulWidget/CupertinoContextMenu/node1_base.dart b/packages/widgets/lib/StatefulWidget/CupertinoContextMenu/node1_base.dart deleted file mode 100644 index c4c998098..000000000 --- a/packages/widgets/lib/StatefulWidget/CupertinoContextMenu/node1_base.dart +++ /dev/null @@ -1,48 +0,0 @@ -import 'package:flutter/cupertino.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 143, -// "name": 'CupertinoContextMenu基本使用', -// "priority": 1, -// "subtitle": "【child】 : 子组件 【Widget】\n" -// "【actions】 : 行为组件集 【List】\n" -// "【previewBuilder】 : 动画构造器 【ContextMenuPreviewBuilder】", -// } -class CustomCupertinoContextMenu extends StatelessWidget { - const CustomCupertinoContextMenu({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return SizedBox( - width: 100, - height: 100, - child: DecoratedBox( - decoration: const BoxDecoration( - image: DecorationImage( - image: AssetImage('assets/images/sabar_bar.webp'), - fit: BoxFit.cover), - borderRadius: BorderRadius.all(Radius.circular(50))), - child: _buildCupertinoContextMenu(context)), - ); - } - - final List info = const ['保存图片', '立刻呼叫', '添加到收藏夹']; - - Widget _buildCupertinoContextMenu(context) => CupertinoContextMenu( - child: Container( - decoration: const BoxDecoration( - image: DecorationImage( - image: AssetImage('assets/images/sabar_bar.webp'), - fit: BoxFit.cover), - borderRadius: BorderRadius.all(Radius.circular(50))), - ), - actions: info - .map((e) => CupertinoContextMenuAction( - child: Center(child: Text(e)), - onPressed: () => Navigator.pop(context), - )) - .toList()); -} diff --git a/packages/widgets/lib/StatefulWidget/CupertinoContextMenuAction/node1_base.dart b/packages/widgets/lib/StatefulWidget/CupertinoContextMenuAction/node1_base.dart deleted file mode 100644 index f8cdf6ebc..000000000 --- a/packages/widgets/lib/StatefulWidget/CupertinoContextMenuAction/node1_base.dart +++ /dev/null @@ -1,44 +0,0 @@ -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 144, -// "name": 'CupertinoContextMenuAction基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 子组件 【Widget】\n" -// "【isDefaultAction】 : 是否默认选中 【bool】\n" -// "【trailingIcon】 : 尾部 【bool】\n" -// "【onPressed】 : 点击事件 【Function()】", -// } -import 'package:flutter/cupertino.dart'; - -class CustomCupertinoContextMenuAction extends StatelessWidget { - const CustomCupertinoContextMenuAction({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Container( - width: 200, - margin: const EdgeInsets.all(5), - child: CupertinoContextMenuAction( - trailingIcon: CupertinoIcons.settings, - isDefaultAction: true, - onPressed: () => Navigator.of(context).pushNamed('AboutMePage'), - child: const Text('张风捷特烈')), - ), - Container( - width: 200, - margin: const EdgeInsets.all(5), - child: CupertinoContextMenuAction( - trailingIcon: CupertinoIcons.home, - isDefaultAction: false, - onPressed: () => Navigator.of(context).pushNamed('AboutMePage'), - child: const Text('百里·巫缨')), - ), - ], - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/CupertinoDatePicker/node1_base.dart b/packages/widgets/lib/StatefulWidget/CupertinoDatePicker/node1_base.dart deleted file mode 100644 index bcad7d5e4..000000000 --- a/packages/widgets/lib/StatefulWidget/CupertinoDatePicker/node1_base.dart +++ /dev/null @@ -1,81 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 137, -// "name": 'CupertinoDatePicker基本使用', -// "priority": 1, -// "subtitle": -// "【initialDateTime】 : 初始日期 【DateTime】\n" -// "【minimumYear】 : 最小年份 【int】\n" -// "【maximumYear】 : 最大年份 【int】\n" -// "【onDateTimeChanged】 : 点击回调 【Function(DateTime)】\n" -// "【minuteInterval】 : 分钟间隔 【int】\n" -// "【use24hFormat】 : 是否是24小时制 【bool】\n" -// "【backgroundColor】 : 背景色 【Color】\n" -// "【mode】 : 模式*3 【CupertinoDatePickerMode】", -// } -class CustomCupertinoDatePicker extends StatefulWidget { - const CustomCupertinoDatePicker({Key? key}) : super(key: key); - - @override - _CustomCupertinoDatePickerState createState() => - _CustomCupertinoDatePickerState(); -} - -class _CustomCupertinoDatePickerState extends State { - DateTime _date = DateTime.now(); - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Text( - '当前日期:${_date.toIso8601String()}', - style: const TextStyle(color: Colors.grey, fontSize: 16), - ), - _buildInfoTitle('CupertinoDatePickerMode.dateAndTime'), - buildPicker(CupertinoDatePickerMode.dateAndTime), - _buildInfoTitle('CupertinoDatePickerMode.date'), - buildPicker(CupertinoDatePickerMode.date), - _buildInfoTitle('CupertinoDatePickerMode.time'), - buildPicker(CupertinoDatePickerMode.time), - ], - ); - } - - Container buildPicker(CupertinoDatePickerMode mode) { - return Container( - margin: const EdgeInsets.all(10), - height: 150, - child: CupertinoDatePicker( - mode: mode, - initialDateTime: DateTime.now(), -// maximumDate: DateTime(2018,8,8), -// minimumDate: DateTime(2030,8,8), - minimumYear: 2018, - maximumYear: 2030, - use24hFormat: false, - minuteInterval: 1, - backgroundColor: CupertinoColors.white, - onDateTimeChanged: (date) { - print(date); - setState(() => _date = date); - }, - ), - ); - } - - Widget _buildInfoTitle(info){ - return Padding( - padding: const EdgeInsets.only(left: 20,top: 20,bottom: 5), - child: Text( - info, - style: const TextStyle(color: Colors.blue, fontSize: 16,fontWeight: FontWeight.bold), - ), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/CupertinoNavigationBar/node1_base.dart b/packages/widgets/lib/StatefulWidget/CupertinoNavigationBar/node1_base.dart deleted file mode 100644 index ee39ef3e2..000000000 --- a/packages/widgets/lib/StatefulWidget/CupertinoNavigationBar/node1_base.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-03-26 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 62, -// "name": 'CupertinoNavigationBar基本用法', -// "priority": 1, -// "subtitle": -// "【leading】 : 左侧组件 【Widget】\n" -// "【middle】 : 中间组件 【Widget】\n" -// "【trailing】 : 尾部组件 【Widget】\n" -// "【backgroundColor】 : 背景色 【Color】\n" -// "【padding】 : 内边距 【EdgeInsetsDirectional】\n" -// "【border】 : 边线 【Border】", -// } -class CustomCupertinoNavigationBar extends StatelessWidget { - const CustomCupertinoNavigationBar({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return CupertinoNavigationBar( - leading: const Icon( - CupertinoIcons.back, - size: 25, - color: Colors.blue, - ), - middle: const Text("风雪雅舍"), - trailing: Image.asset( - "assets/images/icon_head.webp", - width: 25.0, - height: 25.0, - ), - backgroundColor: const Color(0xfff1f1f1), - padding: const EdgeInsetsDirectional.only(start: 10,end: 20), - border: Border.all(color: Colors.transparent), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/CupertinoPageScaffold/node1_base.dart b/packages/widgets/lib/StatefulWidget/CupertinoPageScaffold/node1_base.dart deleted file mode 100644 index 54ad2ec1f..000000000 --- a/packages/widgets/lib/StatefulWidget/CupertinoPageScaffold/node1_base.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:flutter/cupertino.dart'; - -/// create by 张风捷特烈 on 2020-03-26 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 157, -// "name": 'CupertinoPageScaffold基本用法', -// "priority": 1, -// "subtitle": -// "【child】 : 内容 【Widget】\n" -// "【backgroundColor】 : 背景色 【Color】\n" -// "【navigationBar】 : 头部 【ObstructingPreferredSizeWidget】", -// } -class CustomCupertinoPageScaffold extends StatelessWidget { - const CustomCupertinoPageScaffold({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return SizedBox( - width: MediaQuery.of(context).size.width, - height: MediaQuery.of(context).size.height - 300, - child: const CupertinoPageScaffold( - navigationBar: CupertinoNavigationBar( - leading: Icon(CupertinoIcons.reply), - trailing: Icon(CupertinoIcons.share), - middle: Text('Flutter Unit'), - ), - backgroundColor: CupertinoColors.systemBackground, - child: Center( - child: Text('Hello, World!'), - ), - ), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatefulWidget/CupertinoPicker/node1_base.dart b/packages/widgets/lib/StatefulWidget/CupertinoPicker/node1_base.dart deleted file mode 100644 index 20bcb53e7..000000000 --- a/packages/widgets/lib/StatefulWidget/CupertinoPicker/node1_base.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'package:flutter/cupertino.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 139, -// "name": 'CupertinoPicker基本使用', -// "priority": 1, -// "subtitle": -// "【children】 : 子组件列表 【List】\n" -// "【offAxisFraction】 : 轴偏移率 【double】\n" -// "【squeeze】 : 挤压率 【double】\n" -// "【diameterRatio】 : 高与圆柱直径比率 【double】\n" -// "【itemExtent】 : 间距 【double】\n" -// "【backgroundColor】 : 背景色 【Color】\n" -// "【onSelectedItemChanged】 : 选中事件 【Function(int)】", -// } -class CustomCupertinoPicker extends StatelessWidget { - const CustomCupertinoPicker({Key? key}) : super(key: key); - - final List names = const[ - 'Java', - 'Kotlin', - 'Dart', - 'Swift', - 'C++', - 'Python', - "JavaScript", - "PHP", - "Go", - "Object-c" - ]; - - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 150, - child: CupertinoPicker( - backgroundColor: CupertinoColors.systemGrey.withAlpha(33), - diameterRatio: 1, - offAxisFraction: 0.4, - squeeze: 1.5, - itemExtent: 40, - onSelectedItemChanged: (position) { - print('当前条目 ${names[position]}'); - }, - children: names.map((e) => Center(child: Text(e))).toList()), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/CupertinoScrollbar/node1_base.dart b/packages/widgets/lib/StatefulWidget/CupertinoScrollbar/node1_base.dart deleted file mode 100644 index e5a332839..000000000 --- a/packages/widgets/lib/StatefulWidget/CupertinoScrollbar/node1_base.dart +++ /dev/null @@ -1,65 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-31 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 195, -// "name": 'CupertinoScrollbar基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 子组件 【Widget】\n" -// "【controller】 : 控制器 【ScrollController】", -// } -class CustomCupertinoScrollbar extends StatelessWidget { - CustomCupertinoScrollbar({Key? key}) : super(key: key); - - final List data = [ - Colors.purple[50]!, - Colors.purple[100]!, - Colors.purple[200]!, - Colors.purple[300]!, - Colors.purple[400]!, - Colors.purple[500]!, - Colors.purple[600]!, - Colors.purple[700]!, - Colors.purple[800]!, - Colors.purple[900]!, - ]; - - TextStyle get textStyle => const TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2, - ) - ]); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 200, - child: CupertinoScrollbar( - child: ListView( - padding: const EdgeInsets.symmetric(horizontal: 5), - children: data - .map((color) => Container( - alignment: Alignment.center, - width: 100, - height: 50, - color: color, - child: Text( - colorString(color), - style: textStyle, - ), - )) - .toList(), - ), - ), - ); - } - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} \ No newline at end of file diff --git a/packages/widgets/lib/StatefulWidget/CupertinoSegmentedControl/node1_base.dart b/packages/widgets/lib/StatefulWidget/CupertinoSegmentedControl/node1_base.dart deleted file mode 100644 index e2cd73a65..000000000 --- a/packages/widgets/lib/StatefulWidget/CupertinoSegmentedControl/node1_base.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:flutter/cupertino.dart'; - -/// create by 张风捷特烈 on 2020/6/30 -/// contact me by email 1981462002@qq.com -/// 说明: -/// -// { -// "widgetId": 262, -// "name": 'iOS页签基本使用', -// "priority": 1, -// "subtitle": -// "【children】 : 组件Map 【Map】\n" -// "【onValueChanged】 : 值改变回调 【ValueChanged】\n" -// "【groupValue】 : 选中值 【T】\n" -// "【padding】 : 内边距 【EdgeInsetsGeometry】", -// } -class CupertinoSegmentedControlDemo extends StatefulWidget { - const CupertinoSegmentedControlDemo({Key? key}) : super(key: key); - - @override - _CupertinoSegmentedControlDemoState createState() => - _CupertinoSegmentedControlDemoState(); -} - -class _CupertinoSegmentedControlDemoState - extends State { - int _value = 1; - - @override - Widget build(BuildContext context) { - return CupertinoSegmentedControl( - groupValue: _value, - onValueChanged: _onValueChanged, - padding: const EdgeInsets.only(top: 20), - children: const { - 1: Padding( - padding: EdgeInsets.only(left: 20, right: 20), - child: Text("混沌战士"), - ), - 2: Text("青眼白龙"), - 3: Text("黑魔术士"), - }, - ); - } - - void _onValueChanged(int value) { - setState(() { - _value=value; - }); - } -} diff --git a/packages/widgets/lib/StatefulWidget/CupertinoSegmentedControl/node2_color.dart b/packages/widgets/lib/StatefulWidget/CupertinoSegmentedControl/node2_color.dart deleted file mode 100644 index db59addf8..000000000 --- a/packages/widgets/lib/StatefulWidget/CupertinoSegmentedControl/node2_color.dart +++ /dev/null @@ -1,56 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/6/30 -/// contact me by email 1981462002@qq.com -/// 说明: -/// -// { -// "widgetId": 262, -// "name": 'CupertinoSegmentedControl的颜色', -// "priority": 2, -// "subtitle": -// "【unselectedColor】 : 未选中色 【Color】\n" -// "【selectedColor】 : 选中色 【Color】\n" -// "【pressedColor】 : 按下色 【Color】\n" -// "【borderColor】 : 边线色 【Color】", -// } -class CupertinoSegmentedControlColor extends StatefulWidget { - const CupertinoSegmentedControlColor({Key? key}) : super(key: key); - - @override - _CupertinoSegmentedControlColorState createState() => - _CupertinoSegmentedControlColorState(); -} - -class _CupertinoSegmentedControlColorState - extends State { - int _value = 1; - - @override - Widget build(BuildContext context) { - return CupertinoSegmentedControl( - unselectedColor: Colors.yellow, - selectedColor: Colors.green, - pressedColor: Colors.blue, - borderColor: Colors.red, - groupValue: _value, - onValueChanged: _onValueChanged, - padding: const EdgeInsets.only(top: 20), - children: const { - 1: Padding( - padding: EdgeInsets.only(left: 20, right: 20), - child: Text("混沌战士"), - ), - 2: Text("青眼白龙"), - 3: Text("黑魔术士"), - }, - ); - } - - void _onValueChanged(int value) { - setState(() { - _value=value; - }); - } -} diff --git a/packages/widgets/lib/StatefulWidget/CupertinoSlider/node1_base.dart b/packages/widgets/lib/StatefulWidget/CupertinoSlider/node1_base.dart deleted file mode 100644 index fc08892c9..000000000 --- a/packages/widgets/lib/StatefulWidget/CupertinoSlider/node1_base.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-03-31 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 43, -// "name": 'CupertinoSlider基本使用', -// "priority": 1, -// "subtitle": -// "【value】 : 数值 【double】\n" -// "【min】 : 最小值 【double】\n" -// "【max】 : 最大值 【double】\n" -// "【activeColor】 : 激活颜色 【Color】\n" -// "【thumbColor】 : 圆形颜色 【Color】\n" -// "【divisions】 : 分段数 【int】\n" -// "【onChangeStart】 : 开始滑动回调 【Function(double)】\n" -// "【onChangeEnd】 : 滑动结束回调 【Function(double)】\n" -// "【onChanged】 : 改变时回调 【Function(double)】", -// } -class CustomCupertinoSlider extends StatefulWidget { - const CustomCupertinoSlider({Key? key}) : super(key: key); - - @override - _CustomCupertinoSliderState createState() => _CustomCupertinoSliderState(); -} - -class _CustomCupertinoSliderState extends State { - double _value = 0.0; - - @override - Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text('当前值:${_value.toStringAsFixed(1)}'), - CupertinoSlider( - value: _value, - divisions: 180, - min: 0.0, - max: 360.0, - activeColor: Colors.green, - thumbColor: Colors.white, - onChangeStart: (value) => print('开始滑动:$value'), - onChangeEnd: (value) => print('滑动结束:$value'), - onChanged: (value) { - setState(() { - _value = value; - }); - }), - ], - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/CupertinoSlidingSegmentedControl/node1_base.dart b/packages/widgets/lib/StatefulWidget/CupertinoSlidingSegmentedControl/node1_base.dart deleted file mode 100644 index ca98d299a..000000000 --- a/packages/widgets/lib/StatefulWidget/CupertinoSlidingSegmentedControl/node1_base.dart +++ /dev/null @@ -1,56 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/7/22 -/// contact me by email 1981462002@qq.com -/// 说明: 256 CupertinoSlidingSegmentedControl iOS滑动页签 iOS风格的滑动页签,支持点击、滑动切换。可指定页签颜色、背景色、边距等属性。 -// { -// "widgetId": 256, -// "name": 'iOS滑动页签基本使用', -// "priority": 1, -// "subtitle": -// "【children】 : 组件Map 【Map】\n" -// "【onValueChanged】 : 值改变回调 【ValueChanged】\n" -// "【groupValue】 : 选中值 【T】\n" -// "【thumbColor】 : 选中色 【Color】\n" -// "【backgroundColor】 : 背景色 【Color】\n" -// "【padding】 : 内边距 【EdgeInsetsGeometry】", -// } -class CupertinoSlidingSegmentedControlDemo extends StatefulWidget { - const CupertinoSlidingSegmentedControlDemo({Key? key}) : super(key: key); - - @override - _CupertinoSlidingSegmentedControlDemoState createState() => - _CupertinoSlidingSegmentedControlDemoState(); -} - -class _CupertinoSlidingSegmentedControlDemoState - extends State { - int _value = 1; - - @override - Widget build(BuildContext context) { - return CupertinoSlidingSegmentedControl( - groupValue: _value, - onValueChanged: _onValueChanged, - thumbColor: Colors.amberAccent, - backgroundColor: Colors.green.withAlpha(99), - padding: const EdgeInsets.all(5), - children: const { - 1: Padding( - padding: EdgeInsets.only(left: 20, right: 20), - child: Text("混沌战士"), - ), - 2: Text("青眼白龙"), - 3: Text("黑魔导"), - }, - ); - } - - void _onValueChanged(int? value) { - if(value==null) return; - setState(() { - _value=value; - }); - } -} diff --git a/packages/widgets/lib/StatefulWidget/CupertinoSwitch/node1_base.dart b/packages/widgets/lib/StatefulWidget/CupertinoSwitch/node1_base.dart deleted file mode 100644 index 2a72e9c44..000000000 --- a/packages/widgets/lib/StatefulWidget/CupertinoSwitch/node1_base.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-03-31 -/// contact me by email 1981462002@qq.com -/// 说明: -/// { -// "widgetId": 41, -// "name": 'CupertinoSwitch基本使用', -// "priority": 1, -// "subtitle": -// "【value】 : 是否选中 【double】\n" -// "【activeColor】 : 激活态颜色 【Color】\n" -// "【onChanged】 : 切换回调 【Function(double)】", -// } -class CustomCupertinoSwitch extends StatefulWidget { - const CustomCupertinoSwitch({Key? key}) : super(key: key); - - @override - _CustomCupertinoSwitchState createState() => _CustomCupertinoSwitchState(); -} - -class _CustomCupertinoSwitchState extends State { - final List colors = [ - Colors.red, - Colors.yellow, - Colors.blue, - Colors.green - ]; - - bool _checked = false; - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 10, - children: colors - .map((e) => CupertinoSwitch( - value: _checked, - activeColor: e, - onChanged: (v) { - setState(() => _checked = v); - })).toList(), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/CupertinoTabBar/node1_base.dart b/packages/widgets/lib/StatefulWidget/CupertinoTabBar/node1_base.dart deleted file mode 100644 index 218c5c747..000000000 --- a/packages/widgets/lib/StatefulWidget/CupertinoTabBar/node1_base.dart +++ /dev/null @@ -1,77 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-03-31 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 63, -// "name": 'CupertinoTabBar基本用法', -// "priority": 1, -// "subtitle": -// "【currentIndex】 : 当前激活索引 【Widget】\n" -// "【items】 : 条目组件 【Widget】\n" -// "【backgroundColor】 : 背景色 【Color】\n" -// "【inactiveColor】 : 非激活色 【Color】\n" -// "【activeColor】 : 激活色 【Color】\n" -// "【iconSize】 : 图标大小 【double】\n" -// "【border】 : 边线 【Border】\n" -// "【onTap】 : 点击事件 【Function(int)】", -// } -class CustomCupertinoTabBar extends StatefulWidget { - const CustomCupertinoTabBar({Key? key}) : super(key: key); - - @override - _CustomCupertinoTabBarState createState() => _CustomCupertinoTabBarState(); -} - -class _CustomCupertinoTabBarState extends State { - int _position = 0; - final Map iconsMap = { - //底栏图标 - "图鉴": Icons.home, "动态": Icons.toys, - "喜欢": Icons.favorite, "手册": Icons.class_, - "我的": Icons.account_circle, - }; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - _buildContent(context), - _buildTabBar(), - ], - ); - } - - Widget _buildTabBar() { - return CupertinoTabBar( - currentIndex: _position, - onTap: (value) => setState(() => _position = value), - items: iconsMap.keys - .map((e) => BottomNavigationBarItem( - icon: Icon( - iconsMap[e], - ), - label: e, - )) - .toList(), - activeColor: Colors.blue, - inactiveColor: const Color(0xff333333), - backgroundColor: const Color(0xfff1f1f1), - iconSize: 25.0, - ); - } - - Widget _buildContent(BuildContext context) { - return Container( - alignment: Alignment.center, - width: MediaQuery.of(context).size.width, - height: 150, - color: const Color(0xffE7F3FC), - child: Text( - iconsMap.keys.toList()[_position], - style: const TextStyle(color: Colors.blue, fontSize: 24), - ), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/CupertinoTabScaffold/node1_base.dart b/packages/widgets/lib/StatefulWidget/CupertinoTabScaffold/node1_base.dart deleted file mode 100644 index 715b0fd59..000000000 --- a/packages/widgets/lib/StatefulWidget/CupertinoTabScaffold/node1_base.dart +++ /dev/null @@ -1,69 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-26 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 158, -// "name": 'CupertinoTabScaffold基本用法', -// "priority": 1, -// "subtitle": -// "【tabBar】 : 页签条 【CupertinoTabBar】\n" -// "【backgroundColor】 : 背景色 【Color】\n" -// "【controller】 : 控制器 【CupertinoTabController】\n" -// "【tabBuilder】 : 页面构造器 【IndexedWidgetBuilder】", -// } -class CustomCupertinoTabScaffold extends StatefulWidget { - const CustomCupertinoTabScaffold({Key? key}) : super(key: key); - - @override - _CustomCupertinoTabScaffoldState createState() => - _CustomCupertinoTabScaffoldState(); -} - -class _CustomCupertinoTabScaffoldState - extends State { - int _position = 0; - final Map iconsMap = { - //底栏图标 - "图鉴": Icons.home, "动态": Icons.toys, - "喜欢": Icons.favorite, "手册": Icons.class_, - "我的": Icons.account_circle, - }; - - @override - Widget build(BuildContext context) { - return SizedBox( - width: MediaQuery.of(context).size.width, - height: MediaQuery.of(context).size.height - 300, - child: CupertinoTabScaffold( - backgroundColor: Colors.grey.withAlpha(11), - tabBar: _buildTabBar(), - tabBuilder: (_, index) => _buildContent(index)), - ); - } - - CupertinoTabBar _buildTabBar() => CupertinoTabBar( - currentIndex: _position, - onTap: (value) => setState(() => _position = value), - items: iconsMap.keys - .map((e) => BottomNavigationBarItem( - icon: Icon( - iconsMap[e], - ), - label: e, - )) - .toList(), - activeColor: Colors.blue, - inactiveColor: const Color(0xff333333), - backgroundColor: const Color(0xfff1f1f1), - iconSize: 25.0, - ); - - _buildContent(int index) => Container( - alignment: Alignment.center, - child: Text(iconsMap.keys.toList()[index]), - ); -} diff --git a/packages/widgets/lib/StatefulWidget/CupertinoTabView/node1_base.dart b/packages/widgets/lib/StatefulWidget/CupertinoTabView/node1_base.dart deleted file mode 100644 index 5ecde41f4..000000000 --- a/packages/widgets/lib/StatefulWidget/CupertinoTabView/node1_base.dart +++ /dev/null @@ -1,113 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 229 CupertinoTabView Cupertino页面 CupertinoTabView 可以像 MaterialApp 一样维护一个路由栈。通过 routes 、onGenerateRoute 来构建路由,可以通过 navigatorObservers 监听路由。 -// { -// "widgetId": 229, -// "name": 'CupertinoTabView基本使用', -// "priority": 1, -// "subtitle": -// "【builder】 : 主页构造器 【WidgetBuilder】\n" -// "【navigatorObservers】 : 路由监听器 【List】\n" -// "【routes】 : 路由映射 【Map】\n" -// "【onGenerateRoute】 : 路由工厂 【RouteFactory】", -// } - -class CupertinoTabViewDemo extends StatelessWidget { - const CupertinoTabViewDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - padding: const EdgeInsets.all(10), - child: ElevatedButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const CupertinoTabViewPage()), - ); - }, - child: const Text("进入 CupertinoTabView 测试页"), - ), - ); - } -} - - -class CupertinoTabViewPage extends StatelessWidget { - const CupertinoTabViewPage({Key? key}) : super(key: key); - - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 300, - child: CupertinoTabView( - routes: { - '/': (context) => _HomePage(), - '/test_detail': (context) => const DetailPage(), - }, - ), - ); - } -} - -class DetailPage extends StatelessWidget { - const DetailPage({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return CupertinoPageScaffold( - navigationBar: const CupertinoNavigationBar( - middle: Text('我是详情页'), - ), - child: Center( - child: Container( - width: 200, - height: 200, - color: Colors.blue, - ), - ), - ); - } -} - -class _HomePage extends StatelessWidget { - - final String info = "CupertinoTabView 可以像 MaterialApp 一样维护一个路由栈。" - "通过 routes 、onGenerateRoute 来构建路由,可以通过 navigatorObservers 监听路由。" - "在这个路由栈中可以进行指定名称跳转,如下通过 /test_detail 跳到详情页。"; - - @override - Widget build(BuildContext context) { - return CupertinoPageScaffold( - navigationBar: const CupertinoNavigationBar( - middle: Text('我是主页'), - ), - child: Center(child: Column( - - children: [ - const Spacer(), - Material(child: Padding( - padding: const EdgeInsets.only(left:18.0,right: 18,bottom: 20), - child: Text(info), - )), - CupertinoButton( - padding: const EdgeInsets.only(left: 10,right: 10), - color: Colors.blue, - onPressed: () { - Navigator.pushNamed( - context, "/test_detail" - ); - }, - child: const Text("进入详情页"), - ), - const Spacer(), - ], - )), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/CupertinoTextField/node1_base.dart b/packages/widgets/lib/StatefulWidget/CupertinoTextField/node1_base.dart deleted file mode 100644 index 2b8eba399..000000000 --- a/packages/widgets/lib/StatefulWidget/CupertinoTextField/node1_base.dart +++ /dev/null @@ -1,70 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/7/22 -/// contact me by email 1981462002@qq.com -/// 说明: 245 CupertinoTextField 1 Cupertino风格的输入框,属性和TextField类似,可指定控制器、文字样式、装饰线、行数限制、游标样式等。接收输入变化、完成输入等事件。 -// { -// "widgetId": 245, -// "name": "CupertinoTextField基础使用", -// "priority": 1, -// "subtitle": "【placeholder】 : 提示文字 【String】\n" -// "【showCursor】 : 是否显示游标 【bool】\n" -// "【minLines】 : 最小行数 【int】\n" -// "【maxLines】 : 最大行数 【int】\n" -// "【padding】 : 内边距 【EdgeInsetsGeometry】\n" -// "【onChanged】 : 变化监听 【ValueChanged】\n" -// "【onTap】: 点击监听 【GestureTapCallback】\n" -// "【onSubmitted】: 提交监听 【ValueChanged】", -// } -class CupertinoTextFieldDemo extends StatefulWidget { - const CupertinoTextFieldDemo({Key? key}) : super(key: key); - - @override - _CupertinoTextFieldDemoState createState() => _CupertinoTextFieldDemoState(); -} - -class _CupertinoTextFieldDemoState extends State { - String _value = ''; - Color _color =Colors.black; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Text('输入了:$_value',style: TextStyle(color: _color),), - CupertinoTextField( - placeholder: 'Input Name', - showCursor: true, - minLines: 1, - maxLines: 4, - padding: const EdgeInsets.all(8), - onChanged: _onChanged, - onTap: _onTap, - onSubmitted: _onSubmitted, - ), - ], - ); - } - - void _onChanged(String value) { - setState(() { - _value = value; - }); - } - - void _onTap() { - print('----_onTap----'); - setState(() { - _color=Colors.blue; - }); - } - - void _onSubmitted(String value) { - print('----_onSubmitted:$value}----'); - setState(() { - _color=Colors.black; - }); - } - -} diff --git a/packages/widgets/lib/StatefulWidget/CupertinoTextField/node2_style.dart b/packages/widgets/lib/StatefulWidget/CupertinoTextField/node2_style.dart deleted file mode 100644 index 4d30c68e3..000000000 --- a/packages/widgets/lib/StatefulWidget/CupertinoTextField/node2_style.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/7/22 -/// contact me by email 1981462002@qq.com -/// 说明: 245 CupertinoTextField 1 Cupertino风格的输入框,属性和TextField类似,可指定控制器、文字样式、装饰线、行数限制、游标样式等。接收输入变化、完成输入等事件。 -// { -// "widgetId": 245, -// "name": 'CupertinoTextField常用样式属性', -// "priority": 2, -// "subtitle": "【style】 : 输入文字样式 【TextStyle】\n" -// "【prefix】: 前缀组件 【Widget】\n" -// "【prefixMode】: 前缀模式 【OverlayVisibilityMode】\n" -// "【suffix】: 后缀组件 【Widget】\n" -// "【suffixMode】: 后缀模式 【OverlayVisibilityMode】\n" -// "【cursorColor】: 游标颜色 【Color】\n" -// "【cursorWidth】: 游标宽度 【double】\n" -// "【cursorRadius】: 游标圆角 【Radius】\n" -// "【readOnly】: 是否只读 【bool】", -// } -class CupertinoTextFieldStyle extends StatelessWidget { - const CupertinoTextFieldStyle({Key? key}) : super(key: key); - - - @override - Widget build(BuildContext context) { - return const CupertinoTextField( - style: TextStyle(color: Colors.blue), - prefix: Icon(CupertinoIcons.add), - prefixMode: OverlayVisibilityMode.notEditing, - suffix: Icon(CupertinoIcons.clear), - suffixMode: OverlayVisibilityMode.editing, - cursorColor: Colors.purple, - cursorWidth: 4, - cursorRadius: Radius.circular(2), - readOnly: false, - placeholder: '输入用户名', - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/CupertinoTimerPicker/node1_base.dart b/packages/widgets/lib/StatefulWidget/CupertinoTimerPicker/node1_base.dart deleted file mode 100644 index c6f39da1c..000000000 --- a/packages/widgets/lib/StatefulWidget/CupertinoTimerPicker/node1_base.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 138, -// "name": 'CupertinoTimerPicker基本使用', -// "priority": 1, -// "subtitle": -// "【initialTimerDuration】 : 初始时间 【Duration】\n" -// "【minuteInterval】 : 分钟间隔数 【double】\n" -// "【secondInterval】 : 秒间隔数 【double】\n" -// "【alignment】 : 对齐方式 【AlignmentGeometry】\n" -// "【backgroundColor】 : 背景色 【Color】\n" -// "【mode】 : 模式*3 【CupertinoTimerPickerMode】", -// } -class CustomCupertinoTimerPicker extends StatefulWidget { - const CustomCupertinoTimerPicker({Key? key}) : super(key: key); - - @override - _CustomCupertinoTimerPickerState createState() => - _CustomCupertinoTimerPickerState(); -} - -class _CustomCupertinoTimerPickerState - extends State { - Duration _date = const Duration(seconds: 30); - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Text( - '当前时间:${_date.toString()}', - style: const TextStyle(color: Colors.grey, fontSize: 16), - ), - _buildInfoTitle('CupertinoTimerPickerMode.hms'), - buildPicker(CupertinoTimerPickerMode.hms), - _buildInfoTitle('CupertinoTimerPickerMode.hm'), - buildPicker(CupertinoTimerPickerMode.hm), - _buildInfoTitle('CupertinoTimerPickerMode.ms'), - buildPicker(CupertinoTimerPickerMode.ms), - ], - ); - } - - Widget _buildInfoTitle(info) { - return Padding( - padding: const EdgeInsets.only(left: 20, top: 20, bottom: 5), - child: Text( - info, - style: const TextStyle( - color: Colors.blue, fontSize: 16, fontWeight: FontWeight.bold), - ), - ); - } - - Widget buildPicker(CupertinoTimerPickerMode mode) { - return Container( - margin: const EdgeInsets.all(10), - height: 150, - child: CupertinoTimerPicker( - mode: mode, - initialTimerDuration: const Duration(seconds: 30), - onTimerDurationChanged: (date) { - print(date); - setState(() => _date = date); - }, - ), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/DateRangePickerDialog/node1_base.dart b/packages/widgets/lib/StatefulWidget/DateRangePickerDialog/node1_base.dart deleted file mode 100644 index 70ff91d89..000000000 --- a/packages/widgets/lib/StatefulWidget/DateRangePickerDialog/node1_base.dart +++ /dev/null @@ -1,78 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 339, -// "name": 'DateRangePickerDialog 基本使用', -// "priority": 1, -// "subtitle": -// "【firstDate】 : 最早日期 【DateTime】\n" -// "【lastDate】 : 最晚日期 【DateTime】\n" -// "【initialDateRange】 : 初始范围 【DateTimeRange?】\n" -// "【saveText】 : 保存文字 【String?】", -// } -class DateRangePickerDialogDemo extends StatefulWidget { - const DateRangePickerDialogDemo({Key? key}) : super(key: key); - - @override - _DateRangePickerDialogDemoState createState() => - _DateRangePickerDialogDemoState(); -} - -class _DateRangePickerDialogDemoState - extends State { - String _dateRange = ''; - - // 需要 intl 包 - final DateFormat format = DateFormat('yyyy-MM-dd'); - - @override - Widget build(BuildContext context) { - String info = _dateRange.isEmpty ? '选择日期范围' : _dateRange; - - return Center( - child: Wrap( - alignment: WrapAlignment.center, - direction: Axis.vertical, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - ElevatedButton( - onPressed: _show, - child: const Text('选择'), - ), - Text( - info, - style: const TextStyle(color: Colors.grey), - ) - ], - ), - ); - } - - void _show() async { - DateTime firstDate = DateTime(2021, 1, 1); - DateTime lastDate = DateTime.now(); - DateTime start = lastDate.add(const Duration(days: -8)); - DateTime end = lastDate.add(const Duration(days: -2)); - DateTimeRange? range = await showDateRangePicker( - context: context, - firstDate: firstDate, - lastDate: lastDate, - initialDateRange: DateTimeRange( - start: start, - end: end, - ), - saveText: "确定", - ); - if (range != null) { - setState(() { - _dateRange = - "${format.format(range.start)} ~ ${format.format(range.end)}"; - }); - } - } -} diff --git a/packages/widgets/lib/StatefulWidget/DateRangePickerDialog/node2_diy.dart b/packages/widgets/lib/StatefulWidget/DateRangePickerDialog/node2_diy.dart deleted file mode 100644 index 1bbd1c87a..000000000 --- a/packages/widgets/lib/StatefulWidget/DateRangePickerDialog/node2_diy.dart +++ /dev/null @@ -1,75 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; -import 'toly_date_picker.dart' as toly; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 339, -// "name": '魔改 DateRangePickerDialog', -// "priority": 2, -// "subtitle": "修改 DateRangePickerDialog 源码,使得月份条目显示数值背景。", -// } -class DiyDateRangePickerDialogDemo extends StatefulWidget { - const DiyDateRangePickerDialogDemo({Key? key}) : super(key: key); - - @override - _DiyDateRangePickerDialogDemoState createState() => - _DiyDateRangePickerDialogDemoState(); -} - -class _DiyDateRangePickerDialogDemoState - extends State { - String _dateRange = ''; - - // 需要 intl 包 - final DateFormat format = DateFormat('yyyy-MM-dd'); - - @override - Widget build(BuildContext context) { - String info = _dateRange.isEmpty ? '选择日期范围' : _dateRange; - - return Center( - child: Wrap( - alignment: WrapAlignment.center, - direction: Axis.vertical, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - ElevatedButton( - onPressed: _show, - child: const Text('选择'), - ), - Text( - info, - style: const TextStyle(color: Colors.grey), - ) - ], - ), - ); - } - - void _show() async { - DateTime firstDate = DateTime(2021, 1, 1); - DateTime lastDate = DateTime.now(); - DateTime start = lastDate.add(const Duration(days: -8)); - DateTime end = lastDate.add(const Duration(days: -2)); - DateTimeRange? range = await toly.showDateRangePicker( - context: context, - firstDate: firstDate, - lastDate: lastDate, - initialDateRange: DateTimeRange( - start: start, - end: end, - ), - saveText: "确定", - ); - if (range != null) { - setState(() { - _dateRange = - "${format.format(range.start)} ~ ${format.format(range.end)}"; - }); - } - } -} diff --git a/packages/widgets/lib/StatefulWidget/DateRangePickerDialog/toly_date_picker.dart b/packages/widgets/lib/StatefulWidget/DateRangePickerDialog/toly_date_picker.dart deleted file mode 100644 index fd3dfd2d1..000000000 --- a/packages/widgets/lib/StatefulWidget/DateRangePickerDialog/toly_date_picker.dart +++ /dev/null @@ -1,2910 +0,0 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:math' as math; - -import 'package:flutter/gestures.dart' show DragStartBehavior; -import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; - -const Size _calendarPortraitDialogSize = Size(330.0, 518.0); -const Size _calendarLandscapeDialogSize = Size(496.0, 346.0); -const Size _inputPortraitDialogSize = Size(330.0, 270.0); -const Size _inputLandscapeDialogSize = Size(496, 160.0); -const Size _inputRangeLandscapeDialogSize = Size(496, 164.0); -const Duration _dialogSizeAnimationDuration = Duration(milliseconds: 200); -const double _inputFormPortraitHeight = 98.0; -const double _inputFormLandscapeHeight = 108.0; - -/// Shows a dialog containing a Material Design date picker. -/// -/// The returned [Future] resolves to the date selected by the user when the -/// user confirms the dialog. If the user cancels the dialog, null is returned. -/// -/// When the date picker is first displayed, it will show the month of -/// [initialDate], with [initialDate] selected. -/// -/// The [firstDate] is the earliest allowable date. The [lastDate] is the latest -/// allowable date. [initialDate] must either fall between these dates, -/// or be equal to one of them. For each of these [DateTime] parameters, only -/// their dates are considered. Their time fields are ignored. They must all -/// be non-null. -/// -/// The [currentDate] represents the current day (i.e. today). This -/// date will be highlighted in the day grid. If null, the date of -/// `DateTime.now()` will be used. -/// -/// An optional [initialEntryMode] argument can be used to display the date -/// picker in the [DatePickerEntryMode.calendar] (a calendar month grid) -/// or [DatePickerEntryMode.input] (a text input field) mode. -/// It defaults to [DatePickerEntryMode.calendar] and must be non-null. -/// -/// An optional [selectableDayPredicate] function can be passed in to only allow -/// certain days for selection. If provided, only the days that -/// [selectableDayPredicate] returns true for will be selectable. For example, -/// this can be used to only allow weekdays for selection. If provided, it must -/// return true for [initialDate]. -/// -/// The following optional string parameters allow you to override the default -/// text used for various parts of the dialog: -/// -/// * [helpText], label displayed at the top of the dialog. -/// * [cancelText], label on the cancel button. -/// * [confirmText], label on the ok button. -/// * [errorFormatText], message used when the input text isn't in a proper date format. -/// * [errorInvalidText], message used when the input text isn't a selectable date. -/// * [fieldHintText], text used to prompt the user when no text has been entered in the field. -/// * [fieldLabelText], label for the date text input field. -/// -/// An optional [locale] argument can be used to set the locale for the date -/// picker. It defaults to the ambient locale provided by [Localizations]. -/// -/// An optional [textDirection] argument can be used to set the text direction -/// ([TextDirection.ltr] or [TextDirection.rtl]) for the date picker. It -/// defaults to the ambient text direction provided by [Directionality]. If both -/// [locale] and [textDirection] are non-null, [textDirection] overrides the -/// direction chosen for the [locale]. -/// -/// The [context], [useRootNavigator] and [routeSettings] arguments are passed to -/// [showDialog], the documentation for which discusses how it is used. [context] -/// and [useRootNavigator] must be non-null. -/// -/// The [builder] parameter can be used to wrap the dialog widget -/// to add inherited widgets like [Theme]. -/// -/// An optional [initialDatePickerMode] argument can be used to have the -/// calendar date picker initially appear in the [DatePickerMode.year] or -/// [DatePickerMode.day] mode. It defaults to [DatePickerMode.day], and -/// must be non-null. -/// -/// {@macro flutter.widgets.RawDialogRoute} -/// -/// ### State Restoration -/// -/// Using this method will not enable state restoration for the date picker. -/// In order to enable state restoration for a date picker, use -/// [Navigator.restorablePush] or [Navigator.restorablePushNamed] with -/// [DatePickerDialog]. -/// -/// For more information about state restoration, see [RestorationManager]. -/// -/// {@macro flutter.widgets.RestorationManager} -/// -/// {@tool dartpad} -/// This sample demonstrates how to create a restorable Material date picker. -/// This is accomplished by enabling state restoration by specifying -/// [MaterialApp.restorationScopeId] and using [Navigator.restorablePush] to -/// push [DatePickerDialog] when the button is tapped. -/// -/// ** See code in examples/api/lib/material/date_picker/show_date_picker.0.dart ** -/// {@end-tool} -/// -/// See also: -/// -/// * [showDateRangePicker], which shows a Material Design date range picker -/// used to select a range of dates. -/// * [CalendarDatePicker], which provides the calendar grid used by the date picker dialog. -/// * [InputDatePickerFormField], which provides a text input field for entering dates. -/// * [DisplayFeatureSubScreen], which documents the specifics of how -/// [DisplayFeature]s can split the screen into sub-screens. -/// * [showTimePicker], which shows a dialog that contains a Material Design time picker. -/// -Future showDatePicker({ - required BuildContext context, - required DateTime initialDate, - required DateTime firstDate, - required DateTime lastDate, - DateTime? currentDate, - DatePickerEntryMode initialEntryMode = DatePickerEntryMode.calendar, - SelectableDayPredicate? selectableDayPredicate, - String? helpText, - String? cancelText, - String? confirmText, - Locale? locale, - bool useRootNavigator = true, - RouteSettings? routeSettings, - TextDirection? textDirection, - TransitionBuilder? builder, - DatePickerMode initialDatePickerMode = DatePickerMode.day, - String? errorFormatText, - String? errorInvalidText, - String? fieldHintText, - String? fieldLabelText, - TextInputType? keyboardType, - Offset? anchorPoint, -}) async { - assert(context != null); - assert(initialDate != null); - assert(firstDate != null); - assert(lastDate != null); - initialDate = DateUtils.dateOnly(initialDate); - firstDate = DateUtils.dateOnly(firstDate); - lastDate = DateUtils.dateOnly(lastDate); - assert( - !lastDate.isBefore(firstDate), - 'lastDate $lastDate must be on or after firstDate $firstDate.', - ); - assert( - !initialDate.isBefore(firstDate), - 'initialDate $initialDate must be on or after firstDate $firstDate.', - ); - assert( - !initialDate.isAfter(lastDate), - 'initialDate $initialDate must be on or before lastDate $lastDate.', - ); - assert( - selectableDayPredicate == null || selectableDayPredicate(initialDate), - 'Provided initialDate $initialDate must satisfy provided selectableDayPredicate.', - ); - assert(initialEntryMode != null); - assert(useRootNavigator != null); - assert(initialDatePickerMode != null); - assert(debugCheckHasMaterialLocalizations(context)); - - Widget dialog = DatePickerDialog( - initialDate: initialDate, - firstDate: firstDate, - lastDate: lastDate, - currentDate: currentDate, - initialEntryMode: initialEntryMode, - selectableDayPredicate: selectableDayPredicate, - helpText: helpText, - cancelText: cancelText, - confirmText: confirmText, - initialCalendarMode: initialDatePickerMode, - errorFormatText: errorFormatText, - errorInvalidText: errorInvalidText, - fieldHintText: fieldHintText, - fieldLabelText: fieldLabelText, - keyboardType: keyboardType, - ); - - if (textDirection != null) { - dialog = Directionality( - textDirection: textDirection, - child: dialog, - ); - } - - if (locale != null) { - dialog = Localizations.override( - context: context, - locale: locale, - child: dialog, - ); - } - - return showDialog( - context: context, - useRootNavigator: useRootNavigator, - routeSettings: routeSettings, - builder: (BuildContext context) { - return builder == null ? dialog : builder(context, dialog); - }, - anchorPoint: anchorPoint, - ); -} - -/// A Material-style date picker dialog. -/// -/// It is used internally by [showDatePicker] or can be directly pushed -/// onto the [Navigator] stack to enable state restoration. See -/// [showDatePicker] for a state restoration app example. -/// -/// See also: -/// -/// * [showDatePicker], which is a way to display the date picker. -class DatePickerDialog extends StatefulWidget { - /// A Material-style date picker dialog. - DatePickerDialog({ - super.key, - required DateTime initialDate, - required DateTime firstDate, - required DateTime lastDate, - DateTime? currentDate, - this.initialEntryMode = DatePickerEntryMode.calendar, - this.selectableDayPredicate, - this.cancelText, - this.confirmText, - this.helpText, - this.initialCalendarMode = DatePickerMode.day, - this.errorFormatText, - this.errorInvalidText, - this.fieldHintText, - this.fieldLabelText, - this.keyboardType, - this.restorationId, - }) : assert(initialDate != null), - assert(firstDate != null), - assert(lastDate != null), - initialDate = DateUtils.dateOnly(initialDate), - firstDate = DateUtils.dateOnly(firstDate), - lastDate = DateUtils.dateOnly(lastDate), - currentDate = DateUtils.dateOnly(currentDate ?? DateTime.now()), - assert(initialEntryMode != null), - assert(initialCalendarMode != null) { - assert( - !this.lastDate.isBefore(this.firstDate), - 'lastDate ${this.lastDate} must be on or after firstDate ${this.firstDate}.', - ); - assert( - !this.initialDate.isBefore(this.firstDate), - 'initialDate ${this.initialDate} must be on or after firstDate ${this.firstDate}.', - ); - assert( - !this.initialDate.isAfter(this.lastDate), - 'initialDate ${this.initialDate} must be on or before lastDate ${this.lastDate}.', - ); - assert( - selectableDayPredicate == null || selectableDayPredicate!(this.initialDate), - 'Provided initialDate ${this.initialDate} must satisfy provided selectableDayPredicate', - ); - } - - /// The initially selected [DateTime] that the picker should display. - final DateTime initialDate; - - /// The earliest allowable [DateTime] that the user can select. - final DateTime firstDate; - - /// The latest allowable [DateTime] that the user can select. - final DateTime lastDate; - - /// The [DateTime] representing today. It will be highlighted in the day grid. - final DateTime currentDate; - - /// The initial mode of date entry method for the date picker dialog. - /// - /// See [DatePickerEntryMode] for more details on the different data entry - /// modes available. - final DatePickerEntryMode initialEntryMode; - - /// Function to provide full control over which [DateTime] can be selected. - final SelectableDayPredicate? selectableDayPredicate; - - /// The text that is displayed on the cancel button. - final String? cancelText; - - /// The text that is displayed on the confirm button. - final String? confirmText; - - /// The text that is displayed at the top of the header. - /// - /// This is used to indicate to the user what they are selecting a date for. - final String? helpText; - - /// The initial display of the calendar picker. - final DatePickerMode initialCalendarMode; - - /// The error text displayed if the entered date is not in the correct format. - final String? errorFormatText; - - /// The error text displayed if the date is not valid. - /// - /// A date is not valid if it is earlier than [firstDate], later than - /// [lastDate], or doesn't pass the [selectableDayPredicate]. - final String? errorInvalidText; - - /// The hint text displayed in the [TextField]. - /// - /// If this is null, it will default to the date format string. For example, - /// 'mm/dd/yyyy' for en_US. - final String? fieldHintText; - - /// The label text displayed in the [TextField]. - /// - /// If this is null, it will default to the words representing the date format - /// string. For example, 'Month, Day, Year' for en_US. - final String? fieldLabelText; - - /// The keyboard type of the [TextField]. - /// - /// If this is null, it will default to [TextInputType.datetime] - final TextInputType? keyboardType; - - /// Restoration ID to save and restore the state of the [DatePickerDialog]. - /// - /// If it is non-null, the date picker will persist and restore the - /// date selected on the dialog. - /// - /// The state of this widget is persisted in a [RestorationBucket] claimed - /// from the surrounding [RestorationScope] using the provided restoration ID. - /// - /// See also: - /// - /// * [RestorationManager], which explains how state restoration works in - /// Flutter. - final String? restorationId; - - @override - State createState() => _DatePickerDialogState(); -} - -class _DatePickerDialogState extends State with RestorationMixin { - late final RestorableDateTime _selectedDate = RestorableDateTime(widget.initialDate); - late final _RestorableDatePickerEntryMode _entryMode = _RestorableDatePickerEntryMode(widget.initialEntryMode); - final _RestorableAutovalidateMode _autovalidateMode = _RestorableAutovalidateMode(AutovalidateMode.disabled); - - @override - String? get restorationId => widget.restorationId; - - @override - void restoreState(RestorationBucket? oldBucket, bool initialRestore) { - registerForRestoration(_selectedDate, 'selected_date'); - registerForRestoration(_autovalidateMode, 'autovalidateMode'); - registerForRestoration(_entryMode, 'calendar_entry_mode'); - } - - final GlobalKey _calendarPickerKey = GlobalKey(); - final GlobalKey _formKey = GlobalKey(); - - void _handleOk() { - if (_entryMode.value == DatePickerEntryMode.input || _entryMode.value == DatePickerEntryMode.inputOnly) { - final FormState form = _formKey.currentState!; - if (!form.validate()) { - setState(() => _autovalidateMode.value = AutovalidateMode.always); - return; - } - form.save(); - } - Navigator.pop(context, _selectedDate.value); - } - - void _handleCancel() { - Navigator.pop(context); - } - - void _handleEntryModeToggle() { - setState(() { - switch (_entryMode.value) { - case DatePickerEntryMode.calendar: - _autovalidateMode.value = AutovalidateMode.disabled; - _entryMode.value = DatePickerEntryMode.input; - break; - case DatePickerEntryMode.input: - _formKey.currentState!.save(); - _entryMode.value = DatePickerEntryMode.calendar; - break; - case DatePickerEntryMode.calendarOnly: - case DatePickerEntryMode.inputOnly: - assert(false, 'Can not change entry mode from _entryMode'); - break; - } - }); - } - - void _handleDateChanged(DateTime date) { - setState(() { - _selectedDate.value = date; - }); - } - - Size _dialogSize(BuildContext context) { - final Orientation orientation = MediaQuery.of(context).orientation; - switch (_entryMode.value) { - case DatePickerEntryMode.calendar: - case DatePickerEntryMode.calendarOnly: - switch (orientation) { - case Orientation.portrait: - return _calendarPortraitDialogSize; - case Orientation.landscape: - return _calendarLandscapeDialogSize; - } - case DatePickerEntryMode.input: - case DatePickerEntryMode.inputOnly: - switch (orientation) { - case Orientation.portrait: - return _inputPortraitDialogSize; - case Orientation.landscape: - return _inputLandscapeDialogSize; - } - } - } - - static const Map _formShortcutMap = { - // Pressing enter on the field will move focus to the next field or control. - SingleActivator(LogicalKeyboardKey.enter): NextFocusIntent(), - }; - - @override - Widget build(BuildContext context) { - final ThemeData theme = Theme.of(context); - final ColorScheme colorScheme = theme.colorScheme; - final MaterialLocalizations localizations = MaterialLocalizations.of(context); - final Orientation orientation = MediaQuery.of(context).orientation; - final TextTheme textTheme = theme.textTheme; - // Constrain the textScaleFactor to the largest supported value to prevent - // layout issues. - final double textScaleFactor = math.min(MediaQuery.of(context).textScaleFactor, 1.3); - - final String dateText = localizations.formatMediumDate(_selectedDate.value); - final Color onPrimarySurface = colorScheme.brightness == Brightness.light - ? colorScheme.onPrimary - : colorScheme.onSurface; - final TextStyle? dateStyle = orientation == Orientation.landscape - ? textTheme.headline5?.copyWith(color: onPrimarySurface) - : textTheme.headline4?.copyWith(color: onPrimarySurface); - - final Widget actions = Container( - alignment: AlignmentDirectional.centerEnd, - constraints: const BoxConstraints(minHeight: 52.0), - padding: const EdgeInsets.symmetric(horizontal: 8), - child: OverflowBar( - spacing: 8, - children: [ - TextButton( - onPressed: _handleCancel, - child: Text(widget.cancelText ?? localizations.cancelButtonLabel), - ), - TextButton( - onPressed: _handleOk, - child: Text(widget.confirmText ?? localizations.okButtonLabel), - ), - ], - ), - ); - - CalendarDatePicker calendarDatePicker() { - return CalendarDatePicker( - key: _calendarPickerKey, - initialDate: _selectedDate.value, - firstDate: widget.firstDate, - lastDate: widget.lastDate, - currentDate: widget.currentDate, - onDateChanged: _handleDateChanged, - selectableDayPredicate: widget.selectableDayPredicate, - initialCalendarMode: widget.initialCalendarMode, - ); - } - - Form inputDatePicker() { - return Form( - key: _formKey, - autovalidateMode: _autovalidateMode.value, - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 24), - height: orientation == Orientation.portrait ? _inputFormPortraitHeight : _inputFormLandscapeHeight, - child: Shortcuts( - shortcuts: _formShortcutMap, - child: Column( - children: [ - const Spacer(), - InputDatePickerFormField( - initialDate: _selectedDate.value, - firstDate: widget.firstDate, - lastDate: widget.lastDate, - onDateSubmitted: _handleDateChanged, - onDateSaved: _handleDateChanged, - selectableDayPredicate: widget.selectableDayPredicate, - errorFormatText: widget.errorFormatText, - errorInvalidText: widget.errorInvalidText, - fieldHintText: widget.fieldHintText, - fieldLabelText: widget.fieldLabelText, - keyboardType: widget.keyboardType, - autofocus: true, - ), - const Spacer(), - ], - ), - ), - ), - ); - } - - final Widget picker; - final Widget? entryModeButton; - switch (_entryMode.value) { - case DatePickerEntryMode.calendar: - picker = calendarDatePicker(); - entryModeButton = IconButton( - icon: const Icon(Icons.edit), - color: onPrimarySurface, - tooltip: localizations.inputDateModeButtonLabel, - onPressed: _handleEntryModeToggle, - ); - break; - - case DatePickerEntryMode.calendarOnly: - picker = calendarDatePicker(); - entryModeButton = null; - break; - - case DatePickerEntryMode.input: - picker = inputDatePicker(); - entryModeButton = IconButton( - icon: const Icon(Icons.calendar_today), - color: onPrimarySurface, - tooltip: localizations.calendarModeButtonLabel, - onPressed: _handleEntryModeToggle, - ); - break; - - case DatePickerEntryMode.inputOnly: - picker = inputDatePicker(); - entryModeButton = null; - break; - } - - final Widget header = _DatePickerHeader( - helpText: widget.helpText ?? localizations.datePickerHelpText, - titleText: dateText, - titleStyle: dateStyle, - orientation: orientation, - isShort: orientation == Orientation.landscape, - entryModeButton: entryModeButton, - ); - - final Size dialogSize = _dialogSize(context) * textScaleFactor; - return Dialog( - insetPadding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 24.0), - clipBehavior: Clip.antiAlias, - child: AnimatedContainer( - width: dialogSize.width, - height: dialogSize.height, - duration: _dialogSizeAnimationDuration, - curve: Curves.easeIn, - child: MediaQuery( - data: MediaQuery.of(context).copyWith( - textScaleFactor: textScaleFactor, - ), - child: Builder(builder: (BuildContext context) { - switch (orientation) { - case Orientation.portrait: - return Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - header, - Expanded(child: picker), - actions, - ], - ); - case Orientation.landscape: - return Row( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - header, - Flexible( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Expanded(child: picker), - actions, - ], - ), - ), - ], - ); - } - }), - ), - ), - ); - } -} - -// A restorable [DatePickerEntryMode] value. -// -// This serializes each entry as a unique `int` value. -class _RestorableDatePickerEntryMode extends RestorableValue { - _RestorableDatePickerEntryMode( - DatePickerEntryMode defaultValue, - ) : _defaultValue = defaultValue; - - final DatePickerEntryMode _defaultValue; - - @override - DatePickerEntryMode createDefaultValue() => _defaultValue; - - @override - void didUpdateValue(DatePickerEntryMode? oldValue) { - assert(debugIsSerializableForRestoration(value.index)); - notifyListeners(); - } - - @override - DatePickerEntryMode fromPrimitives(Object? data) => DatePickerEntryMode.values[data! as int]; - - @override - Object? toPrimitives() => value.index; -} - -// A restorable [AutovalidateMode] value. -// -// This serializes each entry as a unique `int` value. -class _RestorableAutovalidateMode extends RestorableValue { - _RestorableAutovalidateMode( - AutovalidateMode defaultValue, - ) : _defaultValue = defaultValue; - - final AutovalidateMode _defaultValue; - - @override - AutovalidateMode createDefaultValue() => _defaultValue; - - @override - void didUpdateValue(AutovalidateMode? oldValue) { - assert(debugIsSerializableForRestoration(value.index)); - notifyListeners(); - } - - @override - AutovalidateMode fromPrimitives(Object? data) => AutovalidateMode.values[data! as int]; - - @override - Object? toPrimitives() => value.index; -} - -/// Re-usable widget that displays the selected date (in large font) and the -/// help text above it. -/// -/// These types include: -/// -/// * Single Date picker with calendar mode. -/// * Single Date picker with text input mode. -/// * Date Range picker with text input mode. -/// -/// [helpText], [orientation], [icon], [onIconPressed] are required and must be -/// non-null. -class _DatePickerHeader extends StatelessWidget { - - /// Creates a header for use in a date picker dialog. - const _DatePickerHeader({ - required this.helpText, - required this.titleText, - this.titleSemanticsLabel, - required this.titleStyle, - required this.orientation, - this.isShort = false, - this.entryModeButton, - }) : assert(helpText != null), - assert(orientation != null), - assert(isShort != null); - - static const double _datePickerHeaderLandscapeWidth = 152.0; - static const double _datePickerHeaderPortraitHeight = 120.0; - static const double _headerPaddingLandscape = 16.0; - - /// The text that is displayed at the top of the header. - /// - /// This is used to indicate to the user what they are selecting a date for. - final String helpText; - - /// The text that is displayed at the center of the header. - final String titleText; - - /// The semantic label associated with the [titleText]. - final String? titleSemanticsLabel; - - /// The [TextStyle] that the title text is displayed with. - final TextStyle? titleStyle; - - /// The orientation is used to decide how to layout its children. - final Orientation orientation; - - /// Indicates the header is being displayed in a shorter/narrower context. - /// - /// This will be used to tighten up the space between the help text and date - /// text if `true`. Additionally, it will use a smaller typography style if - /// `true`. - /// - /// This is necessary for displaying the manual input mode in - /// landscape orientation, in order to account for the keyboard height. - final bool isShort; - - final Widget? entryModeButton; - - @override - Widget build(BuildContext context) { - final ThemeData theme = Theme.of(context); - final ColorScheme colorScheme = theme.colorScheme; - final TextTheme textTheme = theme.textTheme; - - // The header should use the primary color in light themes and surface color in dark - final bool isDark = colorScheme.brightness == Brightness.dark; - final Color primarySurfaceColor = isDark ? colorScheme.surface : colorScheme.primary; - final Color onPrimarySurfaceColor = isDark ? colorScheme.onSurface : colorScheme.onPrimary; - - final TextStyle? helpStyle = textTheme.overline?.copyWith( - color: onPrimarySurfaceColor, - ); - - final Text help = Text( - helpText, - style: helpStyle, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ); - final Text title = Text( - titleText, - semanticsLabel: titleSemanticsLabel ?? titleText, - style: titleStyle, - maxLines: orientation == Orientation.portrait ? 1 : 2, - overflow: TextOverflow.ellipsis, - ); - - switch (orientation) { - case Orientation.portrait: - return SizedBox( - height: _datePickerHeaderPortraitHeight, - child: Material( - color: primarySurfaceColor, - child: Padding( - padding: const EdgeInsetsDirectional.only( - start: 24, - end: 12, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 16), - help, - const Flexible(child: SizedBox(height: 38)), - Row( - children: [ - Expanded(child: title), - if (entryModeButton != null) - entryModeButton!, - ], - ), - ], - ), - ), - ), - ); - case Orientation.landscape: - return SizedBox( - width: _datePickerHeaderLandscapeWidth, - child: Material( - color: primarySurfaceColor, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 16), - Padding( - padding: const EdgeInsets.symmetric( - horizontal: _headerPaddingLandscape, - ), - child: help, - ), - SizedBox(height: isShort ? 16 : 56), - Expanded( - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: _headerPaddingLandscape, - ), - child: title, - ), - ), - if (entryModeButton != null) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 4), - child: entryModeButton, - ), - ], - ), - ), - ); - } - } -} - -/// Shows a full screen modal dialog containing a Material Design date range -/// picker. -/// -/// The returned [Future] resolves to the [DateTimeRange] selected by the user -/// when the user saves their selection. If the user cancels the dialog, null is -/// returned. -/// -/// If [initialDateRange] is non-null, then it will be used as the initially -/// selected date range. If it is provided, `initialDateRange.start` must be -/// before or on `initialDateRange.end`. -/// -/// The [firstDate] is the earliest allowable date. The [lastDate] is the latest -/// allowable date. Both must be non-null. -/// -/// If an initial date range is provided, `initialDateRange.start` -/// and `initialDateRange.end` must both fall between or on [firstDate] and -/// [lastDate]. For all of these [DateTime] values, only their dates are -/// considered. Their time fields are ignored. -/// -/// The [currentDate] represents the current day (i.e. today). This -/// date will be highlighted in the day grid. If null, the date of -/// `DateTime.now()` will be used. -/// -/// An optional [initialEntryMode] argument can be used to display the date -/// picker in the [DatePickerEntryMode.calendar] (a scrollable calendar month -/// grid) or [DatePickerEntryMode.input] (two text input fields) mode. -/// It defaults to [DatePickerEntryMode.calendar] and must be non-null. -/// -/// The following optional string parameters allow you to override the default -/// text used for various parts of the dialog: -/// -/// * [helpText], the label displayed at the top of the dialog. -/// * [cancelText], the label on the cancel button for the text input mode. -/// * [confirmText],the label on the ok button for the text input mode. -/// * [saveText], the label on the save button for the fullscreen calendar -/// mode. -/// * [errorFormatText], the message used when an input text isn't in a proper -/// date format. -/// * [errorInvalidText], the message used when an input text isn't a -/// selectable date. -/// * [errorInvalidRangeText], the message used when the date range is -/// invalid (e.g. start date is after end date). -/// * [fieldStartHintText], the text used to prompt the user when no text has -/// been entered in the start field. -/// * [fieldEndHintText], the text used to prompt the user when no text has -/// been entered in the end field. -/// * [fieldStartLabelText], the label for the start date text input field. -/// * [fieldEndLabelText], the label for the end date text input field. -/// -/// An optional [locale] argument can be used to set the locale for the date -/// picker. It defaults to the ambient locale provided by [Localizations]. -/// -/// An optional [textDirection] argument can be used to set the text direction -/// ([TextDirection.ltr] or [TextDirection.rtl]) for the date picker. It -/// defaults to the ambient text direction provided by [Directionality]. If both -/// [locale] and [textDirection] are non-null, [textDirection] overrides the -/// direction chosen for the [locale]. -/// -/// The [context], [useRootNavigator] and [routeSettings] arguments are passed -/// to [showDialog], the documentation for which discusses how it is used. -/// [context] and [useRootNavigator] must be non-null. -/// -/// The [builder] parameter can be used to wrap the dialog widget -/// to add inherited widgets like [Theme]. -/// -/// {@macro flutter.widgets.RawDialogRoute} -/// -/// ### State Restoration -/// -/// Using this method will not enable state restoration for the date range picker. -/// In order to enable state restoration for a date range picker, use -/// [Navigator.restorablePush] or [Navigator.restorablePushNamed] with -/// [DateRangePickerDialog]. -/// -/// For more information about state restoration, see [RestorationManager]. -/// -/// {@macro flutter.widgets.RestorationManager} -/// -/// {@tool sample} -/// This sample demonstrates how to create a restorable Material date range picker. -/// This is accomplished by enabling state restoration by specifying -/// [MaterialApp.restorationScopeId] and using [Navigator.restorablePush] to -/// push [DateRangePickerDialog] when the button is tapped. -/// -/// ** See code in examples/api/lib/material/date_picker/show_date_range_picker.0.dart ** -/// {@end-tool} -/// -/// See also: -/// -/// * [showDatePicker], which shows a Material Design date picker used to -/// select a single date. -/// * [DateTimeRange], which is used to describe a date range. -/// * [DisplayFeatureSubScreen], which documents the specifics of how -/// [DisplayFeature]s can split the screen into sub-screens. -Future showDateRangePicker({ - required BuildContext context, - DateTimeRange? initialDateRange, - required DateTime firstDate, - required DateTime lastDate, - DateTime? currentDate, - DatePickerEntryMode initialEntryMode = DatePickerEntryMode.calendar, - String? helpText, - String? cancelText, - String? confirmText, - String? saveText, - String? errorFormatText, - String? errorInvalidText, - String? errorInvalidRangeText, - String? fieldStartHintText, - String? fieldEndHintText, - String? fieldStartLabelText, - String? fieldEndLabelText, - Locale? locale, - bool useRootNavigator = true, - RouteSettings? routeSettings, - TextDirection? textDirection, - TransitionBuilder? builder, - Offset? anchorPoint, -}) async { - assert(context != null); - assert( - initialDateRange == null || (initialDateRange.start != null && initialDateRange.end != null), - 'initialDateRange must be null or have non-null start and end dates.', - ); - assert( - initialDateRange == null || !initialDateRange.start.isAfter(initialDateRange.end), - "initialDateRange's start date must not be after it's end date.", - ); - initialDateRange = initialDateRange == null ? null : DateUtils.datesOnly(initialDateRange); - assert(firstDate != null); - firstDate = DateUtils.dateOnly(firstDate); - assert(lastDate != null); - lastDate = DateUtils.dateOnly(lastDate); - assert( - !lastDate.isBefore(firstDate), - 'lastDate $lastDate must be on or after firstDate $firstDate.', - ); - assert( - initialDateRange == null || !initialDateRange.start.isBefore(firstDate), - "initialDateRange's start date must be on or after firstDate $firstDate.", - ); - assert( - initialDateRange == null || !initialDateRange.end.isBefore(firstDate), - "initialDateRange's end date must be on or after firstDate $firstDate.", - ); - assert( - initialDateRange == null || !initialDateRange.start.isAfter(lastDate), - "initialDateRange's start date must be on or before lastDate $lastDate.", - ); - assert( - initialDateRange == null || !initialDateRange.end.isAfter(lastDate), - "initialDateRange's end date must be on or before lastDate $lastDate.", - ); - currentDate = DateUtils.dateOnly(currentDate ?? DateTime.now()); - assert(initialEntryMode != null); - assert(useRootNavigator != null); - assert(debugCheckHasMaterialLocalizations(context)); - - Widget dialog = DateRangePickerDialog( - initialDateRange: initialDateRange, - firstDate: firstDate, - lastDate: lastDate, - currentDate: currentDate, - initialEntryMode: initialEntryMode, - helpText: helpText, - cancelText: cancelText, - confirmText: confirmText, - saveText: saveText, - errorFormatText: errorFormatText, - errorInvalidText: errorInvalidText, - errorInvalidRangeText: errorInvalidRangeText, - fieldStartHintText: fieldStartHintText, - fieldEndHintText: fieldEndHintText, - fieldStartLabelText: fieldStartLabelText, - fieldEndLabelText: fieldEndLabelText, - ); - - if (textDirection != null) { - dialog = Directionality( - textDirection: textDirection, - child: dialog, - ); - } - - if (locale != null) { - dialog = Localizations.override( - context: context, - locale: locale, - child: dialog, - ); - } - - return showDialog( - context: context, - useRootNavigator: useRootNavigator, - routeSettings: routeSettings, - useSafeArea: false, - builder: (BuildContext context) { - return builder == null ? dialog : builder(context, dialog); - }, - anchorPoint: anchorPoint, - ); -} - -/// Returns a locale-appropriate string to describe the start of a date range. -/// -/// If `startDate` is null, then it defaults to 'Start Date', otherwise if it -/// is in the same year as the `endDate` then it will use the short month -/// day format (i.e. 'Jan 21'). Otherwise it will return the short date format -/// (i.e. 'Jan 21, 2020'). -String _formatRangeStartDate(MaterialLocalizations localizations, DateTime? startDate, DateTime? endDate) { - return startDate == null - ? localizations.dateRangeStartLabel - : (endDate == null || startDate.year == endDate.year) - ? localizations.formatShortMonthDay(startDate) - : localizations.formatShortDate(startDate); -} - -/// Returns an locale-appropriate string to describe the end of a date range. -/// -/// If `endDate` is null, then it defaults to 'End Date', otherwise if it -/// is in the same year as the `startDate` and the `currentDate` then it will -/// just use the short month day format (i.e. 'Jan 21'), otherwise it will -/// include the year (i.e. 'Jan 21, 2020'). -String _formatRangeEndDate(MaterialLocalizations localizations, DateTime? startDate, DateTime? endDate, DateTime currentDate) { - return endDate == null - ? localizations.dateRangeEndLabel - : (startDate != null && startDate.year == endDate.year && startDate.year == currentDate.year) - ? localizations.formatShortMonthDay(endDate) - : localizations.formatShortDate(endDate); -} - -/// A Material-style date range picker dialog. -/// -/// It is used internally by [showDateRangePicker] or can be directly pushed -/// onto the [Navigator] stack to enable state restoration. See -/// [showDateRangePicker] for a state restoration app example. -/// -/// See also: -/// -/// * [showDateRangePicker], which is a way to display the date picker. -class DateRangePickerDialog extends StatefulWidget { - /// A Material-style date range picker dialog. - const DateRangePickerDialog({ - super.key, - this.initialDateRange, - required this.firstDate, - required this.lastDate, - this.currentDate, - this.initialEntryMode = DatePickerEntryMode.calendar, - this.helpText, - this.cancelText, - this.confirmText, - this.saveText, - this.errorInvalidRangeText, - this.errorFormatText, - this.errorInvalidText, - this.fieldStartHintText, - this.fieldEndHintText, - this.fieldStartLabelText, - this.fieldEndLabelText, - this.restorationId, - }); - - /// The date range that the date range picker starts with when it opens. - /// - /// If an initial date range is provided, `initialDateRange.start` - /// and `initialDateRange.end` must both fall between or on [firstDate] and - /// [lastDate]. For all of these [DateTime] values, only their dates are - /// considered. Their time fields are ignored. - /// - /// If [initialDateRange] is non-null, then it will be used as the initially - /// selected date range. If it is provided, `initialDateRange.start` must be - /// before or on `initialDateRange.end`. - final DateTimeRange? initialDateRange; - - /// The earliest allowable date on the date range. - final DateTime firstDate; - - /// The latest allowable date on the date range. - final DateTime lastDate; - - /// The [currentDate] represents the current day (i.e. today). - /// - /// This date will be highlighted in the day grid. - /// - /// If `null`, the date of `DateTime.now()` will be used. - final DateTime? currentDate; - - /// The initial date range picker entry mode. - /// - /// The date range has two main modes: [DatePickerEntryMode.calendar] (a - /// scrollable calendar month grid) or [DatePickerEntryMode.input] (two text - /// input fields) mode. - /// - /// It defaults to [DatePickerEntryMode.calendar] and must be non-null. - final DatePickerEntryMode initialEntryMode; - - /// The label on the cancel button for the text input mode. - /// - /// If null, the localized value of - /// [MaterialLocalizations.cancelButtonLabel] is used. - final String? cancelText; - - /// The label on the "OK" button for the text input mode. - /// - /// If null, the localized value of - /// [MaterialLocalizations.okButtonLabel] is used. - final String? confirmText; - - /// The label on the save button for the fullscreen calendar mode. - /// - /// If null, the localized value of - /// [MaterialLocalizations.saveButtonLabel] is used. - final String? saveText; - - /// The label displayed at the top of the dialog. - /// - /// If null, the localized value of - /// [MaterialLocalizations.dateRangePickerHelpText] is used. - final String? helpText; - - /// The message used when the date range is invalid (e.g. start date is after - /// end date). - /// - /// If null, the localized value of - /// [MaterialLocalizations.invalidDateRangeLabel] is used. - final String? errorInvalidRangeText; - - /// The message used when an input text isn't in a proper date format. - /// - /// If null, the localized value of - /// [MaterialLocalizations.invalidDateFormatLabel] is used. - final String? errorFormatText; - - /// The message used when an input text isn't a selectable date. - /// - /// If null, the localized value of - /// [MaterialLocalizations.dateOutOfRangeLabel] is used. - final String? errorInvalidText; - - /// The text used to prompt the user when no text has been entered in the - /// start field. - /// - /// If null, the localized value of - /// [MaterialLocalizations.dateHelpText] is used. - final String? fieldStartHintText; - - /// The text used to prompt the user when no text has been entered in the - /// end field. - /// - /// If null, the localized value of [MaterialLocalizations.dateHelpText] is - /// used. - final String? fieldEndHintText; - - /// The label for the start date text input field. - /// - /// If null, the localized value of [MaterialLocalizations.dateRangeStartLabel] - /// is used. - final String? fieldStartLabelText; - - /// The label for the end date text input field. - /// - /// If null, the localized value of [MaterialLocalizations.dateRangeEndLabel] - /// is used. - final String? fieldEndLabelText; - - /// Restoration ID to save and restore the state of the [DateRangePickerDialog]. - /// - /// If it is non-null, the date range picker will persist and restore the - /// date range selected on the dialog. - /// - /// The state of this widget is persisted in a [RestorationBucket] claimed - /// from the surrounding [RestorationScope] using the provided restoration ID. - /// - /// See also: - /// - /// * [RestorationManager], which explains how state restoration works in - /// Flutter. - final String? restorationId; - - @override - State createState() => _DateRangePickerDialogState(); -} - -class _DateRangePickerDialogState extends State with RestorationMixin { - late final _RestorableDatePickerEntryMode _entryMode = _RestorableDatePickerEntryMode(widget.initialEntryMode); - late final RestorableDateTimeN _selectedStart = RestorableDateTimeN(widget.initialDateRange?.start); - late final RestorableDateTimeN _selectedEnd = RestorableDateTimeN(widget.initialDateRange?.end); - final RestorableBool _autoValidate = RestorableBool(false); - final GlobalKey _calendarPickerKey = GlobalKey(); - final GlobalKey<_InputDateRangePickerState> _inputPickerKey = GlobalKey<_InputDateRangePickerState>(); - - @override - String? get restorationId => widget.restorationId; - - @override - void restoreState(RestorationBucket? oldBucket, bool initialRestore) { - registerForRestoration(_entryMode, 'entry_mode'); - registerForRestoration(_selectedStart, 'selected_start'); - registerForRestoration(_selectedEnd, 'selected_end'); - registerForRestoration(_autoValidate, 'autovalidate'); - } - - void _handleOk() { - if (_entryMode.value == DatePickerEntryMode.input || _entryMode.value == DatePickerEntryMode.inputOnly) { - final _InputDateRangePickerState picker = _inputPickerKey.currentState!; - if (!picker.validate()) { - setState(() { - _autoValidate.value = true; - }); - return; - } - } - final DateTimeRange? selectedRange = _hasSelectedDateRange - ? DateTimeRange(start: _selectedStart.value!, end: _selectedEnd.value!) - : null; - - Navigator.pop(context, selectedRange); - } - - void _handleCancel() { - Navigator.pop(context); - } - - void _handleEntryModeToggle() { - setState(() { - switch (_entryMode.value) { - case DatePickerEntryMode.calendar: - _autoValidate.value = false; - _entryMode.value = DatePickerEntryMode.input; - break; - - case DatePickerEntryMode.input: - // Validate the range dates - if (_selectedStart.value != null && - (_selectedStart.value!.isBefore(widget.firstDate) || _selectedStart.value!.isAfter(widget.lastDate))) { - _selectedStart.value = null; - // With no valid start date, having an end date makes no sense for the UI. - _selectedEnd.value = null; - } - if (_selectedEnd.value != null && - (_selectedEnd.value!.isBefore(widget.firstDate) || _selectedEnd.value!.isAfter(widget.lastDate))) { - _selectedEnd.value = null; - } - // If invalid range (start after end), then just use the start date - if (_selectedStart.value != null && _selectedEnd.value != null && _selectedStart.value!.isAfter(_selectedEnd.value!)) { - _selectedEnd.value = null; - } - _entryMode.value = DatePickerEntryMode.calendar; - break; - - case DatePickerEntryMode.calendarOnly: - case DatePickerEntryMode.inputOnly: - assert(false, 'Can not change entry mode from $_entryMode'); - break; - } - }); - } - - void _handleStartDateChanged(DateTime? date) { - setState(() => _selectedStart.value = date); - } - - void _handleEndDateChanged(DateTime? date) { - setState(() => _selectedEnd.value = date); - } - - bool get _hasSelectedDateRange => _selectedStart.value != null && _selectedEnd.value != null; - - @override - Widget build(BuildContext context) { - final MediaQueryData mediaQuery = MediaQuery.of(context); - final Orientation orientation = mediaQuery.orientation; - final double textScaleFactor = math.min(mediaQuery.textScaleFactor, 1.3); - final MaterialLocalizations localizations = MaterialLocalizations.of(context); - final ColorScheme colors = Theme.of(context).colorScheme; - final Color onPrimarySurface = colors.brightness == Brightness.light - ? colors.onPrimary - : colors.onSurface; - - final Widget contents; - final Size size; - ShapeBorder? shape; - final double elevation; - final EdgeInsets insetPadding; - final bool showEntryModeButton = - _entryMode.value == DatePickerEntryMode.calendar || - _entryMode.value == DatePickerEntryMode.input; - switch (_entryMode.value) { - case DatePickerEntryMode.calendar: - case DatePickerEntryMode.calendarOnly: - contents = _CalendarRangePickerDialog( - key: _calendarPickerKey, - selectedStartDate: _selectedStart.value, - selectedEndDate: _selectedEnd.value, - firstDate: widget.firstDate, - lastDate: widget.lastDate, - currentDate: widget.currentDate, - onStartDateChanged: _handleStartDateChanged, - onEndDateChanged: _handleEndDateChanged, - onConfirm: _hasSelectedDateRange ? _handleOk : null, - onCancel: _handleCancel, - entryModeButton: showEntryModeButton - ? IconButton( - icon: const Icon(Icons.edit), - padding: EdgeInsets.zero, - color: onPrimarySurface, - tooltip: localizations.inputDateModeButtonLabel, - onPressed: _handleEntryModeToggle, - ) - : null, - confirmText: widget.saveText ?? localizations.saveButtonLabel, - helpText: widget.helpText ?? localizations.dateRangePickerHelpText, - ); - size = mediaQuery.size; - insetPadding = EdgeInsets.zero; - shape = const RoundedRectangleBorder(); - elevation = 0; - break; - - case DatePickerEntryMode.input: - case DatePickerEntryMode.inputOnly: - contents = _InputDateRangePickerDialog( - selectedStartDate: _selectedStart.value, - selectedEndDate: _selectedEnd.value, - currentDate: widget.currentDate, - picker: Container( - padding: const EdgeInsets.symmetric(horizontal: 24), - height: orientation == Orientation.portrait - ? _inputFormPortraitHeight - : _inputFormLandscapeHeight, - child: Column( - children: [ - const Spacer(), - _InputDateRangePicker( - key: _inputPickerKey, - initialStartDate: _selectedStart.value, - initialEndDate: _selectedEnd.value, - firstDate: widget.firstDate, - lastDate: widget.lastDate, - onStartDateChanged: _handleStartDateChanged, - onEndDateChanged: _handleEndDateChanged, - autofocus: true, - autovalidate: _autoValidate.value, - helpText: widget.helpText, - errorInvalidRangeText: widget.errorInvalidRangeText, - errorFormatText: widget.errorFormatText, - errorInvalidText: widget.errorInvalidText, - fieldStartHintText: widget.fieldStartHintText, - fieldEndHintText: widget.fieldEndHintText, - fieldStartLabelText: widget.fieldStartLabelText, - fieldEndLabelText: widget.fieldEndLabelText, - ), - const Spacer(), - ], - ), - ), - onConfirm: _handleOk, - onCancel: _handleCancel, - entryModeButton: showEntryModeButton - ? IconButton( - icon: const Icon(Icons.calendar_today), - padding: EdgeInsets.zero, - color: onPrimarySurface, - tooltip: localizations.calendarModeButtonLabel, - onPressed: _handleEntryModeToggle, - ) - : null, - confirmText: widget.confirmText ?? localizations.okButtonLabel, - cancelText: widget.cancelText ?? localizations.cancelButtonLabel, - helpText: widget.helpText ?? localizations.dateRangePickerHelpText, - ); - final DialogTheme dialogTheme = Theme.of(context).dialogTheme; - size = orientation == Orientation.portrait ? _inputPortraitDialogSize : _inputRangeLandscapeDialogSize; - insetPadding = const EdgeInsets.symmetric(horizontal: 16.0, vertical: 24.0); - shape = dialogTheme.shape; - elevation = dialogTheme.elevation ?? 24; - break; - } - - return Dialog( - insetPadding: insetPadding, - shape: shape, - elevation: elevation, - clipBehavior: Clip.antiAlias, - child: AnimatedContainer( - width: size.width, - height: size.height, - duration: _dialogSizeAnimationDuration, - curve: Curves.easeIn, - child: MediaQuery( - data: MediaQuery.of(context).copyWith( - textScaleFactor: textScaleFactor, - ), - child: Builder(builder: (BuildContext context) { - return contents; - }), - ), - ), - ); - } -} - -class _CalendarRangePickerDialog extends StatelessWidget { - const _CalendarRangePickerDialog({ - super.key, - required this.selectedStartDate, - required this.selectedEndDate, - required this.firstDate, - required this.lastDate, - required this.currentDate, - required this.onStartDateChanged, - required this.onEndDateChanged, - required this.onConfirm, - required this.onCancel, - required this.confirmText, - required this.helpText, - this.entryModeButton, - }); - - final DateTime? selectedStartDate; - final DateTime? selectedEndDate; - final DateTime firstDate; - final DateTime lastDate; - final DateTime? currentDate; - final ValueChanged onStartDateChanged; - final ValueChanged onEndDateChanged; - final VoidCallback? onConfirm; - final VoidCallback? onCancel; - final String confirmText; - final String helpText; - final Widget? entryModeButton; - - @override - Widget build(BuildContext context) { - final ThemeData theme = Theme.of(context); - final ColorScheme colorScheme = theme.colorScheme; - final MaterialLocalizations localizations = MaterialLocalizations.of(context); - final Orientation orientation = MediaQuery.of(context).orientation; - final TextTheme textTheme = theme.textTheme; - final Color headerForeground = colorScheme.brightness == Brightness.light - ? colorScheme.onPrimary - : colorScheme.onSurface; - final Color headerDisabledForeground = headerForeground.withOpacity(0.38); - final String startDateText = _formatRangeStartDate(localizations, selectedStartDate, selectedEndDate); - final String endDateText = _formatRangeEndDate(localizations, selectedStartDate, selectedEndDate, DateTime.now()); - final TextStyle? headlineStyle = textTheme.headline5; - final TextStyle? startDateStyle = headlineStyle?.apply( - color: selectedStartDate != null ? headerForeground : headerDisabledForeground, - ); - final TextStyle? endDateStyle = headlineStyle?.apply( - color: selectedEndDate != null ? headerForeground : headerDisabledForeground, - ); - final TextStyle saveButtonStyle = textTheme.button!.apply( - color: onConfirm != null ? headerForeground : headerDisabledForeground, - ); - - return SafeArea( - top: false, - left: false, - right: false, - child: Scaffold( - appBar: AppBar( - leading: CloseButton( - onPressed: onCancel, - ), - actions: [ - if (orientation == Orientation.landscape && entryModeButton != null) - entryModeButton!, - TextButton( - onPressed: onConfirm, - child: Text(confirmText, style: saveButtonStyle), - ), - const SizedBox(width: 8), - ], - bottom: PreferredSize( - preferredSize: const Size(double.infinity, 64), - child: Row(children: [ - SizedBox(width: MediaQuery.of(context).size.width < 360 ? 42 : 72), - Expanded( - child: Semantics( - label: '$helpText $startDateText to $endDateText', - excludeSemantics: true, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - helpText, - style: textTheme.overline!.apply( - color: headerForeground, - ), - ), - const SizedBox(height: 8), - Row( - children: [ - Text( - startDateText, - style: startDateStyle, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - Text(' – ', style: startDateStyle, - ), - Flexible( - child: Text( - endDateText, - style: endDateStyle, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), - ], - ), - const SizedBox(height: 16), - ], - ), - ), - ), - if (orientation == Orientation.portrait && entryModeButton != null) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: entryModeButton, - ), - ]), - ), - ), - body: _CalendarDateRangePicker( - initialStartDate: selectedStartDate, - initialEndDate: selectedEndDate, - firstDate: firstDate, - lastDate: lastDate, - currentDate: currentDate, - onStartDateChanged: onStartDateChanged, - onEndDateChanged: onEndDateChanged, - ), - ), - ); - } -} - -const Duration _monthScrollDuration = Duration(milliseconds: 200); - -const double _monthItemHeaderHeight = 58.0; -const double _monthItemFooterHeight = 12.0; -const double _monthItemRowHeight = 42.0; -const double _monthItemSpaceBetweenRows = 8.0; -const double _horizontalPadding = 8.0; -const double _maxCalendarWidthLandscape = 384.0; -const double _maxCalendarWidthPortrait = 480.0; - -/// Displays a scrollable calendar grid that allows a user to select a range -/// of dates. -class _CalendarDateRangePicker extends StatefulWidget { - /// Creates a scrollable calendar grid for picking date ranges. - _CalendarDateRangePicker({ - DateTime? initialStartDate, - DateTime? initialEndDate, - required DateTime firstDate, - required DateTime lastDate, - DateTime? currentDate, - required this.onStartDateChanged, - required this.onEndDateChanged, - }) : initialStartDate = initialStartDate != null ? DateUtils.dateOnly(initialStartDate) : null, - initialEndDate = initialEndDate != null ? DateUtils.dateOnly(initialEndDate) : null, - assert(firstDate != null), - assert(lastDate != null), - firstDate = DateUtils.dateOnly(firstDate), - lastDate = DateUtils.dateOnly(lastDate), - currentDate = DateUtils.dateOnly(currentDate ?? DateTime.now()) { - assert( - this.initialStartDate == null || this.initialEndDate == null || !this.initialStartDate!.isAfter(initialEndDate!), - 'initialStartDate must be on or before initialEndDate.', - ); - assert( - !this.lastDate.isBefore(this.firstDate), - 'firstDate must be on or before lastDate.', - ); - } - - /// The [DateTime] that represents the start of the initial date range selection. - final DateTime? initialStartDate; - - /// The [DateTime] that represents the end of the initial date range selection. - final DateTime? initialEndDate; - - /// The earliest allowable [DateTime] that the user can select. - final DateTime firstDate; - - /// The latest allowable [DateTime] that the user can select. - final DateTime lastDate; - - /// The [DateTime] representing today. It will be highlighted in the day grid. - final DateTime currentDate; - - /// Called when the user changes the start date of the selected range. - final ValueChanged? onStartDateChanged; - - /// Called when the user changes the end date of the selected range. - final ValueChanged? onEndDateChanged; - - @override - _CalendarDateRangePickerState createState() => _CalendarDateRangePickerState(); -} - -class _CalendarDateRangePickerState extends State<_CalendarDateRangePicker> { - final GlobalKey _scrollViewKey = GlobalKey(); - DateTime? _startDate; - DateTime? _endDate; - int _initialMonthIndex = 0; - late ScrollController _controller; - late bool _showWeekBottomDivider; - - @override - void initState() { - super.initState(); - _controller = ScrollController(); - _controller.addListener(_scrollListener); - - _startDate = widget.initialStartDate; - _endDate = widget.initialEndDate; - - // Calculate the index for the initially displayed month. This is needed to - // divide the list of months into two `SliverList`s. - final DateTime initialDate = widget.initialStartDate ?? widget.currentDate; - if (!initialDate.isBefore(widget.firstDate) && - !initialDate.isAfter(widget.lastDate)) { - _initialMonthIndex = DateUtils.monthDelta(widget.firstDate, initialDate); - } - - _showWeekBottomDivider = _initialMonthIndex != 0; - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - void _scrollListener() { - if (_controller.offset <= _controller.position.minScrollExtent) { - setState(() { - _showWeekBottomDivider = false; - }); - } else if (!_showWeekBottomDivider) { - setState(() { - _showWeekBottomDivider = true; - }); - } - } - - int get _numberOfMonths => DateUtils.monthDelta(widget.firstDate, widget.lastDate) + 1; - - void _vibrate() { - switch (Theme.of(context).platform) { - case TargetPlatform.android: - case TargetPlatform.fuchsia: - HapticFeedback.vibrate(); - break; - case TargetPlatform.iOS: - case TargetPlatform.linux: - case TargetPlatform.macOS: - case TargetPlatform.windows: - break; - } - } - - // This updates the selected date range using this logic: - // - // * From the unselected state, selecting one date creates the start date. - // * If the next selection is before the start date, reset date range and - // set the start date to that selection. - // * If the next selection is on or after the start date, set the end date - // to that selection. - // * After both start and end dates are selected, any subsequent selection - // resets the date range and sets start date to that selection. - void _updateSelection(DateTime date) { - _vibrate(); - setState(() { - if (_startDate != null && _endDate == null && !date.isBefore(_startDate!)) { - _endDate = date; - widget.onEndDateChanged?.call(_endDate); - } else { - _startDate = date; - widget.onStartDateChanged?.call(_startDate!); - if (_endDate != null) { - _endDate = null; - widget.onEndDateChanged?.call(_endDate); - } - } - }); - } - - Widget _buildMonthItem(BuildContext context, int index, bool beforeInitialMonth) { - final int monthIndex = beforeInitialMonth - ? _initialMonthIndex - index - 1 - : _initialMonthIndex + index; - final DateTime month = DateUtils.addMonthsToMonthDate(widget.firstDate, monthIndex); - return Stack( - alignment: Alignment.center, - children: [ - Text("${month.month}",style: TextStyle(fontSize: 200,color: Colors.grey.withOpacity(0.1)),), - _MonthItem( - selectedDateStart: _startDate, - selectedDateEnd: _endDate, - currentDate: widget.currentDate, - firstDate: widget.firstDate, - lastDate: widget.lastDate, - displayedMonth: month, - onChanged: _updateSelection, - ), - ], - ); - } - - @override - Widget build(BuildContext context) { - const Key sliverAfterKey = Key('sliverAfterKey'); - - return Column( - children: [ - const _DayHeaders(), - if (_showWeekBottomDivider) const Divider(height: 0), - Expanded( - child: _CalendarKeyboardNavigator( - firstDate: widget.firstDate, - lastDate: widget.lastDate, - initialFocusedDay: _startDate ?? widget.initialStartDate ?? widget.currentDate, - // In order to prevent performance issues when displaying the - // correct initial month, 2 `SliverList`s are used to split the - // months. The first item in the second SliverList is the initial - // month to be displayed. - child: CustomScrollView( - key: _scrollViewKey, - controller: _controller, - center: sliverAfterKey, - slivers: [ - SliverList( - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) => _buildMonthItem(context, index, true), - childCount: _initialMonthIndex, - ), - ), - SliverList( - key: sliverAfterKey, - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) => _buildMonthItem(context, index, false), - childCount: _numberOfMonths - _initialMonthIndex, - ), - ), - ], - ), - ), - ), - ], - ); - } -} - -class _CalendarKeyboardNavigator extends StatefulWidget { - const _CalendarKeyboardNavigator({ - required this.child, - required this.firstDate, - required this.lastDate, - required this.initialFocusedDay, - }); - - final Widget child; - final DateTime firstDate; - final DateTime lastDate; - final DateTime initialFocusedDay; - - @override - _CalendarKeyboardNavigatorState createState() => _CalendarKeyboardNavigatorState(); -} - -class _CalendarKeyboardNavigatorState extends State<_CalendarKeyboardNavigator> { - - final Map _shortcutMap = const { - SingleActivator(LogicalKeyboardKey.arrowLeft): DirectionalFocusIntent(TraversalDirection.left), - SingleActivator(LogicalKeyboardKey.arrowRight): DirectionalFocusIntent(TraversalDirection.right), - SingleActivator(LogicalKeyboardKey.arrowDown): DirectionalFocusIntent(TraversalDirection.down), - SingleActivator(LogicalKeyboardKey.arrowUp): DirectionalFocusIntent(TraversalDirection.up), - }; - late Map> _actionMap; - late FocusNode _dayGridFocus; - TraversalDirection? _dayTraversalDirection; - DateTime? _focusedDay; - - @override - void initState() { - super.initState(); - - _actionMap = >{ - NextFocusIntent: CallbackAction(onInvoke: _handleGridNextFocus), - PreviousFocusIntent: CallbackAction(onInvoke: _handleGridPreviousFocus), - DirectionalFocusIntent: CallbackAction(onInvoke: _handleDirectionFocus), - }; - _dayGridFocus = FocusNode(debugLabel: 'Day Grid'); - } - - @override - void dispose() { - _dayGridFocus.dispose(); - super.dispose(); - } - - void _handleGridFocusChange(bool focused) { - setState(() { - if (focused) { - _focusedDay ??= widget.initialFocusedDay; - } - }); - } - - /// Move focus to the next element after the day grid. - void _handleGridNextFocus(NextFocusIntent intent) { - _dayGridFocus.requestFocus(); - _dayGridFocus.nextFocus(); - } - - /// Move focus to the previous element before the day grid. - void _handleGridPreviousFocus(PreviousFocusIntent intent) { - _dayGridFocus.requestFocus(); - _dayGridFocus.previousFocus(); - } - - /// Move the internal focus date in the direction of the given intent. - /// - /// This will attempt to move the focused day to the next selectable day in - /// the given direction. If the new date is not in the current month, then - /// the page view will be scrolled to show the new date's month. - /// - /// For horizontal directions, it will move forward or backward a day (depending - /// on the current [TextDirection]). For vertical directions it will move up and - /// down a week at a time. - void _handleDirectionFocus(DirectionalFocusIntent intent) { - assert(_focusedDay != null); - setState(() { - final DateTime? nextDate = _nextDateInDirection(_focusedDay!, intent.direction); - if (nextDate != null) { - _focusedDay = nextDate; - _dayTraversalDirection = intent.direction; - } - }); - } - - static const Map _directionOffset = { - TraversalDirection.up: -DateTime.daysPerWeek, - TraversalDirection.right: 1, - TraversalDirection.down: DateTime.daysPerWeek, - TraversalDirection.left: -1, - }; - - int _dayDirectionOffset(TraversalDirection traversalDirection, TextDirection textDirection) { - // Swap left and right if the text direction if RTL - if (textDirection == TextDirection.rtl) { - if (traversalDirection == TraversalDirection.left) { - traversalDirection = TraversalDirection.right; - } else if (traversalDirection == TraversalDirection.right) { - traversalDirection = TraversalDirection.left; - } - } - return _directionOffset[traversalDirection]!; - } - - DateTime? _nextDateInDirection(DateTime date, TraversalDirection direction) { - final TextDirection textDirection = Directionality.of(context); - final DateTime nextDate = DateUtils.addDaysToDate(date, _dayDirectionOffset(direction, textDirection)); - if (!nextDate.isBefore(widget.firstDate) && !nextDate.isAfter(widget.lastDate)) { - return nextDate; - } - return null; - } - - @override - Widget build(BuildContext context) { - return FocusableActionDetector( - shortcuts: _shortcutMap, - actions: _actionMap, - focusNode: _dayGridFocus, - onFocusChange: _handleGridFocusChange, - child: _FocusedDate( - date: _dayGridFocus.hasFocus ? _focusedDay : null, - scrollDirection: _dayGridFocus.hasFocus ? _dayTraversalDirection : null, - child: widget.child, - ), - ); - } -} - -/// InheritedWidget indicating what the current focused date is for its children. -/// -/// This is used by the [_MonthPicker] to let its children [_DayPicker]s know -/// what the currently focused date (if any) should be. -class _FocusedDate extends InheritedWidget { - const _FocusedDate({ - required super.child, - this.date, - this.scrollDirection, - }); - - final DateTime? date; - final TraversalDirection? scrollDirection; - - @override - bool updateShouldNotify(_FocusedDate oldWidget) { - return !DateUtils.isSameDay(date, oldWidget.date) || scrollDirection != oldWidget.scrollDirection; - } - - static _FocusedDate? of(BuildContext context) { - return context.dependOnInheritedWidgetOfExactType<_FocusedDate>(); - } -} - - -class _DayHeaders extends StatelessWidget { - const _DayHeaders(); - - /// Builds widgets showing abbreviated days of week. The first widget in the - /// returned list corresponds to the first day of week for the current locale. - /// - /// Examples: - /// - /// ``` - /// ┌ Sunday is the first day of week in the US (en_US) - /// | - /// S M T W T F S <-- the returned list contains these widgets - /// _ _ _ _ _ 1 2 - /// 3 4 5 6 7 8 9 - /// - /// ┌ But it's Monday in the UK (en_GB) - /// | - /// M T W T F S S <-- the returned list contains these widgets - /// _ _ _ _ 1 2 3 - /// 4 5 6 7 8 9 10 - /// ``` - List _getDayHeaders(TextStyle headerStyle, MaterialLocalizations localizations) { - final List result = []; - for (int i = localizations.firstDayOfWeekIndex; true; i = (i + 1) % 7) { - final String weekday = localizations.narrowWeekdays[i]; - result.add(ExcludeSemantics( - child: Center(child: Text(weekday, style: headerStyle)), - )); - if (i == (localizations.firstDayOfWeekIndex - 1) % 7) { - break; - } - } - return result; - } - - @override - Widget build(BuildContext context) { - final ThemeData themeData = Theme.of(context); - final ColorScheme colorScheme = themeData.colorScheme; - final TextStyle textStyle = themeData.textTheme.subtitle2!.apply(color: colorScheme.onSurface); - final MaterialLocalizations localizations = MaterialLocalizations.of(context); - final List labels = _getDayHeaders(textStyle, localizations); - - // Add leading and trailing containers for edges of the custom grid layout. - labels.insert(0, Container()); - labels.add(Container()); - - return Container( - constraints: BoxConstraints( - maxWidth: MediaQuery.of(context).orientation == Orientation.landscape - ? _maxCalendarWidthLandscape - : _maxCalendarWidthPortrait, - maxHeight: _monthItemRowHeight, - ), - child: GridView.custom( - shrinkWrap: true, - gridDelegate: _monthItemGridDelegate, - childrenDelegate: SliverChildListDelegate( - labels, - addRepaintBoundaries: false, - ), - ), - ); - } -} - -class _MonthItemGridDelegate extends SliverGridDelegate { - const _MonthItemGridDelegate(); - - @override - SliverGridLayout getLayout(SliverConstraints constraints) { - final double tileWidth = (constraints.crossAxisExtent - 2 * _horizontalPadding) / DateTime.daysPerWeek; - return _MonthSliverGridLayout( - crossAxisCount: DateTime.daysPerWeek + 2, - dayChildWidth: tileWidth, - edgeChildWidth: _horizontalPadding, - reverseCrossAxis: axisDirectionIsReversed(constraints.crossAxisDirection), - ); - } - - @override - bool shouldRelayout(_MonthItemGridDelegate oldDelegate) => false; -} - -const _MonthItemGridDelegate _monthItemGridDelegate = _MonthItemGridDelegate(); - -class _MonthSliverGridLayout extends SliverGridLayout { - /// Creates a layout that uses equally sized and spaced tiles for each day of - /// the week and an additional edge tile for padding at the start and end of - /// each row. - /// - /// This is necessary to facilitate the painting of the range highlight - /// correctly. - const _MonthSliverGridLayout({ - required this.crossAxisCount, - required this.dayChildWidth, - required this.edgeChildWidth, - required this.reverseCrossAxis, - }) : assert(crossAxisCount != null && crossAxisCount > 0), - assert(dayChildWidth != null && dayChildWidth >= 0), - assert(edgeChildWidth != null && edgeChildWidth >= 0), - assert(reverseCrossAxis != null); - - /// The number of children in the cross axis. - final int crossAxisCount; - - /// The width in logical pixels of the day child widgets. - final double dayChildWidth; - - /// The width in logical pixels of the edge child widgets. - final double edgeChildWidth; - - /// Whether the children should be placed in the opposite order of increasing - /// coordinates in the cross axis. - /// - /// For example, if the cross axis is horizontal, the children are placed from - /// left to right when [reverseCrossAxis] is false and from right to left when - /// [reverseCrossAxis] is true. - /// - /// Typically set to the return value of [axisDirectionIsReversed] applied to - /// the [SliverConstraints.crossAxisDirection]. - final bool reverseCrossAxis; - - /// The number of logical pixels from the leading edge of one row to the - /// leading edge of the next row. - double get _rowHeight { - return _monthItemRowHeight + _monthItemSpaceBetweenRows; - } - - /// The height in logical pixels of the children widgets. - double get _childHeight { - return _monthItemRowHeight; - } - - @override - int getMinChildIndexForScrollOffset(double scrollOffset) { - return crossAxisCount * (scrollOffset ~/ _rowHeight); - } - - @override - int getMaxChildIndexForScrollOffset(double scrollOffset) { - final int mainAxisCount = (scrollOffset / _rowHeight).ceil(); - return math.max(0, crossAxisCount * mainAxisCount - 1); - } - - double _getCrossAxisOffset(double crossAxisStart, bool isPadding) { - if (reverseCrossAxis) { - return - ((crossAxisCount - 2) * dayChildWidth + 2 * edgeChildWidth) - - crossAxisStart - - (isPadding ? edgeChildWidth : dayChildWidth); - } - return crossAxisStart; - } - - @override - SliverGridGeometry getGeometryForChildIndex(int index) { - final int adjustedIndex = index % crossAxisCount; - final bool isEdge = adjustedIndex == 0 || adjustedIndex == crossAxisCount - 1; - final double crossAxisStart = math.max(0, (adjustedIndex - 1) * dayChildWidth + edgeChildWidth); - - return SliverGridGeometry( - scrollOffset: (index ~/ crossAxisCount) * _rowHeight, - crossAxisOffset: _getCrossAxisOffset(crossAxisStart, isEdge), - mainAxisExtent: _childHeight, - crossAxisExtent: isEdge ? edgeChildWidth : dayChildWidth, - ); - } - - @override - double computeMaxScrollOffset(int childCount) { - assert(childCount >= 0); - final int mainAxisCount = ((childCount - 1) ~/ crossAxisCount) + 1; - final double mainAxisSpacing = _rowHeight - _childHeight; - return _rowHeight * mainAxisCount - mainAxisSpacing; - } -} - -/// Displays the days of a given month and allows choosing a date range. -/// -/// The days are arranged in a rectangular grid with one column for each day of -/// the week. -class _MonthItem extends StatefulWidget { - /// Creates a month item. - _MonthItem({ - required this.selectedDateStart, - required this.selectedDateEnd, - required this.currentDate, - required this.onChanged, - required this.firstDate, - required this.lastDate, - required this.displayedMonth, - this.dragStartBehavior = DragStartBehavior.start, - }) : assert(firstDate != null), - assert(lastDate != null), - assert(!firstDate.isAfter(lastDate)), - assert(selectedDateStart == null || !selectedDateStart.isBefore(firstDate)), - assert(selectedDateEnd == null || !selectedDateEnd.isBefore(firstDate)), - assert(selectedDateStart == null || !selectedDateStart.isAfter(lastDate)), - assert(selectedDateEnd == null || !selectedDateEnd.isAfter(lastDate)), - assert(selectedDateStart == null || selectedDateEnd == null || !selectedDateStart.isAfter(selectedDateEnd)), - assert(currentDate != null), - assert(onChanged != null), - assert(displayedMonth != null), - assert(dragStartBehavior != null); - - /// The currently selected start date. - /// - /// This date is highlighted in the picker. - final DateTime? selectedDateStart; - - /// The currently selected end date. - /// - /// This date is highlighted in the picker. - final DateTime? selectedDateEnd; - - /// The current date at the time the picker is displayed. - final DateTime currentDate; - - /// Called when the user picks a day. - final ValueChanged onChanged; - - /// The earliest date the user is permitted to pick. - final DateTime firstDate; - - /// The latest date the user is permitted to pick. - final DateTime lastDate; - - /// The month whose days are displayed by this picker. - final DateTime displayedMonth; - - /// Determines the way that drag start behavior is handled. - /// - /// If set to [DragStartBehavior.start], the drag gesture used to scroll a - /// date picker wheel will begin at the position where the drag gesture won - /// the arena. If set to [DragStartBehavior.down] it will begin at the position - /// where a down event is first detected. - /// - /// In general, setting this to [DragStartBehavior.start] will make drag - /// animation smoother and setting it to [DragStartBehavior.down] will make - /// drag behavior feel slightly more reactive. - /// - /// By default, the drag start behavior is [DragStartBehavior.start]. - /// - /// See also: - /// - /// * [DragGestureRecognizer.dragStartBehavior], which gives an example for - /// the different behaviors. - final DragStartBehavior dragStartBehavior; - - @override - _MonthItemState createState() => _MonthItemState(); -} - -class _MonthItemState extends State<_MonthItem> { - /// List of [FocusNode]s, one for each day of the month. - late List _dayFocusNodes; - - @override - void initState() { - super.initState(); - final int daysInMonth = DateUtils.getDaysInMonth(widget.displayedMonth.year, widget.displayedMonth.month); - _dayFocusNodes = List.generate( - daysInMonth, - (int index) => FocusNode(skipTraversal: true, debugLabel: 'Day ${index + 1}'), - ); - } - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - // Check to see if the focused date is in this month, if so focus it. - final DateTime? focusedDate = _FocusedDate.of(context)?.date; - if (focusedDate != null && DateUtils.isSameMonth(widget.displayedMonth, focusedDate)) { - _dayFocusNodes[focusedDate.day - 1].requestFocus(); - } - } - - @override - void dispose() { - for (final FocusNode node in _dayFocusNodes) { - node.dispose(); - } - super.dispose(); - } - - Color _highlightColor(BuildContext context) { - return Theme.of(context).colorScheme.primary.withOpacity(0.12); - } - - void _dayFocusChanged(bool focused) { - if (focused) { - final TraversalDirection? focusDirection = _FocusedDate.of(context)?.scrollDirection; - if (focusDirection != null) { - ScrollPositionAlignmentPolicy policy = ScrollPositionAlignmentPolicy.explicit; - switch (focusDirection) { - case TraversalDirection.up: - case TraversalDirection.left: - policy = ScrollPositionAlignmentPolicy.keepVisibleAtStart; - break; - case TraversalDirection.right: - case TraversalDirection.down: - policy = ScrollPositionAlignmentPolicy.keepVisibleAtEnd; - break; - } - Scrollable.ensureVisible(primaryFocus!.context!, - duration: _monthScrollDuration, - alignmentPolicy: policy, - ); - } - } - } - - Widget _buildDayItem(BuildContext context, DateTime dayToBuild, int firstDayOffset, int daysInMonth) { - final ThemeData theme = Theme.of(context); - final ColorScheme colorScheme = theme.colorScheme; - final TextTheme textTheme = theme.textTheme; - final MaterialLocalizations localizations = MaterialLocalizations.of(context); - final TextDirection textDirection = Directionality.of(context); - final Color highlightColor = _highlightColor(context); - final int day = dayToBuild.day; - - final bool isDisabled = dayToBuild.isAfter(widget.lastDate) || dayToBuild.isBefore(widget.firstDate); - - BoxDecoration? decoration; - TextStyle? itemStyle = textTheme.bodyText2; - - final bool isRangeSelected = widget.selectedDateStart != null && widget.selectedDateEnd != null; - final bool isSelectedDayStart = widget.selectedDateStart != null && dayToBuild.isAtSameMomentAs(widget.selectedDateStart!); - final bool isSelectedDayEnd = widget.selectedDateEnd != null && dayToBuild.isAtSameMomentAs(widget.selectedDateEnd!); - final bool isInRange = isRangeSelected && - dayToBuild.isAfter(widget.selectedDateStart!) && - dayToBuild.isBefore(widget.selectedDateEnd!); - - _HighlightPainter? highlightPainter; - - if (isSelectedDayStart || isSelectedDayEnd) { - // The selected start and end dates gets a circle background - // highlight, and a contrasting text color. - itemStyle = textTheme.bodyText2?.apply(color: colorScheme.onPrimary); - decoration = BoxDecoration( - color: colorScheme.primary, - shape: BoxShape.circle, - ); - - if (isRangeSelected && widget.selectedDateStart != widget.selectedDateEnd) { - final _HighlightPainterStyle style = isSelectedDayStart - ? _HighlightPainterStyle.highlightTrailing - : _HighlightPainterStyle.highlightLeading; - highlightPainter = _HighlightPainter( - color: highlightColor, - style: style, - textDirection: textDirection, - ); - } - } else if (isInRange) { - // The days within the range get a light background highlight. - highlightPainter = _HighlightPainter( - color: highlightColor, - style: _HighlightPainterStyle.highlightAll, - textDirection: textDirection, - ); - } else if (isDisabled) { - itemStyle = textTheme.bodyText2?.apply(color: colorScheme.onSurface.withOpacity(0.38)); - } else if (DateUtils.isSameDay(widget.currentDate, dayToBuild)) { - // The current day gets a different text color and a circle stroke - // border. - itemStyle = textTheme.bodyText2?.apply(color: colorScheme.primary); - decoration = BoxDecoration( - border: Border.all(color: colorScheme.primary), - shape: BoxShape.circle, - ); - } - - // We want the day of month to be spoken first irrespective of the - // locale-specific preferences or TextDirection. This is because - // an accessibility user is more likely to be interested in the - // day of month before the rest of the date, as they are looking - // for the day of month. To do that we prepend day of month to the - // formatted full date. - String semanticLabel = '${localizations.formatDecimal(day)}, ${localizations.formatFullDate(dayToBuild)}'; - if (isSelectedDayStart) { - semanticLabel = localizations.dateRangeStartDateSemanticLabel(semanticLabel); - } else if (isSelectedDayEnd) { - semanticLabel = localizations.dateRangeEndDateSemanticLabel(semanticLabel); - } - - Widget dayWidget = Container( - decoration: decoration, - child: Center( - child: Semantics( - label: semanticLabel, - selected: isSelectedDayStart || isSelectedDayEnd, - child: ExcludeSemantics( - child: Text(localizations.formatDecimal(day), style: itemStyle), - ), - ), - ), - ); - - if (highlightPainter != null) { - dayWidget = CustomPaint( - painter: highlightPainter, - child: dayWidget, - ); - } - - if (!isDisabled) { - dayWidget = InkResponse( - focusNode: _dayFocusNodes[day - 1], - onTap: () => widget.onChanged(dayToBuild), - radius: _monthItemRowHeight / 2 + 4, - splashColor: colorScheme.primary.withOpacity(0.38), - onFocusChange: _dayFocusChanged, - child: dayWidget, - ); - } - - return dayWidget; - } - - Widget _buildEdgeContainer(BuildContext context, bool isHighlighted) { - return Container(color: isHighlighted ? _highlightColor(context) : null); - } - - @override - Widget build(BuildContext context) { - final ThemeData themeData = Theme.of(context); - final TextTheme textTheme = themeData.textTheme; - final MaterialLocalizations localizations = MaterialLocalizations.of(context); - final int year = widget.displayedMonth.year; - final int month = widget.displayedMonth.month; - final int daysInMonth = DateUtils.getDaysInMonth(year, month); - final int dayOffset = DateUtils.firstDayOffset(year, month, localizations); - final int weeks = ((daysInMonth + dayOffset) / DateTime.daysPerWeek).ceil(); - final double gridHeight = - weeks * _monthItemRowHeight + (weeks - 1) * _monthItemSpaceBetweenRows; - final List dayItems = []; - - for (int i = 0; true; i += 1) { - // 1-based day of month, e.g. 1-31 for January, and 1-29 for February on - // a leap year. - final int day = i - dayOffset + 1; - if (day > daysInMonth) { - break; - } - if (day < 1) { - dayItems.add(Container()); - } else { - final DateTime dayToBuild = DateTime(year, month, day); - final Widget dayItem = _buildDayItem( - context, - dayToBuild, - dayOffset, - daysInMonth, - ); - dayItems.add(dayItem); - } - } - - // Add the leading/trailing edge containers to each week in order to - // correctly extend the range highlight. - final List paddedDayItems = []; - for (int i = 0; i < weeks; i++) { - final int start = i * DateTime.daysPerWeek; - final int end = math.min( - start + DateTime.daysPerWeek, - dayItems.length, - ); - final List weekList = dayItems.sublist(start, end); - - final DateTime dateAfterLeadingPadding = DateTime(year, month, start - dayOffset + 1); - // Only color the edge container if it is after the start date and - // on/before the end date. - final bool isLeadingInRange = - !(dayOffset > 0 && i == 0) && - widget.selectedDateStart != null && - widget.selectedDateEnd != null && - dateAfterLeadingPadding.isAfter(widget.selectedDateStart!) && - !dateAfterLeadingPadding.isAfter(widget.selectedDateEnd!); - weekList.insert(0, _buildEdgeContainer(context, isLeadingInRange)); - - // Only add a trailing edge container if it is for a full week and not a - // partial week. - if (end < dayItems.length || (end == dayItems.length && dayItems.length % DateTime.daysPerWeek == 0)) { - final DateTime dateBeforeTrailingPadding = - DateTime(year, month, end - dayOffset); - // Only color the edge container if it is on/after the start date and - // before the end date. - final bool isTrailingInRange = - widget.selectedDateStart != null && - widget.selectedDateEnd != null && - !dateBeforeTrailingPadding.isBefore(widget.selectedDateStart!) && - dateBeforeTrailingPadding.isBefore(widget.selectedDateEnd!); - weekList.add(_buildEdgeContainer(context, isTrailingInRange)); - } - - paddedDayItems.addAll(weekList); - } - - final double maxWidth = MediaQuery.of(context).orientation == Orientation.landscape - ? _maxCalendarWidthLandscape - : _maxCalendarWidthPortrait; - return Column( - children: [ - Container( - constraints: BoxConstraints(maxWidth: maxWidth), - height: _monthItemHeaderHeight, - padding: const EdgeInsets.symmetric(horizontal: 16), - alignment: AlignmentDirectional.centerStart, - child: ExcludeSemantics( - child: Text( - localizations.formatMonthYear(widget.displayedMonth), - style: textTheme.bodyText2!.apply(color: themeData.colorScheme.onSurface), - ), - ), - ), - Container( - constraints: BoxConstraints( - maxWidth: maxWidth, - maxHeight: gridHeight, - ), - child: GridView.custom( - physics: const NeverScrollableScrollPhysics(), - gridDelegate: _monthItemGridDelegate, - childrenDelegate: SliverChildListDelegate( - paddedDayItems, - addRepaintBoundaries: false, - ), - ), - ), - const SizedBox(height: _monthItemFooterHeight), - ], - ); - } -} - -/// Determines which style to use to paint the highlight. -enum _HighlightPainterStyle { - /// Paints nothing. - none, - - /// Paints a rectangle that occupies the leading half of the space. - highlightLeading, - - /// Paints a rectangle that occupies the trailing half of the space. - highlightTrailing, - - /// Paints a rectangle that occupies all available space. - highlightAll, -} - -/// This custom painter will add a background highlight to its child. -/// -/// This highlight will be drawn depending on the [style], [color], and -/// [textDirection] supplied. It will either paint a rectangle on the -/// left/right, a full rectangle, or nothing at all. This logic is determined by -/// a combination of the [style] and [textDirection]. -class _HighlightPainter extends CustomPainter { - _HighlightPainter({ - required this.color, - this.style = _HighlightPainterStyle.none, - this.textDirection, - }); - - final Color color; - final _HighlightPainterStyle style; - final TextDirection? textDirection; - - @override - void paint(Canvas canvas, Size size) { - if (style == _HighlightPainterStyle.none) { - return; - } - - final Paint paint = Paint() - ..color = color - ..style = PaintingStyle.fill; - - final Rect rectLeft = Rect.fromLTWH(0, 0, size.width / 2, size.height); - final Rect rectRight = Rect.fromLTWH(size.width / 2, 0, size.width / 2, size.height); - - switch (style) { - case _HighlightPainterStyle.highlightTrailing: - canvas.drawRect( - textDirection == TextDirection.ltr ? rectRight : rectLeft, - paint, - ); - break; - case _HighlightPainterStyle.highlightLeading: - canvas.drawRect( - textDirection == TextDirection.ltr ? rectLeft : rectRight, - paint, - ); - break; - case _HighlightPainterStyle.highlightAll: - canvas.drawRect( - Rect.fromLTWH(0, 0, size.width, size.height), - paint, - ); - break; - case _HighlightPainterStyle.none: - break; - } - } - - @override - bool shouldRepaint(CustomPainter oldDelegate) => false; -} - -class _InputDateRangePickerDialog extends StatelessWidget { - const _InputDateRangePickerDialog({ - required this.selectedStartDate, - required this.selectedEndDate, - required this.currentDate, - required this.picker, - required this.onConfirm, - required this.onCancel, - required this.confirmText, - required this.cancelText, - required this.helpText, - required this.entryModeButton, - }); - - final DateTime? selectedStartDate; - final DateTime? selectedEndDate; - final DateTime? currentDate; - final Widget picker; - final VoidCallback onConfirm; - final VoidCallback onCancel; - final String? confirmText; - final String? cancelText; - final String? helpText; - final Widget? entryModeButton; - - String _formatDateRange(BuildContext context, DateTime? start, DateTime? end, DateTime now) { - final MaterialLocalizations localizations = MaterialLocalizations.of(context); - final String startText = _formatRangeStartDate(localizations, start, end); - final String endText = _formatRangeEndDate(localizations, start, end, now); - if (start == null || end == null) { - return localizations.unspecifiedDateRange; - } - if (Directionality.of(context) == TextDirection.ltr) { - return '$startText – $endText'; - } else { - return '$endText – $startText'; - } - } - - @override - Widget build(BuildContext context) { - final ThemeData theme = Theme.of(context); - final ColorScheme colorScheme = theme.colorScheme; - final MaterialLocalizations localizations = MaterialLocalizations.of(context); - final Orientation orientation = MediaQuery.of(context).orientation; - final TextTheme textTheme = theme.textTheme; - - final Color onPrimarySurfaceColor = colorScheme.brightness == Brightness.light - ? colorScheme.onPrimary - : colorScheme.onSurface; - final TextStyle? dateStyle = orientation == Orientation.landscape - ? textTheme.headline5?.apply(color: onPrimarySurfaceColor) - : textTheme.headline4?.apply(color: onPrimarySurfaceColor); - final String dateText = _formatDateRange(context, selectedStartDate, selectedEndDate, currentDate!); - final String semanticDateText = selectedStartDate != null && selectedEndDate != null - ? '${localizations.formatMediumDate(selectedStartDate!)} – ${localizations.formatMediumDate(selectedEndDate!)}' - : ''; - - final Widget header = _DatePickerHeader( - helpText: helpText ?? localizations.dateRangePickerHelpText, - titleText: dateText, - titleSemanticsLabel: semanticDateText, - titleStyle: dateStyle, - orientation: orientation, - isShort: orientation == Orientation.landscape, - entryModeButton: entryModeButton, - ); - - final Widget actions = Container( - alignment: AlignmentDirectional.centerEnd, - constraints: const BoxConstraints(minHeight: 52.0), - padding: const EdgeInsets.symmetric(horizontal: 8), - child: OverflowBar( - spacing: 8, - children: [ - TextButton( - onPressed: onCancel, - child: Text(cancelText ?? localizations.cancelButtonLabel), - ), - TextButton( - onPressed: onConfirm, - child: Text(confirmText ?? localizations.okButtonLabel), - ), - ], - ), - ); - - switch (orientation) { - case Orientation.portrait: - return Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - header, - Expanded(child: picker), - actions, - ], - ); - - case Orientation.landscape: - return Row( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - header, - Flexible( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Expanded(child: picker), - actions, - ], - ), - ), - ], - ); - } - } -} - -/// Provides a pair of text fields that allow the user to enter the start and -/// end dates that represent a range of dates. -class _InputDateRangePicker extends StatefulWidget { - /// Creates a row with two text fields configured to accept the start and end dates - /// of a date range. - _InputDateRangePicker({ - super.key, - DateTime? initialStartDate, - DateTime? initialEndDate, - required DateTime firstDate, - required DateTime lastDate, - required this.onStartDateChanged, - required this.onEndDateChanged, - this.helpText, - this.errorFormatText, - this.errorInvalidText, - this.errorInvalidRangeText, - this.fieldStartHintText, - this.fieldEndHintText, - this.fieldStartLabelText, - this.fieldEndLabelText, - this.autofocus = false, - this.autovalidate = false, - }) : initialStartDate = initialStartDate == null ? null : DateUtils.dateOnly(initialStartDate), - initialEndDate = initialEndDate == null ? null : DateUtils.dateOnly(initialEndDate), - assert(firstDate != null), - firstDate = DateUtils.dateOnly(firstDate), - assert(lastDate != null), - lastDate = DateUtils.dateOnly(lastDate), - assert(firstDate != null), - assert(lastDate != null), - assert(autofocus != null), - assert(autovalidate != null); - - /// The [DateTime] that represents the start of the initial date range selection. - final DateTime? initialStartDate; - - /// The [DateTime] that represents the end of the initial date range selection. - final DateTime? initialEndDate; - - /// The earliest allowable [DateTime] that the user can select. - final DateTime firstDate; - - /// The latest allowable [DateTime] that the user can select. - final DateTime lastDate; - - /// Called when the user changes the start date of the selected range. - final ValueChanged? onStartDateChanged; - - /// Called when the user changes the end date of the selected range. - final ValueChanged? onEndDateChanged; - - /// The text that is displayed at the top of the header. - /// - /// This is used to indicate to the user what they are selecting a date for. - final String? helpText; - - /// Error text used to indicate the text in a field is not a valid date. - final String? errorFormatText; - - /// Error text used to indicate the date in a field is not in the valid range - /// of [firstDate] - [lastDate]. - final String? errorInvalidText; - - /// Error text used to indicate the dates given don't form a valid date - /// range (i.e. the start date is after the end date). - final String? errorInvalidRangeText; - - /// Hint text shown when the start date field is empty. - final String? fieldStartHintText; - - /// Hint text shown when the end date field is empty. - final String? fieldEndHintText; - - /// Label used for the start date field. - final String? fieldStartLabelText; - - /// Label used for the end date field. - final String? fieldEndLabelText; - - /// {@macro flutter.widgets.editableText.autofocus} - final bool autofocus; - - /// If true, this the date fields will validate and update their error text - /// immediately after every change. Otherwise, you must call - /// [_InputDateRangePickerState.validate] to validate. - final bool autovalidate; - - @override - _InputDateRangePickerState createState() => _InputDateRangePickerState(); -} - -/// The current state of an [_InputDateRangePicker]. Can be used to -/// [validate] the date field entries. -class _InputDateRangePickerState extends State<_InputDateRangePicker> { - late String _startInputText; - late String _endInputText; - DateTime? _startDate; - DateTime? _endDate; - late TextEditingController _startController; - late TextEditingController _endController; - String? _startErrorText; - String? _endErrorText; - bool _autoSelected = false; - - @override - void initState() { - super.initState(); - _startDate = widget.initialStartDate; - _startController = TextEditingController(); - _endDate = widget.initialEndDate; - _endController = TextEditingController(); - } - - @override - void dispose() { - _startController.dispose(); - _endController.dispose(); - super.dispose(); - } - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - final MaterialLocalizations localizations = MaterialLocalizations.of(context); - if (_startDate != null) { - _startInputText = localizations.formatCompactDate(_startDate!); - final bool selectText = widget.autofocus && !_autoSelected; - _updateController(_startController, _startInputText, selectText); - _autoSelected = selectText; - } - - if (_endDate != null) { - _endInputText = localizations.formatCompactDate(_endDate!); - _updateController(_endController, _endInputText, false); - } - } - - /// Validates that the text in the start and end fields represent a valid - /// date range. - /// - /// Will return true if the range is valid. If not, it will - /// return false and display an appropriate error message under one of the - /// text fields. - bool validate() { - String? startError = _validateDate(_startDate); - final String? endError = _validateDate(_endDate); - if (startError == null && endError == null) { - if (_startDate!.isAfter(_endDate!)) { - startError = widget.errorInvalidRangeText ?? MaterialLocalizations.of(context).invalidDateRangeLabel; - } - } - setState(() { - _startErrorText = startError; - _endErrorText = endError; - }); - return startError == null && endError == null; - } - - DateTime? _parseDate(String? text) { - final MaterialLocalizations localizations = MaterialLocalizations.of(context); - return localizations.parseCompactDate(text); - } - - String? _validateDate(DateTime? date) { - if (date == null) { - return widget.errorFormatText ?? MaterialLocalizations.of(context).invalidDateFormatLabel; - } else if (date.isBefore(widget.firstDate) || date.isAfter(widget.lastDate)) { - return widget.errorInvalidText ?? MaterialLocalizations.of(context).dateOutOfRangeLabel; - } - return null; - } - - void _updateController(TextEditingController controller, String text, bool selectText) { - TextEditingValue textEditingValue = controller.value.copyWith(text: text); - if (selectText) { - textEditingValue = textEditingValue.copyWith(selection: TextSelection( - baseOffset: 0, - extentOffset: text.length, - )); - } - controller.value = textEditingValue; - } - - void _handleStartChanged(String text) { - setState(() { - _startInputText = text; - _startDate = _parseDate(text); - widget.onStartDateChanged?.call(_startDate); - }); - if (widget.autovalidate) { - validate(); - } - } - - void _handleEndChanged(String text) { - setState(() { - _endInputText = text; - _endDate = _parseDate(text); - widget.onEndDateChanged?.call(_endDate); - }); - if (widget.autovalidate) { - validate(); - } - } - - @override - Widget build(BuildContext context) { - final MaterialLocalizations localizations = MaterialLocalizations.of(context); - final InputDecorationTheme inputTheme = Theme.of(context).inputDecorationTheme; - return Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: TextField( - controller: _startController, - decoration: InputDecoration( - border: inputTheme.border ?? const UnderlineInputBorder(), - filled: inputTheme.filled, - hintText: widget.fieldStartHintText ?? localizations.dateHelpText, - labelText: widget.fieldStartLabelText ?? localizations.dateRangeStartLabel, - errorText: _startErrorText, - ), - keyboardType: TextInputType.datetime, - onChanged: _handleStartChanged, - autofocus: widget.autofocus, - ), - ), - const SizedBox(width: 8), - Expanded( - child: TextField( - controller: _endController, - decoration: InputDecoration( - border: inputTheme.border ?? const UnderlineInputBorder(), - filled: inputTheme.filled, - hintText: widget.fieldEndHintText ?? localizations.dateHelpText, - labelText: widget.fieldEndLabelText ?? localizations.dateRangeEndLabel, - errorText: _endErrorText, - ), - keyboardType: TextInputType.datetime, - onChanged: _handleEndChanged, - ), - ), - ], - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/DecoratedBoxTransition/node1_base.dart b/packages/widgets/lib/StatefulWidget/DecoratedBoxTransition/node1_base.dart deleted file mode 100644 index 9f5defefc..000000000 --- a/packages/widgets/lib/StatefulWidget/DecoratedBoxTransition/node1_base.dart +++ /dev/null @@ -1,77 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 113, -// "name": 'DecoratedBoxTransition基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】\n" -// "【position】 : 前/背景色 【DecorationPosition】\n" -// "【decoration】 : 动画 【Animation】", -// } -class CustomDecoratedBoxTransition extends StatefulWidget { - const CustomDecoratedBoxTransition({Key? key}) : super(key: key); - - @override - _CustomDecoratedBoxTransitionState createState() => - _CustomDecoratedBoxTransitionState(); -} - -class _CustomDecoratedBoxTransitionState - extends State - with SingleTickerProviderStateMixin { - late AnimationController _ctrl; - - @override - void initState() { - _ctrl = AnimationController( - vsync: this, - duration: const Duration(seconds: 1), - ); - _ctrl.forward(); - super.initState(); - } - - @override - void dispose() { - _ctrl.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: () => _ctrl.forward(from: 0), - child: SizedBox( - width: 200, - height: 100, - child: DecoratedBoxTransition( - position: DecorationPosition.background, - decoration: DecorationTween( - begin: const BoxDecoration( - color: Colors.greenAccent, - borderRadius: BorderRadius.all(Radius.circular(50)), - boxShadow: [ - BoxShadow( - offset: Offset(1, 1), - color: Colors.purple, - blurRadius: 3, - spreadRadius: 1) - ]), - end: const BoxDecoration( - color: Colors.orange, - borderRadius: BorderRadius.all(Radius.circular(10)), - boxShadow: [ - BoxShadow( - offset: Offset(1, 1), - color: Colors.blue, - blurRadius: 1, - spreadRadius: 0) - ])).animate(_ctrl), - child: const Icon(Icons.android, color: Colors.white, size: 60), - ), - )); - } -} diff --git a/packages/widgets/lib/StatefulWidget/DefaultTabController/node1_base.dart b/packages/widgets/lib/StatefulWidget/DefaultTabController/node1_base.dart deleted file mode 100644 index 41e708eb5..000000000 --- a/packages/widgets/lib/StatefulWidget/DefaultTabController/node1_base.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 230 DefaultTabController 默认Tab控制器 在使用TabBar和TabBarView时,需要同一个控制器实现页签和页面的控制。DefaultTabController会在未指定控制器时提供默认控制器,简化使用。 -// { -// "widgetId": 230, -// "name": 'DefaultTabController基本使用', -// "priority": 1, -// "subtitle": -// "【length】 : 页签数量 【int】\n" -// "【initialIndex】 : 初始页签索引 【int】\n" -// "【child】 : 组件 【Widget】", -// } - -class DefaultTabControllerDemo extends StatelessWidget { - final List tabs = const [ - Tab(text: '青眼白龙'), - Tab(text: '黑魔术师'), - Tab(text: '混沌战士'), - ]; - - const DefaultTabControllerDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 300, - child: DefaultTabController( - length: tabs.length, - child: Scaffold( - appBar: AppBar( - title: const Text("DefaultTabController"), - bottom: TabBar( - tabs: tabs, - ), - ), - body: TabBarView( - children: tabs.map((Tab tab) { - return Center( - child: Text( - '${tab.text}', - style: const TextStyle(fontSize: 20), - ), - ); - }).toList(), - ), - ), - ), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/DefaultTextStyleTransition/node1_base.dart b/packages/widgets/lib/StatefulWidget/DefaultTextStyleTransition/node1_base.dart deleted file mode 100644 index 8d8ef77c6..000000000 --- a/packages/widgets/lib/StatefulWidget/DefaultTextStyleTransition/node1_base.dart +++ /dev/null @@ -1,87 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 114, -// "name": 'DefaultTextStyleTransition基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】\n" -// "【textAlign】 : 文字对齐方式 【TextAlign】\n" -// "【softWrap】 : 是否包裹 【bool】\n" -// "【maxLines】 : 最大行数 【int】\n" -// "【overflow】 : 溢出模式 【TextOverflow】\n" -// "【style】 : 动画 【Animation】", -// } -class CustomDefaultTextStyleTransition extends StatefulWidget { - const CustomDefaultTextStyleTransition({Key? key}) : super(key: key); - - @override - _CustomDefaultTextStyleTransitionState createState() => - _CustomDefaultTextStyleTransitionState(); -} - -class _CustomDefaultTextStyleTransitionState - extends State - with SingleTickerProviderStateMixin { - late AnimationController _ctrl; - - @override - void initState() { - _ctrl = AnimationController( - vsync: this, - duration: const Duration(seconds: 1), - ); - _ctrl.forward(); - super.initState(); - } - - @override - void dispose() { - _ctrl.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: () { - setState(() { - _ctrl.reset(); - _ctrl.forward(); - }); - }, - child: Container( - alignment: Alignment.center, - width: 300, - height: 100, - child: DefaultTextStyleTransition( - textAlign: TextAlign.start, - softWrap: true, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyleTween( - begin: const TextStyle( - color: Colors.blue, - fontSize: 50, - shadows: [ - Shadow( - offset: Offset(1, 1), - color: Colors.black, - blurRadius: 3) - ]), - end: const TextStyle( - color: Colors.white, - fontSize: 20, - shadows: [ - Shadow( - offset: Offset(1, 1), - color: Colors.purple, - blurRadius: 3) - ])).animate(_ctrl), - child: const Text('张风捷特烈'), - ), - )); - } -} diff --git a/packages/widgets/lib/StatefulWidget/Dismissible/node1_base.dart b/packages/widgets/lib/StatefulWidget/Dismissible/node1_base.dart deleted file mode 100644 index aba2c84c6..000000000 --- a/packages/widgets/lib/StatefulWidget/Dismissible/node1_base.dart +++ /dev/null @@ -1,100 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: - - -// { -// "widgetId": 176, -// "name": 'Dismissible基本使用', -// "priority": 1, -// "subtitle": "【child】 : 子组件 【Widget】\n" -// "【background】 : 左底 【Widget】\n" -// "【secondaryBackground】 : 右底 【Widget】\n" -// "【key】 : 键 【Key】\n" -// "【confirmDismiss】 : 确认回调 【DismissDirectionCallback】\n" -// "【onDismissed】 : 消失回调 【DismissDirectionCallback】\n", -// } -class CustomDismissible extends StatefulWidget { - const CustomDismissible({Key? key}) : super(key: key); - - @override - _CustomDismissibleState createState() => _CustomDismissibleState(); -} - -class _CustomDismissibleState extends State { - final List data = [ - Colors.purple[50]!, - Colors.purple[100]!, - Colors.purple[200]!, - Colors.purple[300]!, - Colors.purple[400]!, - Colors.purple[500]!, - Colors.purple[600]!, - Colors.purple[700]!, - Colors.purple[800]!, - Colors.purple[900]!, - ]; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 200, - child: ListView( - padding: const EdgeInsets.symmetric(horizontal: 5), - children: data.map((color) => _buildItem(color)).toList(), - ), - ); - } - - Widget _buildItem(Color color) { - return Dismissible( - background: Container( - color: Colors.green, - alignment: const Alignment(-0.9, 0), - child: const Icon( - Icons.check, - color: Colors.white, - ), - ), - secondaryBackground: Container( - alignment: const Alignment(0.9, 0), - child: const Icon( - Icons.close, - color: Colors.white, - ), - color: Colors.red, - ), - key: ValueKey(color), - onDismissed: (d) { - data.remove(color); - }, - confirmDismiss: (e) async { - if (e == DismissDirection.endToStart) { - return true; - } else { - return false; - } - }, - child: Container( - alignment: Alignment.center, - height: 50, - color: color, - child: Text( - colorString(color), - style: const TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2, - ) - ]), - ), - ), - ); - } - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} \ No newline at end of file diff --git a/packages/widgets/lib/StatefulWidget/Dismissible/node2_direction.dart b/packages/widgets/lib/StatefulWidget/Dismissible/node2_direction.dart deleted file mode 100644 index 585169ac0..000000000 --- a/packages/widgets/lib/StatefulWidget/Dismissible/node2_direction.dart +++ /dev/null @@ -1,108 +0,0 @@ - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: - - -// { -// "widgetId": 176, -// "name": 'Dismissible基本使用', -// "priority": 2, -// "subtitle": "【direction】 : 方向 【DismissDirection】\n" -// "【crossAxisEndOffset】 : 偏移 【double】\n", -// } -class DirectionDismissible extends StatefulWidget { - const DirectionDismissible({Key? key}) : super(key: key); - - @override - _CustomDirectionDismissibleState createState() => - _CustomDirectionDismissibleState(); -} - -class _CustomDirectionDismissibleState extends State { - final List data = [ - Colors.purple[50]!, - Colors.purple[100]!, - Colors.purple[200]!, - Colors.purple[300]!, - Colors.purple[400]!, - Colors.purple[500]!, - Colors.purple[600]!, - Colors.purple[700]!, - Colors.purple[800]!, - Colors.purple[900]!, - ]; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 200, - child: ListView( - scrollDirection: Axis.horizontal, - padding: const EdgeInsets.symmetric(horizontal: 5), - children: data.map((color) => _buildItem(color)).toList(), - ), - ); - } - - Widget _buildItem(Color color) { - return Dismissible( - direction: DismissDirection.vertical, - background: Container( - color: Colors.green, - alignment: const Alignment( - 0, - -0.9, - ), - child: const Icon( - Icons.check, - color: Colors.white, - ), - ), - crossAxisEndOffset: 0.5, - secondaryBackground: Container( - alignment: const Alignment( - 0, - 0.9, - ), - child: const Icon( - Icons.close, - color: Colors.white, - ), - color: Colors.red, - ), - key: ValueKey(color), - onDismissed: (d) { - data.remove(color); - }, - confirmDismiss: (e) async { - print(e); - if (e == DismissDirection.up) { - return true; - } else { - return false; - } - }, - child: Container( - alignment: Alignment.center, - width: 80, - color: color, - child: Text( - colorString(color), - style: const TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2, - ) - ]), - ), - ), - ); - } - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} \ No newline at end of file diff --git a/packages/widgets/lib/StatefulWidget/DragTarget/node1_base.dart b/packages/widgets/lib/StatefulWidget/DragTarget/node1_base.dart deleted file mode 100644 index 41a1aa1ec..000000000 --- a/packages/widgets/lib/StatefulWidget/DragTarget/node1_base.dart +++ /dev/null @@ -1,97 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-22 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 104, -// "name": 'DragTarget基本使用', -// "priority": 1, -// "subtitle": -// "【builder】 : 组件构造器 【DragTargetBuilder】\n" -// "【onWillAccept】 : 拖入时 【Function(T)】\n" -// "【onAccept】 : 拖拽成功 【Function(T)】\n" -// "【onLeave】 : 拖入再脱出 【Function(T)】", -// } - -class CustomDragTarget extends StatefulWidget { - const CustomDragTarget({Key? key}) : super(key: key); - - @override - _CustomDragTargetState createState() => _CustomDragTargetState(); -} - -class _CustomDragTargetState extends State { - Color _color = Colors.grey; - String _info = 'DragTarget'; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Wrap(children: _buildColors(), spacing: 10), - const SizedBox(height: 20), - _buildDragTarget() - ], - ); - } - - final List colors = const [ - Colors.red, - Colors.yellow, - Colors.blue, - Colors.green, - Colors.orange, - Colors.purple, - Colors.cyanAccent - ]; - - List _buildColors() => colors - .map( - (e) => Draggable( - child: Container( - width: 30, - height: 30, - alignment: Alignment.center, - child: Text( - colors.indexOf(e).toString(), - style: const TextStyle( - color: Colors.white, fontWeight: FontWeight.bold), - ), - decoration: BoxDecoration(color: e, shape: BoxShape.circle), - ), - data: e, - feedback: Container( - width: 25, - height: 25, - decoration: BoxDecoration(color: e, shape: BoxShape.circle), - )), - ).toList(); - - Widget _buildDragTarget() { - return DragTarget( - onLeave: (data) => setState(() => _info='onLeave'), - onAccept: (data) => setState(() { - _info='onAccept'; - _color = data; - }), - onWillAccept: (data) { - setState(() { - _info='onWillAccept'; - }); - print("onWillAccept: data = $data "); - return data != null; - }, - builder: (context, candidateData, rejectedData) => Container( - width: 150.0, - height: 50.0, - color: _color, - child: Center( - child: Text( - _info, - style: const TextStyle(color: Colors.white), - ), - ))); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatefulWidget/Draggable/node1_base.dart b/packages/widgets/lib/StatefulWidget/Draggable/node1_base.dart deleted file mode 100644 index 98da97505..000000000 --- a/packages/widgets/lib/StatefulWidget/Draggable/node1_base.dart +++ /dev/null @@ -1,47 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-22 -/// contact me by email 1981462002@qq.com -/// 说明: -/// -// { -// "widgetId": 103, -// "name": 'Draggable基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子 【Widget】\n" -// "【feedback】 : 拖拽时的孩子 【Widget】\n" -// "【axis】 : 拖动的轴 【Axis】", -// } -class CustomDraggable extends StatelessWidget { - const CustomDraggable({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - List axis = [null, Axis.vertical, Axis.horizontal]; - return Wrap( - spacing: 30, - children: axis - .map((e) => Draggable( - axis: e, - child: Container( - width: 30, - height: 30, - alignment: Alignment.center, - decoration: const BoxDecoration( - color: Colors.blue, - shape: BoxShape.circle, - ), - ), - feedback: Container( - width: 30, - height: 30, - decoration: const BoxDecoration( - color: Colors.red, - shape: BoxShape.circle, - ), - ), - )) - .toList()); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatefulWidget/Draggable/node2_data.dart b/packages/widgets/lib/StatefulWidget/Draggable/node2_data.dart deleted file mode 100644 index 907f4583e..000000000 --- a/packages/widgets/lib/StatefulWidget/Draggable/node2_data.dart +++ /dev/null @@ -1,107 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-22 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 103, -// "name": 'Draggable与DragTarget联用', -// "priority": 2, -// "subtitle": -// "【data】 : 数据 【T】\n" -// "【onDragStarted】 : 开始拖拽 【Function()】\n" -// "【onDragEnd】 : 结束拖拽 【Function(DraggableDetails)】\n" -// "【onDragCompleted】 : 拖拽完成 【Function()】\n" -// "【onDraggableCanceled】 : 拖拽取消 【Function(Velocity,Offset)】\n" -// "【onChanged】 : 改变时回调 【Function(T)】", -// } - -class DraggablePage extends StatefulWidget { - const DraggablePage({Key? key}) : super(key: key); - - @override - _DraggablePageState createState() => _DraggablePageState(); -} - -class _DraggablePageState extends State { - Color _color = Colors.grey; - String _info = 'DragTarget'; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Wrap( - children: _buildColors(), - spacing: 10, - ), - const SizedBox( - height: 20, - ), - _buildDragTarget() - ], - ); - } - - List colors = [ - Colors.red, - Colors.yellow, - Colors.blue, - Colors.green, - Colors.orange, - Colors.purple, - Colors.cyanAccent - ]; - - List _buildColors() => colors - .map( - (e) => Draggable( - onDragStarted: () => setState(() => _info = '开始拖拽'), - onDragEnd: (d) => setState(() => _info = '结束拖拽'), - onDragCompleted: () => _info = '拖拽完成', - onDraggableCanceled: (v, o) => _info = '拖拽取消', - child: Container( - width: 30, - height: 30, - alignment: Alignment.center, - child: Text( - colors.indexOf(e).toString(), - style: const TextStyle( - color: Colors.white, fontWeight: FontWeight.bold,), - ), - decoration: BoxDecoration(color: e, shape: BoxShape.circle), - ), - data: e, - feedback: Container( - width: 25, - height: 25, - decoration: BoxDecoration(color: e, shape: BoxShape.circle), - )), - ) - .toList(); - - Widget _buildDragTarget() { - return DragTarget( - onLeave: (data) => print("onLeave: data = $data "), - onAccept: (data) { - print("onAccept: data = $data "); - setState(() { - _color = data; - }); - }, - onWillAccept: (data) { - print("onWillAccept: data = $data "); - return data != null; - }, - builder: (context, candidateData, rejectedData) => Container( - width: 150.0, - height: 50.0, - color: _color, - child: Center( - child: Text( - _info, - style: const TextStyle(color: Colors.white), - ), - ))); - } -} diff --git a/packages/widgets/lib/StatefulWidget/Draggable/node3_use.dart b/packages/widgets/lib/StatefulWidget/Draggable/node3_use.dart deleted file mode 100644 index ef946daa0..000000000 --- a/packages/widgets/lib/StatefulWidget/Draggable/node3_use.dart +++ /dev/null @@ -1,85 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/28 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 103, -// "name": 'Draggable其他使用', -// "priority": 3, -// "subtitle": -// "可以根据拖拽来处理一些事件。如删除、查询、弹框等", -// } - -class DeleteDraggable extends StatefulWidget { - const DeleteDraggable({Key? key}) : super(key: key); - - @override - _DeleteDraggableState createState() => _DeleteDraggableState(); -} - -class _DeleteDraggableState extends State { - List colors = [ - Colors.red, - Colors.yellow, - Colors.blue, - Colors.green, - Colors.orange, - Colors.purple, - Colors.cyanAccent - ]; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Wrap(children: _buildColors(), spacing: 10), - const SizedBox(height: 20), - _buildDragTarget() - ], - ); - } - - Widget _buildDragTarget() { - return DragTarget( - onAccept: (data) { - setState(() { - colors.removeAt(data); - }); - }, - onWillAccept: (data) => data != null, - builder: (context, candidateData, rejectedData) => Container( - width: 50.0, - height: 50.0, - decoration: - const BoxDecoration(color: Colors.red, shape: BoxShape.circle), - child: const Center( - child: Icon(Icons.delete_sweep, color: Colors.white), - ))); - } - - List _buildColors() => colors - .map( - (e) => Draggable( - child: Container( - width: 30, - height: 30, - alignment: Alignment.center, - child: Text( - colors.indexOf(e).toString(), - style: const TextStyle( - color: Colors.white, fontWeight: FontWeight.bold), - ), - decoration: BoxDecoration(color: e, shape: BoxShape.circle), - ), - data: colors.indexOf(e), - feedback: Container( - width: 25, - height: 25, - decoration: BoxDecoration( - color: e.withAlpha(100), shape: BoxShape.circle), - )), - ) - .toList(); -} diff --git a/packages/widgets/lib/StatefulWidget/DraggableScrollableSheet/node1_base.dart b/packages/widgets/lib/StatefulWidget/DraggableScrollableSheet/node1_base.dart deleted file mode 100644 index 2a4be825a..000000000 --- a/packages/widgets/lib/StatefulWidget/DraggableScrollableSheet/node1_base.dart +++ /dev/null @@ -1,107 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 252 DraggableScrollableSheet 拖滑页 可拖动和滑动的Sheet,可指定最大、最小、最初的分度现在滑动范围。构造器builder需要返回一个可滑动组件。 -// { -// "widgetId": 252, -// "name": 'DraggableScrollableSheet基本使用', -// "priority": 1, -// "subtitle": -// "【initialChildSize】 : 初始分度 【double】\n" -// "【minChildSize】 : 最小分度 【double】\n" -// "【maxChildSize】 : 最大分度 【double】\n" -// "【builder】 : 滑动组件构造器 【ScrollableWidgetBuilder】\n" -// "【expand】 : 是否延展 【bool】", -// } - -class DraggableScrollableSheetDemo extends StatelessWidget { - const DraggableScrollableSheetDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(10), - child: ElevatedButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => DraggableScrollableSheetPage()), - ); - }, - child: const Text("进入 DraggableScrollableSheet 测试页"), - ), - ); - } -} - -class DraggableScrollableSheetPage extends StatelessWidget { - DraggableScrollableSheetPage({Key? key}) : super(key: key); - - final List data = [ - Colors.orange[50]!, - Colors.orange[100]!, - Colors.orange[200]!, - Colors.orange[300]!, - Colors.orange[400]!, - Colors.orange[500]!, - Colors.orange[600]!, - Colors.orange[700]!, - Colors.orange[800]!, - Colors.orange[900]!, - Colors.red[50]!, - Colors.red[100]!, - Colors.red[200]!, - Colors.red[300]!, - Colors.red[400]!, - Colors.red[500]!, - Colors.red[600]!, - Colors.red[700]!, - Colors.red[800]!, - Colors.red[900]!, - ]; - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text("DraggableScrollableSheet"), - ), - body: SizedBox.expand( - child: DraggableScrollableSheet( - initialChildSize: 0.3, - minChildSize: 0.2, - maxChildSize: 0.5, - expand: true, - builder: (BuildContext context, ScrollController scrollController)=> - ListView.builder( - controller: scrollController, - itemCount: data.length, - itemBuilder: buildColorItem, - ), - )), - ); - } - - Widget buildColorItem(BuildContext context, int index) { - return Container( - alignment: Alignment.center, - height: 60, - color: data[index], - child: Text( - colorString(data[index]), - style: const TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2, - ) - ]), - ), - ); - } - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/packages/widgets/lib/StatefulWidget/DrawerController/node1_base.dart b/packages/widgets/lib/StatefulWidget/DrawerController/node1_base.dart deleted file mode 100644 index e0dc5d670..000000000 --- a/packages/widgets/lib/StatefulWidget/DrawerController/node1_base.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'package:flutter/material.dart'; - - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 257 DrawerController 为 Drawer 组件提供交互行为,一般很少使用。在 Scaffold 组件源码中有使用场景。 -// { -// "widgetId": 257, -// "name": 'DrawerController基本使用', -// "priority": 1, -// "subtitle": -// "【drawerCallback】 : 事件回调 【DrawerCallback】\n" -// "【enableOpenDragGesture】 : 是否侧边滑开 【bool】\n" -// "【alignment】 : 对齐方式 【DrawerAlignment】\n" -// "【scrimColor】 : 背景颜色 【Color】\n" -// "【child】 : Drawer组件 【Widget】", -// } - -class DrawerControllerDemo extends StatefulWidget { - const DrawerControllerDemo({Key? key}) : super(key: key); - - @override - _DrawerControllerDemoState createState() => _DrawerControllerDemoState(); -} - -class _DrawerControllerDemoState extends State { - final GlobalKey _drawerKey = - GlobalKey(); - - bool _open = false; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - ElevatedButton( - onPressed: toggleDrawer, - child: const Text("显隐 Drawer"), - ), - SizedBox( - height: 200, - child: DrawerController( - scrimColor: Colors.blue.withAlpha(88), - enableOpenDragGesture: true, - key: _drawerKey, - alignment: DrawerAlignment.start, - drawerCallback: (value) { - _open = value; - }, - child: Drawer( - child: Container( - alignment: Alignment.center, - color: Colors.red, - child: const Text( - "I am Drawer!", - style: TextStyle(color: Colors.white, fontSize: 18), - ), - ), - ), - ), - ), - ], - ); - } - - void toggleDrawer() { - if (_open) { - _drawerKey.currentState?.close(); - } else { - print('---open--$_open-------'); - _drawerKey.currentState?.open(); - } - } -} diff --git a/packages/widgets/lib/StatefulWidget/DropdownButton/node1_base.dart b/packages/widgets/lib/StatefulWidget/DropdownButton/node1_base.dart deleted file mode 100644 index 3f5b6b334..000000000 --- a/packages/widgets/lib/StatefulWidget/DropdownButton/node1_base.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-16 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 55, -// "name": 'DropdownButton基本用法', -// "priority": 1, -// "subtitle": -// "【value】 : 当前值 【T】\n" -// "【items】 : 下拉选框 【List>】\n" -// "【icon】 : 图标 【Widget】\n" -// "【elevation】 : 影深 【double】\n" -// "【onChanged】 : 选择条目事件 【Function(T)】\n" -// "【backgroundColor】 : 背景色 【Color】", -// } -class CustomDropDownButton extends StatefulWidget { - const CustomDropDownButton({Key? key}) : super(key: key); - - @override - _CustomDropDownButtonState createState() => _CustomDropDownButtonState(); -} - -class _CustomDropDownButtonState extends State { - Color _color = Colors.red; - final List _colors = const [ - Colors.red, - Colors.yellow, - Colors.blue, - Colors.green - ]; - final List _info = const ["红色", "黄色", "蓝色", "绿色"]; - - @override - Widget build(BuildContext context) { - return Wrap( - children: [ - Container( - margin: const EdgeInsets.symmetric(horizontal: 20), - width: 50, - height: 50, - color: _color, - ), - DropdownButton( - value: _color, - elevation: 1, - icon: Icon( - Icons.expand_more, - size: 20, - color: _color, - ), - items: _buildItems(), - onChanged: (v) => setState(() => _color = v??Colors.blue)), - ], - ); - } - - List> _buildItems() => _colors - .map((e) => DropdownMenuItem( - value: e, - child: Text( - _info[_colors.indexOf(e)], - style: TextStyle(color: e), - ))) - .toList(); -} \ No newline at end of file diff --git a/packages/widgets/lib/StatefulWidget/DropdownButton/node2_style.dart b/packages/widgets/lib/StatefulWidget/DropdownButton/node2_style.dart deleted file mode 100644 index b8587aada..000000000 --- a/packages/widgets/lib/StatefulWidget/DropdownButton/node2_style.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-16 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 55, -// "name": 'DropdownButton的样式指定', -// "priority": 2, -// "subtitle": -// "【isDense】 : 是否紧排 【bool】\n" -// "【iconSize】 : 图标大小 【double】\n" -// "【hint】 : 提示组件 【Widget】\n" -// "【iconEnabledColor】 : 图标颜色 【Color】", -// } - -class StyleDropDownButton extends StatefulWidget { - const StyleDropDownButton({Key? key}) : super(key: key); - - @override - _StyleDropDownButtonState createState() => _StyleDropDownButtonState(); -} - -class _StyleDropDownButtonState extends State { - Color _color = Colors.red; - - final List _colors = const [ - Colors.red, - Colors.yellow, - Colors.blue, - Colors.green - ]; - final List _info = const ["红色", "黄色", "蓝色", "绿色"]; - - @override - Widget build(BuildContext context) { - return Wrap( - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Container( - margin: const EdgeInsets.symmetric(horizontal: 20), - width: 50, - height: 50, - color: _color, - ), - DropdownButton( - hint: const Text('请选择'), - isDense: true, - iconSize:20, - iconEnabledColor:_color, - value: _color, - items: _buildItems(), - onChanged: (v) => setState(() => _color = v??Colors.blue)), - ], - ); - } - - List> _buildItems() => _colors - .map((e) => DropdownMenuItem( - value: e, - child: Text( - _info[_colors.indexOf(e)], - style: TextStyle(color: e), - ))) - .toList(); -} diff --git a/packages/widgets/lib/StatefulWidget/DropdownButtonFormField/node1_base.dart b/packages/widgets/lib/StatefulWidget/DropdownButtonFormField/node1_base.dart deleted file mode 100644 index 8785f98be..000000000 --- a/packages/widgets/lib/StatefulWidget/DropdownButtonFormField/node1_base.dart +++ /dev/null @@ -1,76 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 223 DropdownButtonFormField 表单下拉框 -/// 底层依赖 DropdownButton 实现,所以基本属性类似。但拥有 FormField 的特性,可以回调 onSaved、validator 方法。 -/// link: 55,222 -/// -// { -// "widgetId": 223, -// "name": '表单下拉框简单使用', -// "priority": 1, -// "subtitle": -// "【items】 : 子组件列表 【List>】\n" -// "【validator】 : 表单验证回调 【FormFieldValidator】\n" -// "【onSaved】 : 表单保存回调 【FormFieldSetter】\n" -// "其他属性详见 DropdownButton,表单校验特性详见 FormField。", -// } - -class DropdownButtonFormFieldDemo extends StatefulWidget { - const DropdownButtonFormFieldDemo({Key? key}) : super(key: key); - - @override - _DropdownButtonFormFieldDemoState createState() => - _DropdownButtonFormFieldDemoState(); -} - -class _DropdownButtonFormFieldDemoState extends State { - Color _color = Colors.blue; - final List _colors = const [ - Colors.red, - Colors.yellow, - Colors.blue, - Colors.green - ]; - final List _info = const ["红色", "黄色", "蓝色", "绿色"]; - - @override - Widget build(BuildContext context) { - return Wrap( - children: [ - Container( - margin: const EdgeInsets.symmetric(horizontal: 20), - width: 50, - height: 50, - color: _color, - ), - - SizedBox( - width: 80, - child: DropdownButtonFormField( - value: _color, - elevation: 1, - hint: const Text('选择颜色',style: TextStyle(fontSize: 12),), - icon: Icon( - Icons.expand_more, - size: 20, - color: _color, - ), - items: _buildItems(), - onChanged: (v) => setState(() => _color = v ?? Colors.blue)), - ) - - ], - ); - } - - List> _buildItems() => _colors - .map((e) => DropdownMenuItem( - value: e, - child: Text( - _info[_colors.indexOf(e)], - style: TextStyle(color: e), - ))) - .toList(); -} \ No newline at end of file diff --git a/packages/widgets/lib/StatefulWidget/EditableText/node1_base.dart b/packages/widgets/lib/StatefulWidget/EditableText/node1_base.dart deleted file mode 100644 index 35b4836c3..000000000 --- a/packages/widgets/lib/StatefulWidget/EditableText/node1_base.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/7/22 -/// contact me by email 1981462002@qq.com -/// 说明: 244 EditableText 可编辑文字 可以编辑的文字,是TextField的底层最核心组件,一般不单独使用。 -// { -// "widgetId": 244, -// "name": "EditableText基本使用", -// "priority": 1, -// "subtitle": "【controller】 : 控制器 【TextEditingController】\n" -// "【focusNode】 : 焦点 【FocusNode】\n" -// "【style】 : 文字样式 【TextStyle】\n" -// "【backgroundCursorColor】 : 背景游标颜色 【Color】\n" -// "【cursorColor】 : 游标颜色 【Color】\n" -// "上面五个是EditableText必须的属性,其他同TextField,此处不再列举。", -// } - -class EditableTextDemo extends StatefulWidget { - const EditableTextDemo({Key? key}) : super(key: key); - - @override - _EditableTextDemoState createState() => _EditableTextDemoState(); -} - -class _EditableTextDemoState extends State { - final TextEditingController _ctrl = - TextEditingController(text: 'Hello Flutter Unit!'); - final FocusNode _node = FocusNode(); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(8.0), - child: EditableText( - controller: _ctrl, - focusNode: _node, - style: const TextStyle(fontSize: 16, color: Colors.blue), - cursorColor: Colors.blue, - backgroundCursorColor: Colors.orange, - ), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/ElevatedButton/node1_base.dart b/packages/widgets/lib/StatefulWidget/ElevatedButton/node1_base.dart deleted file mode 100644 index 31ff221dd..000000000 --- a/packages/widgets/lib/StatefulWidget/ElevatedButton/node1_base.dart +++ /dev/null @@ -1,44 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 354 ElevatedButton Material风格的升起按钮,表现和RaisedButton类似。可通过样式更改边框、颜色、阴影等属性。 -// { -// "widgetId": 354, -// "name": 'ElevatedButton基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 是否具有滚动主体 【Widget】\n" -// "【onPressed】 : 点击事件 【VoidCallback】\n" -// "【onLongPress】 : 长按事件 【VoidCallback】", -// } - -class ElevatedButtonDemo extends StatelessWidget { - const ElevatedButtonDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - alignment: Alignment.center, - height: 60, - child: Wrap( - spacing: 20, - children: [ - ElevatedButton( - child: const Text('ElevatedButton'), - onPressed: _onPressed, - onLongPress: _onLongPress, - ), - const ElevatedButton( - child: Text('禁用按钮'), - onPressed: null, - onLongPress: null, - ), - ], - )); - } - - void _onPressed() {} - - void _onLongPress() {} -} diff --git a/packages/widgets/lib/StatefulWidget/ElevatedButton/node2_style.dart b/packages/widgets/lib/StatefulWidget/ElevatedButton/node2_style.dart deleted file mode 100644 index 9096c128e..000000000 --- a/packages/widgets/lib/StatefulWidget/ElevatedButton/node2_style.dart +++ /dev/null @@ -1,60 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 354, -// "name": 'ElevatedButton样式', -// "priority": 2, -// "subtitle": -// "【style】 : 按钮样式 【ButtonStyle】\n" -// "【focusNode】 : 焦点 【FocusNode】\n" -// "【clipBehavior】 : 裁剪行为 【Clip】\n" -// "【autofocus】 : 自动聚焦 【bool】", -// } - -class ElevatedButtonStyleDemo extends StatelessWidget { - const ElevatedButtonStyleDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - alignment: Alignment.center, - child: Wrap( - spacing: 10, - children: [ - ElevatedButton( - style: TextButton.styleFrom( - backgroundColor: Colors.orange, - primary: Colors.white, - elevation: 2, - shadowColor: Colors.orangeAccent), - child: const Text('ElevatedButton样式'), - onPressed: _onPressed, - onLongPress: _onLongPress, - ), - ElevatedButton( - style: TextButton.styleFrom( - backgroundColor: Colors.white, - primary: Colors.black, - side: const BorderSide(color: Colors.blue,width: 1), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10)) - ), - // elevation: 2, - shadowColor: Colors.orangeAccent), - child: const Text('ElevatedButton边线'), - autofocus: false, - onPressed: _onPressed, - onLongPress: _onLongPress, - ), - ], - ), - ); - } - - void _onPressed() {} - - void _onLongPress() {} -} diff --git a/packages/widgets/lib/StatefulWidget/ExpandIcon/node1_base.dart b/packages/widgets/lib/StatefulWidget/ExpandIcon/node1_base.dart deleted file mode 100644 index a90eefcd3..000000000 --- a/packages/widgets/lib/StatefulWidget/ExpandIcon/node1_base.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-03-16 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 51, -// "name": 'ExpandIcon基本使用', -// "priority": 1, -// "subtitle": -// "【isExpanded】 : 是否展开 【bool】\n" -// "【padding】 : 内边距 【EdgeInsetsGeometry】\n", -// "【size】 : 图标大小 【double】\n" -// "【color】 : 不展开时颜色 【Color】\n" -// "【expandedColor】 : 展开时颜色 【Color】\n" -// "【onPressed】 : 点击事件 【Function(bool)】", -// } -class CustomExpandIcon extends StatefulWidget { - const CustomExpandIcon({Key? key}) : super(key: key); - - @override - _CustomExpandIconState createState() => _CustomExpandIconState(); -} - -class _CustomExpandIconState extends State { - bool _closed = true; - - @override - Widget build(BuildContext context) { - return ExpandIcon( - isExpanded: _closed, - padding: const EdgeInsets.all(5), - size: 30, - color: Colors.blue, - expandedColor: Colors.orangeAccent, - onPressed: (value) => setState(() => _closed = !_closed), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/ExpansionPanelList/node1_base.dart b/packages/widgets/lib/StatefulWidget/ExpansionPanelList/node1_base.dart deleted file mode 100644 index 3faf35a8b..000000000 --- a/packages/widgets/lib/StatefulWidget/ExpansionPanelList/node1_base.dart +++ /dev/null @@ -1,98 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 178, -// "name": 'ExpansionPanelList基本使用', -// "priority": 1, -// "subtitle": -// "【children】 : 子组件列表 【List】\n" -// "【animationDuration】 : 动画时长 【Duration】\n" -// "【expansionCallback】 : 展开回调 【List】\n" -// "【onPressed】 : 点击事件 【Function()】", -// } -class CustomExpansionPanelList extends StatefulWidget { - const CustomExpansionPanelList({Key? key}) : super(key: key); - - @override - _CustomExpansionPanelListState createState() => - _CustomExpansionPanelListState(); -} - -class _CustomExpansionPanelListState extends State { - final List data = [ - Colors.red[50]!, - Colors.red[100]!, - Colors.red[200]!, - Colors.red[300]!, - Colors.red[400]!, - Colors.red[500]!, - Colors.red[600]!, - Colors.red[700]!, - Colors.red[800]!, - Colors.red[900]!, - ]; - int _position = 0; - - @override - Widget build(BuildContext context) { - return SizedBox( - width: 300, - child: ExpansionPanelList( - children: data.map((color) => _buildItem(color)).toList(), - animationDuration: const Duration(milliseconds: 200), - expansionCallback: (index, open) { - setState(() => _position = open ? -1 : index); - }, - ), - ); - } - - ExpansionPanel _buildItem(Color color) { - return ExpansionPanel( - isExpanded: data.indexOf(color) == _position, - canTapOnHeader: true, - headerBuilder: (ctx, index) => Center( - child: Wrap( - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Container( - height: 30, - width: 30, - decoration: - BoxDecoration(color: color, shape: BoxShape.circle), - ), - Container( - width: 120, - alignment: Alignment.center, - height: 50, - child: Text( - colorString(color), - style: const TextStyle(color: Colors.black), - ), - ), - ], - ), - ), - body: Container( - alignment: Alignment.center, - height: 50, - color: color, - child: Text( - colorString(color), - style: const TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2, - ) - ]), - ), - )); - } - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/packages/widgets/lib/StatefulWidget/ExpansionTile/node1_base.dart b/packages/widgets/lib/StatefulWidget/ExpansionTile/node1_base.dart deleted file mode 100644 index 7c59f98f9..000000000 --- a/packages/widgets/lib/StatefulWidget/ExpansionTile/node1_base.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:flutter/material.dart'; -import '../../StatelessWidget/RadioListTile/node1_base.dart'; -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 52, -// "name": 'ExpansionTile基本使用', -// "priority": 1, -// "subtitle": "【children】 : 展开内容 【List】\n" -// "【leading】 : 头左组件 【Widget】\n" -// "【title】 : 头中组件 【Widget】\n" -// "【trailing】 : 头尾组件 【Widget】\n" -// "【backgroundColor】 : 背景色 【Color】\n" -// "【onExpansionChanged】 : 折叠事件 【Function(bool)】\n" -// "【initiallyExpanded】 : 是否初始时展开 【bool】", -// } -class CustomExpansionTile extends StatefulWidget { - const CustomExpansionTile({Key? key}) : super(key: key); - - @override - _CustomExpansionTileState createState() => _CustomExpansionTileState(); -} - -class _CustomExpansionTileState extends State { - @override - Widget build(BuildContext context) { - return ExpansionTile( - leading: const Icon(Icons.star), - title: const Text("选择语言"), - backgroundColor: Colors.grey.withAlpha(6), - onExpansionChanged: (value) { - print('$value'); - }, - initiallyExpanded: false, - children: const[CustomRadioListTile()], - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/Form/node1_base.dart b/packages/widgets/lib/StatefulWidget/Form/node1_base.dart deleted file mode 100644 index 1cf4dbd54..000000000 --- a/packages/widgets/lib/StatefulWidget/Form/node1_base.dart +++ /dev/null @@ -1,114 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-31 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 198, -// "name": 'Form基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 子组件 【Widget】\n" -// "【onChanged】 : 表单变化回调 【VoidCallback】\n" -// "【onWillPop】 : 返回回调 【WillPopCallback】", -// } -class CustomForm extends StatefulWidget { - const CustomForm({Key? key}) : super(key: key); - - @override - _CustomFormState createState() => _CustomFormState(); -} - -class _CustomFormState extends State { - final GlobalKey _formKey = GlobalKey(); - - @override - Widget build(BuildContext context) { - return Form( - onWillPop: () => _willPop(context), - key: _formKey, - onChanged: () { - print('Form---onChanged'); - }, - child: - Stack( - alignment: Alignment.centerRight, - children: [ - SizedBox( - width: 350, - child: UnconstrainedBox( - child: SizedBox( - width: 200, - height: 70, - child: TextFormField( - style: const TextStyle(textBaseline: TextBaseline.alphabetic), - decoration: const InputDecoration( - border: OutlineInputBorder(), - labelText: 'username', - ), - validator: _validateUsername, - ), - ), - ), - ), - Positioned( - top: 0, right: 0, child: _buildSubmitButton(context)), - ], - ), - ); - } - - String? _validateUsername(value) { - if (value.isEmpty) { - return '用户名不能为空'; - } - return null; - } - - Widget _buildSubmitButton(BuildContext context) { - return ElevatedButton( - style: TextButton.styleFrom( - backgroundColor: Colors.blue, - shape: const CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFDFDFDF)), - ), - ), - onPressed: _onSubmit, - child: const Icon( - Icons.check, - color: Colors.white, - ), - ); - } - - void _onSubmit(){ - if(_formKey.currentState==null) return; - if (_formKey.currentState!.validate()) { - FocusScope.of(context).requestFocus(FocusNode()); - Navigator.of(context).pop(); - } - } - - Future _willPop(context) async { - return await showDialog( - context: context, - builder: (context) => AlertDialog( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10))), - title: const Text('提示'), - content: const Text('你确定要离开此页吗?'), - actions: [ - ElevatedButton( - onPressed: () => Navigator.of(context).pop(true), - child: const Text('确定'), - ), - ElevatedButton( - onPressed: () => Navigator.of(context).pop(false), - child: const Text('取消'), - ), - ], - ), - ) ?? - false; - } -} diff --git a/packages/widgets/lib/StatefulWidget/FormField/node1_base.dart b/packages/widgets/lib/StatefulWidget/FormField/node1_base.dart deleted file mode 100644 index f687de2f5..000000000 --- a/packages/widgets/lib/StatefulWidget/FormField/node1_base.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-04-01 -/// contact me by email 1981462002@qq.com -/// 说明: 222 FormField 表单字段 -/// 一个表单字段,需要在 Form 组件中使用,内含泛型 T 的字段作为状态量,对根据字段的更新和验证会触发相应回调。 -/// link:198,199,223, -// { -// "widgetId": 222, -// "name": 'FormField 介绍', -// "priority": 1, -// "subtitle": -// "【builder】 : 内容构造器 【FormFieldBuilder】\n" -// "【initialValue】 : 初始值 【T】\n" -// "【validator】 : 验证函数 【FormFieldValidator 】\n" -// "【enabled】 : 是否有效 【bool】\n" -// "【onSaved】 : 表单save时回调 【FormFieldSetter】", -// } -class FormFieldDemo extends StatelessWidget { - const FormFieldDemo({Key? key}) : super(key: key); - - final String info = - 'FormField 代表表单中的一个字段,对于字符串类型的字段,框架中封装了 TextFormField 以便使用;下拉选择的字段,用 DropdownButtonFormField。' - '目前框架中 FormField 的子类也只有这两个。既然是表单字段,必然是要和 Form 组件一起使用。通过对 Form 添加 GlobalKey ,来获取 FormState 对象。' - '当 FormState 调用 save 方法时,所有的 FormField 都会触发 onSave 方法,当 FormState 调用 validate 方法时,所有的 FormField 都会触发 validate 方法。'; - - - @override - Widget build(BuildContext context) { - - - return Container( - color: Colors.blue.withOpacity(0.1), - padding: const EdgeInsets.all(10), - margin: const EdgeInsets.all(10), - child: Text(info), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/FutureBuilder/node1_base.dart b/packages/widgets/lib/StatefulWidget/FutureBuilder/node1_base.dart deleted file mode 100644 index d0aacef9b..000000000 --- a/packages/widgets/lib/StatefulWidget/FutureBuilder/node1_base.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 172, -// "name": 'FutureBuilder基本使用', -// "priority": 1, -// "subtitle": -// "【builder】 : 子组件 【AsyncWidgetBuilder】\n" -// "【initialData】 : 初始数据 【T】\n" -// "【future】 : 异步任务 【Future】", -// } -class CustomFutureBuilder extends StatefulWidget { - const CustomFutureBuilder({Key? key}) : super(key: key); - - @override - _CustomFutureBuilderState createState() => _CustomFutureBuilderState(); -} - -class _CustomFutureBuilderState extends State { - late Future _future; - - @override - void initState() { - _future = loadData(); - super.initState(); - } - - @override - Widget build(BuildContext context) { - return FutureBuilder( - initialData: 'Load', - future: _future, - builder: (ctx, snap) { - if (snap.connectionState == ConnectionState.done) { - return Text('${snap.data}'); - } - if (snap.connectionState == ConnectionState.waiting) { - return const CircularProgressIndicator(); - } - if (snap.hasError) { - return const Text('Error'); - } - return Container(); - }); - } - - Future loadData() async { - await Future.delayed(const Duration(seconds: 2)); - return 'LoadeSuccess'; - } -} diff --git a/packages/widgets/lib/StatefulWidget/GlowingOverscrollIndicator/node1_base.dart b/packages/widgets/lib/StatefulWidget/GlowingOverscrollIndicator/node1_base.dart deleted file mode 100644 index efd87685e..000000000 --- a/packages/widgets/lib/StatefulWidget/GlowingOverscrollIndicator/node1_base.dart +++ /dev/null @@ -1,64 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 250 GlowingOverscrollIndicator 孩子为可滑动列表,当滑动到顶部和底部时的指示效果,可指定颜色,没什么太大卵用。是Android和fuchsia系统默认滑动效果。 -// { -// "widgetId": 250, -// "name": '基本使用', -// "priority": 1, -// "subtitle": -// "【showLeading】 : 头部是否生效 【bool】\n" -// "【showTrailing】 : 底部是否生效 【bool】\n" -// "【axisDirection】 : 轴向 【AxisDirection】\n" -// "【color】 : 颜色 【Color】\n" -// "【child】 : 子组件 【Widget】", -// } - -class GlowingOverscrollIndicatorDemo extends StatelessWidget { - GlowingOverscrollIndicatorDemo({Key? key}) : super(key: key); - - final List data = [ - Colors.orange[50]!, - Colors.orange[100]!, - Colors.orange[200]!, - Colors.orange[300]!, - Colors.orange[400]!, - Colors.orange[500]!, - Colors.orange[600]!, - Colors.orange[700]!, - Colors.orange[800]!, - Colors.orange[900]!, - Colors.red[50]!, - Colors.red[100]!, - Colors.red[200]!, - Colors.red[300]!, - Colors.red[400]!, - Colors.red[500]!, - Colors.red[600]!, - Colors.red[700]!, - Colors.red[800]!, - Colors.red[900]!, - ]; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 300, - child: GlowingOverscrollIndicator( - color: Colors.purple, - // showLeading: false, - // showTrailing: false, - axisDirection: AxisDirection.down, - child: ListView.builder( - itemBuilder: (_, index) => Container( - margin: const EdgeInsets.all(10), - height: 60, - color: data[index], - ), - itemCount: data.length, - ), - ), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/Hero/node1_base.dart b/packages/widgets/lib/StatefulWidget/Hero/node1_base.dart deleted file mode 100644 index 119a0f418..000000000 --- a/packages/widgets/lib/StatefulWidget/Hero/node1_base.dart +++ /dev/null @@ -1,128 +0,0 @@ - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 171, -// "name": 'Hero基本使用', -// "priority": 1, -// "subtitle": -// "【tag】 : 标签 【String】\n", -// } -class CustomHero extends StatelessWidget { - const CustomHero({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - Hero hero = Hero( - //----定义一个Hero,并添加tag标签,此中组件共享 - tag: 'user-head', - child: ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(30)), - child: Image.asset( - "assets/images/icon_head.webp", - width: 60, - height: 60, - fit: BoxFit.cover, - ), - ), - ); - - Widget container = Container( - alignment: const Alignment(-0.8, -0.8), - child: hero, - width: 250, - height: 250 * 0.618, - decoration: BoxDecoration( - gradient: LinearGradient(colors: [ - Colors.red.withAlpha(99), - Colors.yellow.withAlpha(189), - Colors.green.withAlpha(88), - Colors.blue.withAlpha(230) - ])), - ); - - return GestureDetector( - child: Card(elevation: 5, child: container), - onTap: () => Navigator.push( - context, - Bottom2TopRouter(child: const TargetPage(), duration: 1000), - ), - ); - } - -} - -class TargetPage extends StatelessWidget { - const TargetPage({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - Hero hero = const Hero( - //----定义一个Hero,为其添加标签,两个标签相同,则可以共享 - tag: 'user-head', - child: Padding( - padding: EdgeInsets.all(6.0), - child: CircleAvatar( - backgroundColor: Colors.transparent, - backgroundImage: AssetImage( - "assets/images/icon_head.webp", - ), - ), - ), - ); - - Widget touch = InkWell( - onTap: () { - Navigator.of(context).pop(); - }, - child: hero, - ); - - return Scaffold( - appBar: AppBar( - actions: [touch], - ), - body: Container( - decoration: BoxDecoration( - gradient: LinearGradient(colors: [ - Colors.red.withAlpha(99), - Colors.yellow.withAlpha(189), - Colors.green.withAlpha(88), - Colors.blue.withAlpha(230) - ])), - ), - ); - } -} - -//下--->上 -class Bottom2TopRouter extends PageRouteBuilder { - final Widget child; - final int duration; - final Curve curve; - - Bottom2TopRouter({ - required this.child, - this.duration = 500, - this.curve = Curves.fastOutSlowIn, - }) : super( - transitionDuration: Duration(milliseconds: duration), - pageBuilder: (ctx, a1, a2) { - return child; - }, - transitionsBuilder: ( - ctx, - a1, - a2, - Widget child, - ) => SlideTransition( - position: Tween( - begin: const Offset(0.0, 1.0), - end: const Offset(0.0, 0.0), - ).animate(CurvedAnimation(parent: a1, curve: curve)), - child: child)); -} diff --git a/packages/widgets/lib/StatefulWidget/Image/node1_base.dart b/packages/widgets/lib/StatefulWidget/Image/node1_base.dart deleted file mode 100644 index 42ca2bfbd..000000000 --- a/packages/widgets/lib/StatefulWidget/Image/node1_base.dart +++ /dev/null @@ -1,38 +0,0 @@ - -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 38, -// "name": '可从资源文件和网络加载图片', -// "priority": 1, -// "subtitle": "Image.asset加载资源图片,指定路径;\n" -// "Image.network加载资源网络图片,指定链接。\n" -// "Image.file加载资源文件图片,指定路径。\n" -// "Image.memory加载内存图片,指定字节数组。\n" -// "【height】 : 宽 【double】\n" -// "【width】: 高 【double】" -// } - -class LoadImage extends StatelessWidget { - const LoadImage({Key? key}) : super(key: key); - - final String assetsImagePath = "assets/images/icon_head.webp"; - final String netImageUrl = "https://p9-juejin.byteimg.com" - "/tos-cn-i-k3u1fbpfcp/36dee4e4dceb4c41a93df4a3603439fe~" - "tplv-k3u1fbpfcp-zoom-crop-mark:1304:1304:1304:734.awebp"; - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 10, - children: [ - Image.asset(assetsImagePath, height: 80, width: 80), - _loadFromNet(), - ], - ); - } - - Widget _loadFromNet() => Image.network(netImageUrl, height: 80); -} diff --git a/packages/widgets/lib/StatefulWidget/Image/node2_fit.dart b/packages/widgets/lib/StatefulWidget/Image/node2_fit.dart deleted file mode 100644 index 0155c389b..000000000 --- a/packages/widgets/lib/StatefulWidget/Image/node2_fit.dart +++ /dev/null @@ -1,62 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 38, -// "name": '图片的适应模式', -// "priority": 2, -// "subtitle": "【fit】 : 适应模式*7 【BoxFit】\n", -// }, - -class FitImage extends StatefulWidget { - const FitImage({Key? key}) : super(key: key); - - @override - _FitImageState createState() => _FitImageState(); -} - -class _FitImageState extends State { - bool _smallImage = false; - - @override - Widget build(BuildContext context) { - List imageLi = BoxFit.values - .toList() - .map((mode) => Column(children:[ - Container( - margin: const EdgeInsets.all(5), - width: 100, - height: 80, - color: Colors.grey.withAlpha(88), - child: Image( - image: AssetImage(!_smallImage - ? "assets/images/wy_300x200.webp" - : "assets/images/wy_30x20.webp"), - fit: mode)), - Text(mode.toString().split(".")[1]) - ])).toList(); - - return Wrap( - children: [...imageLi, _buildSwitch()], - ); - } - - Widget _buildSwitch() { - return Container( - alignment: Alignment.center, - width: 200, - height: 100, - child: Wrap( - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - const Text("使用小图"), - Switch( - value: _smallImage, - onChanged: (b) => setState(() => _smallImage = b)), - ], - ), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/Image/node3_alignment.dart b/packages/widgets/lib/StatefulWidget/Image/node3_alignment.dart deleted file mode 100644 index 75f33e7b1..000000000 --- a/packages/widgets/lib/StatefulWidget/Image/node3_alignment.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 38, -// "name": '图片对齐模式', -// "priority": 3, -// "subtitle": "【alignment】 : 颜色 【AlignmentGeometry】\n" -// " 常用Alignment类的九个静态常量,但也可定制位置", -// }, - -class AlignmentImage extends StatelessWidget { - const AlignmentImage({Key? key}) : super(key: key); - - final List alignment = const[ - Alignment.center, - Alignment.centerLeft, - Alignment.centerRight, - Alignment.topCenter, - Alignment.topLeft, - Alignment.topRight, - Alignment.bottomCenter, - Alignment.bottomLeft, - Alignment.bottomRight - ]; //测试数组 - - @override - Widget build(BuildContext context) { - List imgLi = alignment - .map((alignment) => //生成子Widget列表 - Column(children: [ - Container( - margin: const EdgeInsets.all(5), - width: 90, - height: 60, - color: Colors.grey.withAlpha(88), - child: Image( - image: const AssetImage("assets/images/wy_30x20.webp"), - alignment: alignment, - )), - Text(alignment.toString().split(".")[1]) - ])) - .toList(); - var imageAlignment = Wrap(children: imgLi); - return imageAlignment; - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatefulWidget/Image/node4_colorBlendMode.dart b/packages/widgets/lib/StatefulWidget/Image/node4_colorBlendMode.dart deleted file mode 100644 index 530f5cefa..000000000 --- a/packages/widgets/lib/StatefulWidget/Image/node4_colorBlendMode.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 38, -// "name": '图片颜色及混合模式', -// "priority": 4, -// "subtitle": "【color】 : 颜色 【Color】\n" -// "【colorBlendMode】: 混合模式*29 【BlendMode】", -// }, - -class BlendModeImage extends StatelessWidget { - const BlendModeImage({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Wrap( - children: BlendMode.values - .toList() - .map((mode) => Column(children:[ - Container( - margin: const EdgeInsets.all(5), - width: 60, - height: 60, - color: Colors.red, - child: Image( - image: const AssetImage("assets/images/icon_head.webp"), - color: Colors.blue.withAlpha(88), - colorBlendMode: mode)), - Text(mode.toString().split(".")[1]) - ])) - .toList(), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatefulWidget/Image/node5_repeat.dart b/packages/widgets/lib/StatefulWidget/Image/node5_repeat.dart deleted file mode 100644 index 59d36b6b6..000000000 --- a/packages/widgets/lib/StatefulWidget/Image/node5_repeat.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 38, -// "name": '图片重复模式', -// "priority": 5, -// "subtitle": "【repeat】 : 重复模式*4 【ImageRepeat】", -// }, - -class RepeatImage extends StatelessWidget { - const RepeatImage({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Wrap( - children: ImageRepeat.values - .toList() - .map((mode) => Column(children:[ - Container( - margin: const EdgeInsets.all(5), - width: 150, - height: 60, - color: Colors.red, - child: Image( - image: const AssetImage("assets/images/wy_30x20.webp"), - repeat: mode)), - Text(mode.toString().split(".")[1]) - ])) - .toList(), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatefulWidget/Image/node6_centerSlice.dart b/packages/widgets/lib/StatefulWidget/Image/node6_centerSlice.dart deleted file mode 100644 index 95086264d..000000000 --- a/packages/widgets/lib/StatefulWidget/Image/node6_centerSlice.dart +++ /dev/null @@ -1,31 +0,0 @@ - -import 'package:flutter/material.dart'; - - - -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 38, -// "name": '图片实现局部放大', -// "priority": 6, -// "subtitle": "【centerSlice】 : 保留的区域 【Rect】", -// }, - -class CenterSliceImage extends StatelessWidget { - const CenterSliceImage({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return SizedBox( - width: 300, - height: 80, - child: Image.asset( - "assets/images/right_chat.png", - centerSlice: const Rect.fromLTRB(9, 27, 60, 27 + 1.0), - fit: BoxFit.fill, - ), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/Ink/node1_base.dart b/packages/widgets/lib/StatefulWidget/Ink/node1_base.dart deleted file mode 100644 index c8345204d..000000000 --- a/packages/widgets/lib/StatefulWidget/Ink/node1_base.dart +++ /dev/null @@ -1,44 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-26 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 152, -// "name": 'Ink基本使用', -// "priority": 1, -// "subtitle": "【child】 : 子组件 【Widget】\n" -// "【padding】 : 内边距 【EdgeInsetsGeometry】\n" -// "【decoration】 : 装饰 【Decoration】\n" -// "【width】 : 宽 【double】\n" -// "【height】 : 高 【double】\n" -// "【color】 : 颜色 【Color】", -// } -class CustomInk extends StatelessWidget { - const CustomInk({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Material( - color: Colors.orangeAccent, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Center( - child: Ink( - padding: const EdgeInsets.all(10), - decoration: const BoxDecoration( - color: Colors.yellow, - borderRadius: BorderRadius.all(Radius.circular(20))), - width: 200.0, - height: 100.0, - child: InkWell( - onTap: () {}, - child: const Center(child: Text('Hello')), - ), - ), - ), - ), - ); - } -} - diff --git a/packages/widgets/lib/StatefulWidget/Ink/node2_image.dart b/packages/widgets/lib/StatefulWidget/Ink/node2_image.dart deleted file mode 100644 index 5041a0187..000000000 --- a/packages/widgets/lib/StatefulWidget/Ink/node2_image.dart +++ /dev/null @@ -1,41 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-26 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 152, -// "name": 'Ink.image图片水波纹', -// "priority": 2, -// "subtitle": " 其中属性与Image组件一致,详见Image组件", -// } - -class InkImage extends StatelessWidget { - const InkImage({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Material( - color: Colors.grey[800], - child: Center( - child: Ink.image( - image: const AssetImage('assets/images/sabar.webp'), - fit: BoxFit.cover, - width: 300.0, - height: 200.0, - child: InkWell( - onTap: () {}, - child: const Align( - alignment: Alignment.topLeft, - child: Padding( - padding: EdgeInsets.all(10.0), - child: Text('Chaos', - style: TextStyle( - fontWeight: FontWeight.w900, color: Colors.black)), - ), - )), - ), - ), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/InkResponse/node1_base.dart b/packages/widgets/lib/StatefulWidget/InkResponse/node1_base.dart deleted file mode 100644 index 0fcbdb5eb..000000000 --- a/packages/widgets/lib/StatefulWidget/InkResponse/node1_base.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 149, -// "name": 'InkResponse基本事件', -// "priority": 1, -// "subtitle": "【child】 : 子组件 【Widget】\n" -// "【onTap】 : 点击事件 【Function()】\n" -// "【onDoubleTap】 : 双击事件 【Function()】\n" -// "【onTapCancel】 : 点击取消 【Function()】\n" -// "【onLongPress】 : 长按事件 【Function()】", -// } - -class CustomInkResponse extends StatefulWidget { - const CustomInkResponse({Key? key}) : super(key: key); - - @override - _CustomInkResponseState createState() => _CustomInkResponseState(); -} - -class _CustomInkResponseState extends State { - String _info = 'Push'; - - @override - Widget build(BuildContext context) { - return InkResponse( - onTap: () => setState(() => _info = 'onTap'), - onDoubleTap: () => setState(() => _info = 'onDoubleTap'), - onLongPress: () => setState(() => _info = 'onLongPress'), - onTapCancel: () => setState(() => _info = 'onTapCancel'), - child: Container( - alignment: Alignment.center, - width: 200, - height: 100, - child: Text(_info), - ), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatefulWidget/InkResponse/node2_color.dart b/packages/widgets/lib/StatefulWidget/InkResponse/node2_color.dart deleted file mode 100644 index d5d614731..000000000 --- a/packages/widgets/lib/StatefulWidget/InkResponse/node2_color.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 149, -// "name": 'InkResponse其他属性', -// "priority": 2, -// "subtitle": "【child】 : 子组件 【Widget】\n" -// "【onHighlightChanged】 : 高亮变化回调 【Function(bool)】\n" -// "【highlightColor】 : 高亮色 【Color】\n" -// "【splashColor】 : 水波纹色 【Color】\n" -// "【radius】 : 水波半径 【double】", -// } - -class ColorInkResponse extends StatefulWidget { - const ColorInkResponse({Key? key}) : super(key: key); - - @override - _ColorInkResponseState createState() => _ColorInkResponseState(); -} - -class _ColorInkResponseState extends State { - String _info = 'Push'; - - @override - Widget build(BuildContext context) { - return InkResponse( - onTap: () => {}, - splashColor: Colors.blueAccent, - highlightColor: Colors.orange, - onHighlightChanged: (v) => - setState(() => _info = 'onHighlightChanged:$v'), - radius: 50, - child: Container( - alignment: Alignment.center, - width: 200, - height: 100, - child: Text(_info), - ), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/InkWell/node1_base.dart b/packages/widgets/lib/StatefulWidget/InkWell/node1_base.dart deleted file mode 100644 index d0e0ed506..000000000 --- a/packages/widgets/lib/StatefulWidget/InkWell/node1_base.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 150, -// "name": 'InkWell基本事件', -// "priority": 1, -// "subtitle": "【child】 : 子组件 【Widget】\n" -// "【onTap】 : 点击事件 【Function()】\n" -// "【onDoubleTap】 : 双击事件 【Function()】\n" -// "【onTapCancel】 : 点击取消 【Function()】\n" -// "【onLongPress】 : 长按事件 【Function()】", -// } - -class CustomInkWell extends StatefulWidget { - const CustomInkWell({Key? key}) : super(key: key); - - @override - _CustomInkWellState createState() => _CustomInkWellState(); -} - -class _CustomInkWellState extends State { - String _info = 'Push'; - - @override - Widget build(BuildContext context) { - return InkWell( - onTap: () => setState(() => _info = 'onTap'), - onDoubleTap: () => setState(() => _info = 'onDoubleTap'), - onLongPress: () => setState(() => _info = 'onLongPress'), - onTapCancel: () => setState(() => _info = 'onTapCancel'), - child: Container( - alignment: Alignment.center, - width: 120, - height: 50, - child: Text(_info), - ), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatefulWidget/InkWell/node2_color.dart b/packages/widgets/lib/StatefulWidget/InkWell/node2_color.dart deleted file mode 100644 index ba752c6cd..000000000 --- a/packages/widgets/lib/StatefulWidget/InkWell/node2_color.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 150, -// "name": 'InkWell其他属性', -// "priority": 2, -// "subtitle": "【child】 : 子组件 【Widget】\n" -// "【onHighlightChanged】 : 高亮变化回调 【Function(bool)】\n" -// "【highlightColor】 : 高亮色 【Color】\n" -// "【splashColor】 : 水波纹色 【Color】\n" -// "【radius】 : 水波半径 【double】", -// } - -class ColorInkWell extends StatefulWidget { - const ColorInkWell({Key? key}) : super(key: key); - - @override - _ColorInkWellState createState() => _ColorInkWellState(); -} - -class _ColorInkWellState extends State { - String _info = 'Push'; - - @override - Widget build(BuildContext context) { - return InkWell( - onTap: () => {}, - splashColor: Colors.blueAccent, - highlightColor: Colors.orange, - onHighlightChanged: (v) => - setState(() => _info = 'onHighlightChanged:$v'), - radius: 50, - child: Container( - alignment: Alignment.center, - width: 180, - height: 50, - child: Text(_info), - ), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/InputDecorator/node1_base.dart b/packages/widgets/lib/StatefulWidget/InputDecorator/node1_base.dart deleted file mode 100644 index 0990e86dc..000000000 --- a/packages/widgets/lib/StatefulWidget/InputDecorator/node1_base.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/7/22 -/// contact me by email 1981462002@qq.com -/// 说明: 231 InputDecorator 输入装饰 在外层包裹输入的装饰,是TextField的底层核心组件之一,一般不单独使用。 -// { -// "widgetId": 231, -// "name": "InputDecorator基本使用", -// "priority": 1, -// "subtitle": "【decoration】 : 装饰对象 【InputDecoration】\n" -// "【textAlign】 : 文字对齐方式 【TextAlign】\n" -// "【child】 : 子组件 【Widget】", -// } - -class InputDecoratorDemo extends StatelessWidget { - const InputDecoratorDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(8.0), - child: InputDecorator( - decoration: const InputDecoration(), - child: EditableText( - controller: TextEditingController(text:'hello'), - focusNode: FocusNode(), - style: const TextStyle(fontSize: 12,color: Colors.black), - cursorColor: Colors.blue, - backgroundCursorColor: Colors.orange, - ), - ), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/InteractiveViewer/node1_base.dart b/packages/widgets/lib/StatefulWidget/InteractiveViewer/node1_base.dart deleted file mode 100644 index e944cb43d..000000000 --- a/packages/widgets/lib/StatefulWidget/InteractiveViewer/node1_base.dart +++ /dev/null @@ -1,56 +0,0 @@ - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/7/22 -/// contact me by email 1981462002@qq.com -/// 说明: 351 InteractiveViewer 交互视图 主要对移动、缩放等手势交互进行封装,简化使用,可指定移动边界、缩放比例、手势监听等。 -// { -// "widgetId": 351, -// "name": "InteractiveViewer基本使用", -// "priority": 1, -// "subtitle": "【alignPanAxis】 : 沿轴拖动 【bool】\n" -// "【boundaryMargin】 : 边界边距 【EdgeInsets】\n" -// "【panEnabled】 : 是否可平移 【bool】\n" -// "【scaleEnabled】 : 是否可缩放 【bool】\n" -// "【maxScale】 : 最大放大倍数 【double】\n" -// "【minScale】 : 最小缩小倍数 【double】\n" -// "【onInteractionEnd】 : 交互结束回调 【GestureScaleEndCallback】\n" -// "【onInteractionStart】 : 交互开始回调 【GestureScaleStartCallback】\n" -// "【onInteractionUpdate】 : 交互更新回调 【GestureScaleUpdateCallback】\n" -// "【child】 : 游标颜色 【Widget】", -// } - -class InteractiveViewerDemo extends StatelessWidget { - const InteractiveViewerDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - height: 150, - color: Colors.grey.withAlpha(33), - child: InteractiveViewer( - boundaryMargin: const EdgeInsets.all(40.0), - maxScale: 2.5, - minScale: 0.3, - panEnabled: true, - scaleEnabled: true, - child: Image.asset('assets/images/caver.webp'), - onInteractionStart: _onInteractionStart, - onInteractionUpdate: _onInteractionUpdate, - onInteractionEnd: _onInteractionEnd, - ), - ); - } - - void _onInteractionStart(ScaleStartDetails details) { - print('onInteractionStart----' + details.toString()); - } - - void _onInteractionUpdate(ScaleUpdateDetails details) { - print('onInteractionUpdate----' + details.toString()); - } - - void _onInteractionEnd(ScaleEndDetails details) { - print('onInteractionEnd----' + details.toString()); - } -} diff --git a/packages/widgets/lib/StatefulWidget/InteractiveViewer/node2_constrained.dart b/packages/widgets/lib/StatefulWidget/InteractiveViewer/node2_constrained.dart deleted file mode 100644 index 18b915e13..000000000 --- a/packages/widgets/lib/StatefulWidget/InteractiveViewer/node2_constrained.dart +++ /dev/null @@ -1,75 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/7/22 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 351, -// "name": "constrained属性测试", -// "priority": 2, -// "subtitle": "【constrained】 : 受约束的 【bool】", -// } - -class InteractiveViewerDemo2 extends StatelessWidget { - const InteractiveViewerDemo2({Key? key}) : super(key: key); - - final List colors = const [ - Colors.red, - Colors.yellow, - Colors.blue, - Colors.green - ]; - - final List colors2 = const [ - Colors.yellow, - Colors.blue, - Colors.green, - Colors.red - ]; - - @override - Widget build(BuildContext context) { - int _rowCount = 20; - int _columnCount = 4; - - return SizedBox( - width: 300, - height: 200, - child: InteractiveViewer( - constrained: false, - scaleEnabled: false, - child: Table( - columnWidths: { - for (int column = 0; column < _columnCount; column += 1) - column: const FixedColumnWidth(150.0), - }, - children: buildRows(_rowCount, _columnCount), - ), - ), - ); - } - - List buildRows(int rowCount, int columnCount) { - return [ - for (int row = 0; row < rowCount; row += 1) - TableRow( - children: [ - for (int column = 0; column < columnCount; column += 1) - Container( - margin: const EdgeInsets.all(2), - height: 50, - alignment: Alignment.center, - color: _colorful(row, column), - child: Text( - '($row,$column)', - style: const TextStyle(fontSize: 20, color: Colors.white), - ), - ), - ], - ), - ]; - } - - Color _colorful(int row, int column) => - row % 2 == 0 ? colors[column] : colors2[column]; -} diff --git a/packages/widgets/lib/StatefulWidget/InteractiveViewer/node3_controller.dart b/packages/widgets/lib/StatefulWidget/InteractiveViewer/node3_controller.dart deleted file mode 100644 index bf6d633d2..000000000 --- a/packages/widgets/lib/StatefulWidget/InteractiveViewer/node3_controller.dart +++ /dev/null @@ -1,152 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/7/22 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 351, -// "name": "变换控制器的使用", -// "priority": 3, -// "subtitle": "【transformationController】 : 变换控制器 【TransformationController】", -// } - -class InteractiveViewerDemo3 extends StatefulWidget { - const InteractiveViewerDemo3({Key? key}) : super(key: key); - - @override - _InteractiveViewerDemo3State createState() => _InteractiveViewerDemo3State(); -} - -class _InteractiveViewerDemo3State extends State - with SingleTickerProviderStateMixin { - final TransformationController _transformationController = - TransformationController(); - late Animation _animationReset; - late AnimationController _controllerReset; - - void _onAnimateReset() { - _transformationController.value = _animationReset.value; - if (!_controllerReset.isAnimating) { - _animationReset.removeListener(_onAnimateReset); - _controllerReset.reset(); - } - } - - void _animateResetInitialize() { - _controllerReset.reset(); - _animationReset = Matrix4Tween( - begin: _transformationController.value, - end: Matrix4.identity(), - ).animate(_controllerReset); - _animationReset.addListener(_onAnimateReset); - _controllerReset.forward(); - } - - void _animateResetStop() { - _controllerReset.stop(); - _animationReset.removeListener(_onAnimateReset); - _controllerReset.reset(); - } - - void _onInteractionStart(ScaleStartDetails details) { - if (_controllerReset.status == AnimationStatus.forward) { - _animateResetStop(); - } - } - - @override - void initState() { - super.initState(); - _controllerReset = AnimationController( - vsync: this, - duration: const Duration(milliseconds: 400), - ); - } - - @override - void dispose() { - _controllerReset.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Wrap( - direction: Axis.vertical, - spacing: 10, - crossAxisAlignment: WrapCrossAlignment.center, - alignment: WrapAlignment.center, - children: [ - Container( - height: 150, - color: Colors.grey.withAlpha(33), - child: InteractiveViewer( - boundaryMargin: const EdgeInsets.all(40), - transformationController: _transformationController, - minScale: 0.1, - maxScale: 1.8, - onInteractionStart: _onInteractionStart, - child: Image.asset('assets/images/caver.webp'), - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - _buildButton(), - _buildButton2(), - _buildButton3(), - ], - ) - ], - ); - } - - Widget _buildButton() { - return MaterialButton( - child: const Icon( - Icons.refresh, - color: Colors.white, - ), - color: Colors.green, - shape: const CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFDFDFDF)), - ), - onPressed: _animateResetInitialize); - } - - final double _x = 0.0; - - Widget _buildButton2() { - return MaterialButton( - child: const Icon( - Icons.navigate_before, - color: Colors.white, - ), - color: Colors.green, - shape: const CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFDFDFDF)), - ), - onPressed: () { - var temp = _transformationController.value.clone(); - temp.translate(_x - 4); - _transformationController.value = temp; - }); - } - - Widget _buildButton3() { - return MaterialButton( - child: const Icon( - Icons.navigate_next, - color: Colors.white, - ), - color: Colors.green, - shape: const CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFDFDFDF)), - ), - onPressed: () { - var temp = _transformationController.value.clone(); - temp.translate(_x + 4); - _transformationController.value = temp; - }); - } -} diff --git a/packages/widgets/lib/StatefulWidget/LicensePage/node1_base.dart b/packages/widgets/lib/StatefulWidget/LicensePage/node1_base.dart deleted file mode 100644 index f9e135232..000000000 --- a/packages/widgets/lib/StatefulWidget/LicensePage/node1_base.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 145, -// "name": 'LicensePage基本使用', -// "priority": 1, -// "subtitle": "【applicationIcon】 : 左上图标 【Widget】\n" -// "【applicationVersion】 : 版本号 【String】\n" -// "【applicationName】 : 应用名 【String】\n" -// "【applicationLegalese】 : 应用律术 【String】", -// } -class CustomLicensePage extends StatelessWidget { - const CustomLicensePage({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return SizedBox( - width: MediaQuery.of(context).size.width, - height: 400, - child: const LicensePage( - applicationIcon: FlutterLogo(), - applicationVersion: 'v0.0.1', - applicationName: 'Flutter Unit', - applicationLegalese: 'Copyright© 2018-2020 张风捷特烈', - ), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/LinearProgressIndicator/node1_base.dart b/packages/widgets/lib/StatefulWidget/LinearProgressIndicator/node1_base.dart deleted file mode 100644 index a124eb33c..000000000 --- a/packages/widgets/lib/StatefulWidget/LinearProgressIndicator/node1_base.dart +++ /dev/null @@ -1,44 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 47, -// "name": 'LinearProgressIndicator基本使用', -// "priority": 1, -// "subtitle": -// "【value】 : 进度 【double】\n" -// "【backgroundColor】 : 背景色 【Color】\n" -// "【valueColor】 : 进度颜色 【Animation】\n" -// " value为null时会不停循环", -// } -class CustomLinearProgressIndicator extends StatefulWidget { - const CustomLinearProgressIndicator({Key? key}) : super(key: key); - - @override - _CustomLinearProgressIndicatorState createState() => - _CustomLinearProgressIndicatorState(); -} - -class _CustomLinearProgressIndicatorState - extends State { - List data = [0.2, 0.4, 0.6, 0.8, null]; - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 10, - children: data - .map((e) => SizedBox( - width: 50, - height: 3, - child:LinearProgressIndicator( - value: e, - backgroundColor: Colors.grey.withAlpha(33), - valueColor: const AlwaysStoppedAnimation(Colors.orange), - ), - )) - .toList(), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/ListWheelScrollView/node1_base.dart b/packages/widgets/lib/StatefulWidget/ListWheelScrollView/node1_base.dart deleted file mode 100644 index b62dc3d32..000000000 --- a/packages/widgets/lib/StatefulWidget/ListWheelScrollView/node1_base.dart +++ /dev/null @@ -1,89 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 179, -// "name": 'ListWheelScrollView基本使用', -// "priority": 1, -// "subtitle": "【children】 : 子组件列表 【List】\n" -// "【perspective】 : 透视度 【double】\n" -// "【itemExtent】 : item高 【EdgeInsets】\n" -// "【onSelectedItemChanged】 : 选中回调 【ValueChanged 】", -// } -class CustomListWheelScrollView extends StatefulWidget { - const CustomListWheelScrollView({Key? key}) : super(key: key); - - @override - _CustomListWheelScrollViewState createState() => - _CustomListWheelScrollViewState(); -} - -class _CustomListWheelScrollViewState extends State { - final List data = [ - Colors.orange[50]!, - Colors.orange[100]!, - Colors.orange[200]!, - Colors.orange[300]!, - Colors.orange[400]!, - Colors.orange[500]!, - Colors.orange[600]!, - Colors.orange[700]!, - Colors.orange[800]!, - Colors.orange[900]!, - ]; - - Color _color = Colors.blue; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - _buildCircle(), - SizedBox( - height: 150, - width: 300, - child: ListWheelScrollView( - perspective: 0.006, - itemExtent: 50, - onSelectedItemChanged: (index) { - print('onSelectedItemChanged:$index'); - setState(() => _color = data[index]); - }, - children: data.map((color) => _buildItem(color)).toList(), - ), - ), - ], - ); - } - - Widget _buildCircle() => Container( - margin: const EdgeInsets.only(bottom: 5), - width: 30, - height: 30, - decoration: BoxDecoration(color: _color, shape: BoxShape.circle), - ); - - Widget _buildItem(Color color) { - return Container( - key: ValueKey(color) , - alignment: Alignment.center, - height: 50, - color: color, - child: Text( - colorString(color), - style: const TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2, - ) - ]), - ), - ); - } - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/packages/widgets/lib/StatefulWidget/LongPressDraggable/node1_base.dart b/packages/widgets/lib/StatefulWidget/LongPressDraggable/node1_base.dart deleted file mode 100644 index 727a5b430..000000000 --- a/packages/widgets/lib/StatefulWidget/LongPressDraggable/node1_base.dart +++ /dev/null @@ -1,99 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-22 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 105, -// "name": 'LongPressDraggable与DragTarget联用', -// "priority": 1, -// "subtitle": "【child】 : 孩子 【Widget】\n" -// "【feedback】 : 拖拽时的孩子 【Widget】\n" -// "【axis】 : 拖动的轴 【Axis】\n" -// "【data】 : 数据 【T】\n" -// "【onDragStarted】 : 开始拖拽 【Function()】\n" -// "【onDragEnd】 : 结束拖拽 【Function(DraggableDetails)】\n" -// "【onDragCompleted】 : 拖拽完成 【Function()】\n" -// "【onDraggableCanceled】 : 拖拽取消 【Function(Velocity,Offset)】", -// } -class CustomLongPressDraggable extends StatefulWidget { - const CustomLongPressDraggable({Key? key}) : super(key: key); - - @override - _CustomLongPressDraggableState createState() => - _CustomLongPressDraggableState(); -} - -class _CustomLongPressDraggableState extends State { - Color _color = Colors.grey; - String _info = 'DragTarget'; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Wrap( - children: _buildColors(), - spacing: 10, - ), - const SizedBox(height: 20), - _buildDragTarget() - ], - ); - } - - final List colors = const [ - Colors.red, - Colors.yellow, - Colors.blue, - Colors.green, - Colors.orange, - Colors.purple, - Colors.cyanAccent - ]; - - List _buildColors() => colors - .map( - (e) => LongPressDraggable( - onDragStarted: () => setState(() => _info = '开始拖拽'), - onDragEnd: (d) => setState(() => _info = '结束拖拽'), - onDragCompleted: () => _info = '拖拽完成', - onDraggableCanceled: (v, o) => _info = '拖拽取消', - child: Container( - width: 30, - height: 30, - alignment: Alignment.center, - child: Text( - colors.indexOf(e).toString(), - style: const TextStyle( - color: Colors.white, fontWeight: FontWeight.bold), - ), - decoration: BoxDecoration(color: e, shape: BoxShape.circle), - ), - data: e, - feedback: Container( - width: 25, - height: 25, - decoration: BoxDecoration(color: e, shape: BoxShape.circle), - )), - ) - .toList(); - - Widget _buildDragTarget() { - return DragTarget( - onAccept: (data) => setState(() { - _info='onAccept'; - _color = data; - }), - builder: (context, candidateData, rejectedData) => Container( - width: 150.0, - height: 50.0, - color: _color, - child: Center( - child: Text( - _info, - style: const TextStyle(color: Colors.white), - ), - ))); - } -} diff --git a/packages/widgets/lib/StatefulWidget/Material/node1_base.dart b/packages/widgets/lib/StatefulWidget/Material/node1_base.dart deleted file mode 100644 index 4e78c4405..000000000 --- a/packages/widgets/lib/StatefulWidget/Material/node1_base.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-26 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 160, -// "name": 'Material基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 子组件 【Widget】\n" -// "【type】 : 类型 【MaterialType】\n" -// "【elevation】 : 影深 【double】\n" -// "【shadowColor】 : 阴影颜色 【Color】\n" -// "【color】 : 颜色 【Color】", -// } -class CustomMaterial extends StatelessWidget { - const CustomMaterial({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 10, - runSpacing: 10, - children: MaterialType.values.map((e) => _buildMaterial(e)).toList()); - } - - Material _buildMaterial(MaterialType type) => Material( - shadowColor: Colors.blue, - type: type, - color: Colors.orange, - elevation: 3, - child: Container( - alignment: Alignment.center, - width: 100, - height: 60, - child: Text( - type.toString().split('.')[1], - style: const TextStyle(color: Colors.black), - ), - ), - ); -} diff --git a/packages/widgets/lib/StatefulWidget/Material/node2_shape.dart b/packages/widgets/lib/StatefulWidget/Material/node2_shape.dart deleted file mode 100644 index 58872e81d..000000000 --- a/packages/widgets/lib/StatefulWidget/Material/node2_shape.dart +++ /dev/null @@ -1,67 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-26 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 160, -// "name": 'Material的shape属性', -// "priority": 2, -// "subtitle": -// "【shape】 : 形状 【ShapeBorder】\n", -// } -class ShapeMaterial extends StatelessWidget { - const ShapeMaterial({Key? key}) : super(key: key); - - final Map shapeMap = const { - 'BorderDirectional': BorderDirectional( - top: BorderSide( - color: Colors.white, - ), - start: BorderSide(color: Colors.black, width: 15), - bottom: BorderSide( - color: Colors.white, - )), - 'Border': Border( - top: BorderSide(width: 5.0, color: Color(0xFFDFDFDF)), - left: BorderSide(width: 5.0, color: Color(0xFFDFDFDF)), - right: BorderSide(width: 5.0, color: Color(0xFF7F7F7F)), - bottom: BorderSide(width: 5.0, color: Color(0xFF7F7F7F)), - ), - 'Circle': CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFDFDFDF)), - ), - 'RoundedRectangleBorder': RoundedRectangleBorder( - side: BorderSide(width: 1.0, color: Colors.black), - borderRadius: BorderRadius.all(Radius.circular(15))), - 'ContinuousRectangleBorder': ContinuousRectangleBorder( - side: BorderSide.none, - borderRadius: BorderRadius.all(Radius.circular(40.0)), - ) - }; - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 10, - runSpacing: 10, - children: shapeMap.keys.map((e) => _buildMaterial(e)).toList(), - ); - } - - Material _buildMaterial(String type) => Material( - shadowColor: Colors.blue, - shape: shapeMap[type], - color: Colors.orange, - elevation: 3, - textStyle: const TextStyle(color: Colors.white), - child: Container( - alignment: Alignment.center, - width: 300, - height: 60, - child: Text( - type, - ), - ), - ); -} diff --git a/packages/widgets/lib/StatefulWidget/MaterialApp/node1_base.dart b/packages/widgets/lib/StatefulWidget/MaterialApp/node1_base.dart deleted file mode 100644 index a2b7b0137..000000000 --- a/packages/widgets/lib/StatefulWidget/MaterialApp/node1_base.dart +++ /dev/null @@ -1,131 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-17 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 65, -// "name": 'MaterialApp基本用法', -// "priority": 1, -// "subtitle": "【theme】 : 主题 【ThemeData】\n" -// "【title】 : 任务栏标题 【String】\n" -// "【debugShowCheckedModeBanner】 : 开启角标 【bool】\n" -// "【showPerformanceOverlay】 : 开启性能浮层 【bool】\n" -// "【debugShowMaterialGrid】 : 开启网格 【bool】\n" -// "【onGenerateRoute】 : 路由生成器 【RouteFactory】\n" -// "【home】 : 主页 【Widget】", -// } -class MaterialAppDemo extends StatefulWidget { - const MaterialAppDemo({Key? key}) : super(key: key); - - @override - _WidgetsAppDemoState createState() => _WidgetsAppDemoState(); -} - -class _WidgetsAppDemoState extends State { - bool _debugShowCheckedModeBanner = false; - bool _showPerformanceOverlay = false; - bool _debugShowMaterialGrid = false; - - @override - Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - _buildSwitchers(), - SizedBox( - height: 250, - child: MaterialApp( - debugShowCheckedModeBanner: _debugShowCheckedModeBanner, - showPerformanceOverlay: _showPerformanceOverlay, - debugShowMaterialGrid: _debugShowMaterialGrid, - home: const HomePage(), - ), - ), - ], - ); - } - - Widget _buildSwitchers() { - return DefaultTextStyle( - style: const TextStyle(color: Colors.blue), - child: Wrap( - spacing: 10, - children: [ - Column( - children: [ - Switch( - value: _showPerformanceOverlay, - onChanged: (v) { - setState(() { - _showPerformanceOverlay = v; - }); - }, - ), - const Text('性能浮层') - ], - ), - Column( - children: [ - Switch( - value: _debugShowCheckedModeBanner, - onChanged: (v) { - setState(() { - _debugShowCheckedModeBanner = v; - }); - }, - ), - const Text('开启角标') - ], - ), - Column( - children: [ - Switch( - value: _debugShowMaterialGrid, - onChanged: (v) { - setState(() { - _debugShowMaterialGrid = v; - }); - }, - ), - const Text('开启网格') - ], - ) - ], - ), - ); - } -} - -class HomePage extends StatefulWidget { - const HomePage({Key? key}) : super(key: key); - - @override - _HomePageState createState() => _HomePageState(); -} - -class _HomePageState extends State { - var _count = 0; - - @override - Widget build(BuildContext context) { - return Scaffold( - body: Container( - alignment: const Alignment(0, 0.7), - child: Text( - '你点击了$_count次', - style: const TextStyle(fontSize: 18, color: Colors.blue), - ), - ), - floatingActionButton: FloatingActionButton( - child: const Icon(Icons.add), - onPressed: () { - setState(() { - _count++; - }); - }, - ), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/MergeableMaterial/node1_base.dart b/packages/widgets/lib/StatefulWidget/MergeableMaterial/node1_base.dart deleted file mode 100644 index 184721676..000000000 --- a/packages/widgets/lib/StatefulWidget/MergeableMaterial/node1_base.dart +++ /dev/null @@ -1,83 +0,0 @@ - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 261 MergeableMaterial 可合并材料 用于展示 MergeableMaterialItem 的列表,包括 MaterialSlice(主体) 和 MaterialGap(分隔) 。 -// { -// "widgetId": 261, -// "name": 'MergeableMaterial基本使用', -// "priority": 1, -// "subtitle": -// "【elevation】 : 影深 【double】\n" -// "【hasDividers】 : 是否有分隔线 【bool】\n" -// "【dividerColor】 : 分隔线颜色 【Color】\n" -// "【mainAxis】 : 轴向 【Axis】\n" -// "【children】 : 子组件集 【List】", -// } - -class MergeableMaterialDemo extends StatefulWidget { - const MergeableMaterialDemo({Key? key}) : super(key: key); - - @override - _MergeableMaterialDemoState createState() => _MergeableMaterialDemoState(); -} - -class _MergeableMaterialDemoState extends State { - List items=[]; - - @override - void initState() { - super.initState(); - _init(20); - } - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 300, - child: SingleChildScrollView( - child: MergeableMaterial( - elevation: 1, - hasDividers: true, - dividerColor: Colors.red, - children: items, - ), - ), - ); - } - - final List data = [ - Colors.orange[50]!, - Colors.orange[100]!, - Colors.orange[200]!, - Colors.orange[300]!, - Colors.orange[400]!, - Colors.orange[500]!, - Colors.orange[600]!, - Colors.orange[700]!, - Colors.orange[800]!, - Colors.orange[900]! - ]; - - void _init(int count) { - for (int i = 0; i < count; i++) { - items.add(MaterialSlice( - key: ValueKey(i), - child: Container( - alignment: Alignment.center, - height: 60, - color: data[i % data.length], - child: Text(colorString(data[i % data.length])), - ))); - if(i!=count-1){ - items.add(MaterialGap( - key: ValueKey(i), - size: 5)); - } - } - } - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/packages/widgets/lib/StatefulWidget/MonthPicker/node1_base.dart b/packages/widgets/lib/StatefulWidget/MonthPicker/node1_base.dart deleted file mode 100644 index af9d3653f..000000000 --- a/packages/widgets/lib/StatefulWidget/MonthPicker/node1_base.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 135, -// "name": 'MonthPicker基本使用', -// "priority": 1, -// "subtitle": "【selectedDate】 : 选中日期 【DateTime】\n" -// "【firstDate】 : 最前日期限制 【DateTime】\n" -// "【lastDate】 : 最后日期限制 【DateTime】\n" -// "【onChanged】 : 点击回调 【Function(DateTime)】", -// } -class CustomMonthPicker extends StatelessWidget{ - const CustomMonthPicker({Key? key}) : super(key: key); - - final String info = - 'MonthPicker 月份期选择器于 Flutter3.0 退出历史舞台。取代者为 CalendarDatePicker 日历选择器。'; - - @override - Widget build(BuildContext context) { - return Container( - color: Colors.blue.withOpacity(0.1), - padding: const EdgeInsets.all(10), - margin: const EdgeInsets.all(10), - child: Text(info), - ); - } - - // final DateTime _date = DateTime.now(); - // - // @override - // Widget build(BuildContext context) { - // return SizedBox( - // height: 350, - // child: MonthPicker( - // selectedDate: _date, - // onChanged: (date) => setState(() => _date = date), - // firstDate: DateTime(2018), - // lastDate: DateTime(2030), - // ), - // ); - // } -} diff --git a/packages/widgets/lib/StatefulWidget/MouseRegion/node1_base.dart b/packages/widgets/lib/StatefulWidget/MouseRegion/node1_base.dart deleted file mode 100644 index e5f7a6d85..000000000 --- a/packages/widgets/lib/StatefulWidget/MouseRegion/node1_base.dart +++ /dev/null @@ -1,71 +0,0 @@ - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/7/22 -/// contact me by email 1981462002@qq.com -/// 说明: 293 MouseRegion 用于鼠标事件监听的组件,通常用于桌面和Web平台,可监听鼠标的移入、移除、移动事件。 -// { -// "widgetId": 293, -// "name": "MouseRegion基本使用", -// "priority": 1, -// "subtitle": "【onEnter】 : 移入事件 【PointerEnterEventListener】\n" -// "【onHover】: 移动事件 【PointerHoverEventListener】\n" -// "【onExit】: 移出事件 【PointerExitEventListener】", -// } - -class MouseRegionDemo extends StatefulWidget { - const MouseRegionDemo({Key? key}) : super(key: key); - - @override - _MouseRegionDemoState createState() => _MouseRegionDemoState(); -} - -class _MouseRegionDemoState extends State { - int _enterCounter = 0; - int _exitCounter = 0; - double x = 0.0; - double y = 0.0; - void _incrementEnter(PointerEvent details) { - setState(() { - _enterCounter++; - }); - } - void _incrementExit(PointerEvent details) { - setState(() { - _exitCounter++; - }); - } - void _updateLocation(PointerEvent details) { - setState(() { - x = details.position.dx; - y = details.position.dy; - }); - } - @override - Widget build(BuildContext context) { - return ConstrainedBox( - constraints: BoxConstraints.tight(const Size(300.0, 200.0)), - child: MouseRegion( - onEnter: _incrementEnter, - onHover: _updateLocation, - onExit: _incrementExit, - child: Container( - color: Colors.lightBlueAccent, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text('你的鼠标移入移除信息:'), - Text( - '$_enterCounter Entries\n$_exitCounter Exits', - style: Theme.of(context).textTheme.headline4, - ), - Text( - 'The cursor is here: (${x.toStringAsFixed(2)}, ${y.toStringAsFixed(2)})', - ), - ], - ), - ), - ), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/NavigationRail/node1_base.dart b/packages/widgets/lib/StatefulWidget/NavigationRail/node1_base.dart deleted file mode 100644 index b4913d376..000000000 --- a/packages/widgets/lib/StatefulWidget/NavigationRail/node1_base.dart +++ /dev/null @@ -1,118 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2022/7/23 -/// contact me by email 1981462002@qq.com -/// 说明: 358 NavigationRail 侧导航栏,一般用于桌面导航菜单。支持展开和收缩区域,可指定首尾位置组件。。 -// { -// "widgetId": 358, -// "name": "NavigationRail 基本使用", -// "priority": 1, -// "subtitle": "【destinations】 : 菜单数据列表 【List】\n" -// "【selectedIndex】: 激活索引 【int】\n" -// "【labelType】: 标签样式 【NavigationRailLabelType?】\n" -// "【onDestinationSelected】: 菜单点击事件 【ValueChanged?】", -// } - -class CustomNavigationRail extends StatefulWidget { - const CustomNavigationRail({Key? key}) : super(key: key); - - @override - State createState() => _CustomNavigationRailState(); -} - -class _CustomNavigationRailState extends State { - final PageController _controller = PageController(); - - @override - Widget build(BuildContext context) { - return Row( - children: [ - _NavigationRailDemo( - onDestinationSelected: _onDestinationSelected, - ), - Expanded( - child: PageView( - controller: _controller, - children: const [ - _TestContent(content: '消息'), - _TestContent(content: '视频会议'), - _TestContent(content: '通讯录'), - _TestContent(content: '云文档'), - _TestContent(content: '工作台'), - _TestContent(content: '日历'), - ], - )) - ], - ); - } - - void _onDestinationSelected(int value) { - _controller.jumpToPage(value); - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } -} - -class _TestContent extends StatelessWidget { - final String content; - - const _TestContent({Key? key, required this.content}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Center( - child: Text( - content, - style: const TextStyle(fontSize: 26), - )); - } -} - -class _NavigationRailDemo extends StatefulWidget { - final ValueChanged? onDestinationSelected; - - const _NavigationRailDemo({Key? key, this.onDestinationSelected}) - : super(key: key); - - @override - State<_NavigationRailDemo> createState() => _NavigationRailDemoState(); -} - -class _NavigationRailDemoState extends State<_NavigationRailDemo> { - int _selectIndex = 0; - - final List destinations = const [ - NavigationRailDestination( - icon: Icon(Icons.message_outlined), label: Text("消息")), - NavigationRailDestination( - icon: Icon(Icons.video_camera_back_outlined), label: Text("视频会议")), - NavigationRailDestination( - icon: Icon(Icons.book_outlined), label: Text("通讯录")), - NavigationRailDestination( - icon: Icon(Icons.cloud_upload_outlined), label: Text("云文档")), - NavigationRailDestination( - icon: Icon(Icons.games_sharp), label: Text("工作台")), - NavigationRailDestination( - icon: Icon(Icons.calendar_month), label: Text("日历")) - ]; - - @override - Widget build(BuildContext context) { - return NavigationRail( - onDestinationSelected: _onDestinationSelected, - labelType: NavigationRailLabelType.all, - destinations: destinations, - selectedIndex: _selectIndex, - ); - } - - void _onDestinationSelected(int value) { - _selectIndex = value; - setState(() {}); - widget.onDestinationSelected?.call(value); - } -} diff --git a/packages/widgets/lib/StatefulWidget/NavigationRail/node2_extend.dart b/packages/widgets/lib/StatefulWidget/NavigationRail/node2_extend.dart deleted file mode 100644 index 61638bdb0..000000000 --- a/packages/widgets/lib/StatefulWidget/NavigationRail/node2_extend.dart +++ /dev/null @@ -1,146 +0,0 @@ - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2022/7/23 -/// contact me by email 1981462002@qq.com -/// 说明: 358 NavigationRail 侧导航栏,一般用于桌面导航菜单。支持展开和收缩区域,可指定首尾位置组件。。 -// { -// "widgetId": 358, -// "name": "NavigationRail 折叠效果", -// "priority": 2, -// "subtitle": "【elevation】 : 影深 【double】\n" -// "【leading】: 首组件 【Widget?】\n" -// "【trailing】: 尾组件 【Widget?】\n" -// "【extended】: 是否展开 【bool】", -// } - -class ExtendableNavigationRail extends StatefulWidget { - const ExtendableNavigationRail({Key? key}) : super(key: key); - - @override - State createState() => _ExtendableNavigationRailState(); -} - -class _ExtendableNavigationRailState extends State { - final PageController _controller = PageController(); - - @override - Widget build(BuildContext context) { - return Row( - children: [ - ExtendableNavigation( - onDestinationSelected: _onDestinationSelected, - ), - Expanded( - child: PageView( - controller: _controller, - children: const [ - _TestContent(content: '消息'), - _TestContent(content: '视频会议'), - _TestContent(content: '通讯录'), - _TestContent(content: '云文档'), - _TestContent(content: '工作台'), - _TestContent(content: '日历'), - ], - )) - ], - ); - } - - void _onDestinationSelected(int value) { - _controller.jumpToPage(value); - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } -} - -class _TestContent extends StatelessWidget { - final String content; - - const _TestContent({Key? key, required this.content}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Center( - child: Text( - content, - style: const TextStyle(fontSize: 26), - )); - } -} - -class ExtendableNavigation extends StatefulWidget { - final ValueChanged? onDestinationSelected; - - const ExtendableNavigation({Key? key, this.onDestinationSelected}) - : super(key: key); - - @override - State createState() => _ExtendableNavigationState(); -} - -class _ExtendableNavigationState extends State { - int _selectIndex = 0; - bool _extended = false; - - final List destinations = const [ - NavigationRailDestination( - icon: Icon(Icons.message_outlined), label: Text("消息")), - NavigationRailDestination( - icon: Icon(Icons.video_camera_back_outlined), label: Text("视频会议")), - NavigationRailDestination( - icon: Icon(Icons.book_outlined), label: Text("通讯录")), - NavigationRailDestination( - icon: Icon(Icons.cloud_upload_outlined), label: Text("云文档")), - NavigationRailDestination( - icon: Icon(Icons.games_sharp), label: Text("工作台")), - NavigationRailDestination( - icon: Icon(Icons.calendar_month), label: Text("日历")) - ]; - - Widget buildLeading() { - return GestureDetector( - onTap: _toggleExtended, - child: const Icon( - Icons.menu_open, - color: Colors.grey, - )); - } - - @override - Widget build(BuildContext context) { - return NavigationRail( - leading: buildLeading(), - extended: _extended, - elevation: 1, - trailing: const Expanded( - child: Align( - alignment: Alignment.bottomCenter, - child: Padding( - padding: EdgeInsets.only(bottom: 20.0), - child: FlutterLogo(), - ), - ), - ), - onDestinationSelected: _onDestinationSelected, - destinations: destinations, - selectedIndex: _selectIndex, - ); - } - - void _onDestinationSelected(int value) { - _selectIndex = value; - setState(() {}); - widget.onDestinationSelected?.call(value); - } - - void _toggleExtended() { - setState(() { - _extended = !_extended; - }); - } -} diff --git a/packages/widgets/lib/StatefulWidget/NavigationRail/node3_dark.dart b/packages/widgets/lib/StatefulWidget/NavigationRail/node3_dark.dart deleted file mode 100644 index 02315f817..000000000 --- a/packages/widgets/lib/StatefulWidget/NavigationRail/node3_dark.dart +++ /dev/null @@ -1,176 +0,0 @@ - -import 'package:flutter/material.dart'; - - - -// minWidth: 72, -// minExtendedWidth: 200 , -// unselectedIconTheme: const IconThemeData(color: textColor) , -// selectedIconTheme: const IconThemeData(color: activeColor) , -// unselectedLabelTextStyle: labelStyle, -// selectedLabelTextStyle: labelStyle, - -/// create by 张风捷特烈 on 2022/7/23 -/// contact me by email 1981462002@qq.com -/// 说明: 358 NavigationRail 侧导航栏,一般用于桌面导航菜单。支持展开和收缩区域,可指定首尾位置组件。。 -// { -// "widgetId": 358, -// "name": "NavigationRail 样式", -// "priority": 3, -// "subtitle": "【useIndicator】 : 是否显示指示器 【bool】\n" -// "【indicatorColor】: 指示器颜色 【Color?】\n" -// "【backgroundColor】: 背景色 【Color?】\n" -// "【labelType】: 标签样式 【NavigationRailLabelType?】\n" -// "【selectedIconTheme】: 选中图标样式 【IconThemeData?】\n" -// "【unselectedIconTheme】: 未选中图标样式 【IconThemeData?】\n" -// "【selectedLabelTextStyle】: 选中文字样式 【TextStyle?】\n" -// "【unselectedLabelTextStyle】: 未选中文字样式 【TextStyle?】\n" -// "【minExtendedWidth】: 展开宽度 【double?】\n" -// "【minWidth】: 未展开宽度 【double?】", -// } - - -class DarkNavigationRail extends StatefulWidget { - const DarkNavigationRail({Key? key}) : super(key: key); - - @override - State createState() => _DarkNavigationRailState(); -} - -class _DarkNavigationRailState extends State { - final PageController _controller = PageController(); - - @override - Widget build(BuildContext context) { - return Row( - children: [ - DarkExtendableNavigation( - onDestinationSelected: _onDestinationSelected, - ), - Expanded( - child: PageView( - controller: _controller, - children: const [ - _TestContent(content: '消息'), - _TestContent(content: '视频会议'), - _TestContent(content: '通讯录'), - _TestContent(content: '云文档'), - _TestContent(content: '工作台'), - _TestContent(content: '日历'), - ], - )) - ], - ); - } - - void _onDestinationSelected(int value) { - _controller.jumpToPage(value); - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } -} - -class _TestContent extends StatelessWidget { - final String content; - - const _TestContent({Key? key, required this.content}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Center( - child: Text( - content, - style: const TextStyle(fontSize: 26), - )); - } -} - -class DarkExtendableNavigation extends StatefulWidget { - final ValueChanged? onDestinationSelected; - - const DarkExtendableNavigation({Key? key, this.onDestinationSelected}) - : super(key: key); - - @override - State createState() => _DarkExtendableNavigationState(); -} - -class _DarkExtendableNavigationState extends State { - int _selectIndex = 0; - bool _extended = false; - - final List destinations = const [ - NavigationRailDestination( - icon: Icon(Icons.message_outlined), label: Text("消息")), - NavigationRailDestination( - icon: Icon(Icons.video_camera_back_outlined), label: Text("视频会议")), - NavigationRailDestination( - icon: Icon(Icons.book_outlined), label: Text("通讯录")), - NavigationRailDestination( - icon: Icon(Icons.cloud_upload_outlined), label: Text("云文档")), - NavigationRailDestination( - icon: Icon(Icons.games_sharp), label: Text("工作台")), - NavigationRailDestination( - icon: Icon(Icons.calendar_month), label: Text("日历")) - ]; - - Widget buildLeading() { - return GestureDetector( - onTap: _toggleExtended, - child: const Icon( - Icons.menu_open, - color: Colors.grey, - )); - } - - @override - Widget build(BuildContext context) { - const Color textColor = Color(0xffcfd1d7); - const Color activeColor = Colors.white; - const TextStyle labelStyle = TextStyle(color: textColor,fontSize: 11); - - return NavigationRail( - leading: buildLeading(), - extended: _extended, - labelType: NavigationRailLabelType.none, - useIndicator: true, - indicatorColor: Colors.blue, - elevation: 1, - backgroundColor: const Color(0xff324465), - minWidth: 72, - minExtendedWidth: 200 , - unselectedIconTheme: const IconThemeData(color: textColor) , - selectedIconTheme: const IconThemeData(color: activeColor) , - unselectedLabelTextStyle: labelStyle, - selectedLabelTextStyle: labelStyle, - trailing: const Expanded( - child: Align( - alignment: Alignment.bottomCenter, - child: Padding( - padding: EdgeInsets.only(bottom: 20.0), - child: FlutterLogo(), - ), - ), - ), - onDestinationSelected: _onDestinationSelected, - destinations: destinations, - selectedIndex: _selectIndex, - ); - } - - void _onDestinationSelected(int value) { - _selectIndex = value; - setState(() {}); - widget.onDestinationSelected?.call(value); - } - - void _toggleExtended() { - setState(() { - _extended = !_extended; - }); - } -} diff --git a/packages/widgets/lib/StatefulWidget/Navigator/node1_base.dart b/packages/widgets/lib/StatefulWidget/Navigator/node1_base.dart deleted file mode 100644 index 5daa0c0a0..000000000 --- a/packages/widgets/lib/StatefulWidget/Navigator/node1_base.dart +++ /dev/null @@ -1,196 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/7/19 -/// contact me by email 1981462002@qq.com -/// 说明: -/// -// { -// "widgetId": 232, -// "name": 'Navigator基本用法', -// "priority": 1, -// "subtitle": -// "【initialRoute】 : 最初显示路由 【String】\n" -// "【onGenerateRoute】 : 路由生成器 【RouteFactory】\n" -// "【observers】 : 路由监听器 【List】\n" -// "【onPopPage】 : 出栈回调 【PopPageCallback】", -// } -class NavigatorDemo extends StatelessWidget { - const NavigatorDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 200, - width: 300, - child: Navigator( - onPopPage: _onPopPage, - initialRoute: '/home-content', - onGenerateRoute: _onGenerateRoute, - observers: [TolyNavigatorObservers()], - ), - ); - } - - Route _onGenerateRoute(RouteSettings settings) { - switch (settings.name) { - case '/home-content': - return MaterialPageRoute( - builder: (_) => const HomeContent(), settings: settings); - case "/red": - return MaterialPageRoute(builder: (_) => const RedPage(), settings: settings); - case "/yellow": - return MaterialPageRoute( - builder: (_) => const YellowPage(), settings: settings); - case "/green": - return MaterialPageRoute( - builder: (_) => const GreenPage(), settings: settings); - default: - return MaterialPageRoute( - builder: (_) => const HomeContent(), settings: settings); - } - } - - bool _onPopPage(Route route, result) { - print('----_onPopPage-----'); - return true; - } -} - -//路由监听器 -class TolyNavigatorObservers extends NavigatorObserver { - @override - void didPush(Route route, Route? previousRoute) { - print( - '--didPush:--route:--${route.settings}--previousRoute:--${previousRoute?.settings}'); - } - - @override - void didStopUserGesture() { - print('--didStopUserGesture:--'); - } - - @override - void didStartUserGesture(Route route, Route? previousRoute) { - print( - '--didStartUserGesture:--route:--${route.settings}--previousRoute:--${previousRoute?.settings}'); - } - - @override - void didReplace({Route? newRoute, Route? oldRoute}) { - print( - '--didReplace:--newRoute:--${newRoute?.settings}--oldRoute:--${oldRoute?.settings}'); - } - - @override - void didRemove(Route? route, Route? previousRoute) { - print( - '--didRemove:--route:--${route?.settings}--previousRoute:--${previousRoute?.settings}'); - } - - @override - void didPop(Route route, Route? previousRoute) { - print( - '--didPop:--route:--${route.settings}--previousRoute:--${previousRoute?.settings}'); - } -} - -class HomeContent extends StatelessWidget { - const HomeContent({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - ElevatedButton( - style: TextButton.styleFrom( - backgroundColor: Colors.red, - shape: const CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFDFDFDF)), - ), - ), - child: const Text('to red'), - onPressed: () { - Navigator.pushNamed(context, '/red'); - }, - ), - ElevatedButton( - style: TextButton.styleFrom( - backgroundColor: Colors.yellow, - shape: const CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFDFDFDF)), - ), - ), - child: const Text('to yellow'), - onPressed: () { - Navigator.pushNamed(context, '/yellow'); - }, - ), - ElevatedButton( - style: TextButton.styleFrom( - backgroundColor: Colors.green, - shape: const CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFDFDFDF)), - ), - ), - child: const Text('to yellow'), - onPressed: () { - Navigator.pushNamed(context, '/green'); - }, - ) - ], - ), - ], - ); - } -} - -class RedPage extends StatelessWidget { - const RedPage({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text("RedPage"), - ), - body: Container( - color: Colors.red, - ), - ); - } -} - -class YellowPage extends StatelessWidget { - const YellowPage({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text("YellowPage"), - ), - body: Container( - color: Colors.yellow, - ), - ); - } -} - -class GreenPage extends StatelessWidget { - const GreenPage({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text("GreenPage"), - ), - body: Container( - color: Colors.green, - ), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/NestedScrollView/node1_base.dart b/packages/widgets/lib/StatefulWidget/NestedScrollView/node1_base.dart deleted file mode 100644 index 241090fc6..000000000 --- a/packages/widgets/lib/StatefulWidget/NestedScrollView/node1_base.dart +++ /dev/null @@ -1,103 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/6/16 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 251, -// "name": 'NestedScrollView基本用法', -// "priority": 1, -// "subtitle": -// "【controller】 : 滑动控制器 【ScrollController】\n" -// "【scrollDirection】 : 滑动方向 【Axis】\n" -// "【reverse】 : 是否反向 【bool】\n" -// "【physics】 : 滑顶样式 【ScrollPhysics】\n" -// "【dragStartBehavior】 : 开始拖动行为 【DragStartBehavior】\n" -// "【headerSliverBuilder】 : *头部构造器 【NestedScrollViewHeaderSliversBuilder】\n" -// "【body】 : *内容 【Widget】", -// } - -class NestedScrollViewDemo extends StatelessWidget { - final List _tabs = const ['风神传', '封妖志', "幻将录", "永恒传说"]; - - const NestedScrollViewDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return SizedBox( - width: MediaQuery.of(context).size.width, - height: MediaQuery.of(context).size.height - 200, - child: Scaffold( - body: DefaultTabController( - length: _tabs.length, - child: NestedScrollView( - headerSliverBuilder: - (BuildContext context, bool innerBoxIsScrolled) { - return [ - SliverOverlapAbsorber( - handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), - sliver: SliverAppBar( - title: const Text('旷古奇书'), - pinned: true, - elevation: 6, //影深 - expandedHeight: 220.0, - forceElevated: innerBoxIsScrolled, //为true时展开有阴影 - flexibleSpace: FlexibleSpaceBar( - background: Image.asset( - "assets/images/wy_300x200_filter.webp", - fit: BoxFit.cover, - ), - ), - bottom: TabBar( - tabs: _tabs - .map((String name) => Tab(text: name,)) - .toList(), - ), - ), - ), - ]; - }, - body: _buildTabBarView(), - ), - ), - )); - } - - Widget _buildTabBarView() { - return TabBarView( - children: _tabs.map((String name) { - return SafeArea( - top: false, - bottom: false, - child: Builder( - builder: (BuildContext context) { - return CustomScrollView( - key: PageStorageKey(name), - slivers: [ - SliverOverlapInjector( - handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), - ), - SliverPadding( - padding: const EdgeInsets.all(8.0), - sliver: SliverFixedExtentList( - itemExtent: 48.0, - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) { - return ListTile( - title: Text('《$name》 第 $index章'), - ); - }, - childCount: 50, - ), - ), - ), - ], - ); - }, - ), - ); - }).toList(), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/OutlinedButton/node1_base.dart b/packages/widgets/lib/StatefulWidget/OutlinedButton/node1_base.dart deleted file mode 100644 index ec7a27c90..000000000 --- a/packages/widgets/lib/StatefulWidget/OutlinedButton/node1_base.dart +++ /dev/null @@ -1,44 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 355 OutlinedButton Material风格的边线按钮,表现和OutlineButton类似。可通过样式更改边框、颜色、阴影等属性。 -// { -// "widgetId": 355, -// "name": 'OutlinedButton基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 是否具有滚动主体 【Widget】\n" -// "【onPressed】 : 点击事件 【VoidCallback】\n" -// "【onLongPress】 : 长按事件 【VoidCallback】", -// } - -class OutlinedButtonDemo extends StatelessWidget { - const OutlinedButtonDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - alignment: Alignment.center, - height: 60, - child: Wrap( - spacing: 20, - children: [ - OutlinedButton( - child: const Text('OutlinedButton'), - onPressed: _onPressed, - onLongPress: _onLongPress, - ), - const OutlinedButton( - child: Text('禁用按钮'), - onPressed: null, - onLongPress: null, - ), - ], - )); - } - - void _onPressed() {} - - void _onLongPress() {} -} diff --git a/packages/widgets/lib/StatefulWidget/OutlinedButton/node2_style.dart b/packages/widgets/lib/StatefulWidget/OutlinedButton/node2_style.dart deleted file mode 100644 index 6cf13aaa2..000000000 --- a/packages/widgets/lib/StatefulWidget/OutlinedButton/node2_style.dart +++ /dev/null @@ -1,60 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 355, -// "name": 'OutlinedButton样式', -// "priority": 2, -// "subtitle": -// "【style】 : 按钮样式 【ButtonStyle】\n" -// "【focusNode】 : 焦点 【FocusNode】\n" -// "【clipBehavior】 : 裁剪行为 【Clip】\n" -// "【autofocus】 : 自动聚焦 【bool】", -// } - -class OutlinedButtonStyleDemo extends StatelessWidget { - const OutlinedButtonStyleDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - alignment: Alignment.center, - child: Wrap( - spacing: 10, - children: [ - OutlinedButton( - style: TextButton.styleFrom( - backgroundColor: Colors.orange, - primary: Colors.white, - elevation: 2, - shadowColor: Colors.orangeAccent), - child: const Text('ElevatedButton样式'), - onPressed: _onPressed, - onLongPress: _onLongPress, - ), - OutlinedButton( - style: TextButton.styleFrom( - backgroundColor: Colors.white, - primary: Colors.black, - side: const BorderSide(color: Colors.blue,width: 1), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10)) - ), - // elevation: 2, - shadowColor: Colors.orangeAccent), - child: const Text('ElevatedButton边线'), - autofocus: false, - onPressed: _onPressed, - onLongPress: _onLongPress, - ), - ], - ), - ); - } - - void _onPressed() {} - - void _onLongPress() {} -} diff --git a/packages/widgets/lib/StatefulWidget/Overlay/node1_base.dart b/packages/widgets/lib/StatefulWidget/Overlay/node1_base.dart deleted file mode 100644 index d8a9aed29..000000000 --- a/packages/widgets/lib/StatefulWidget/Overlay/node1_base.dart +++ /dev/null @@ -1,107 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 182, -// "name": 'Overlay基本使用', -// "priority": 1, -// "subtitle": -// " Overlay.of(context).insert插入全局组件", -// } - -class CustomOverlay extends StatelessWidget { - const CustomOverlay({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Wrap( - children: [ - SizedBox( - height: 50, - child: RawMaterialButton( - elevation: 2, - shape: const CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFDFDFDF)), - ), - fillColor: Colors.blue, - splashColor: Colors.orange, - textStyle: const TextStyle(color: Colors.white), - child: const Icon(Icons.add), - onPressed: ()=>showFloating(context), - ), - ), - const SizedBox( - height: 50, - child: RawMaterialButton( - elevation: 2, - shape: CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFDFDFDF)), - ), - fillColor: Colors.red, - splashColor: Colors.orange, - textStyle: TextStyle(color: Colors.white), - child: Icon(Icons.remove), - onPressed: hideFloating, - ), - ), - ], - ); - } -} - -bool show = false; -Offset offset = const Offset(200, 200); - -const double radius = 60; - -var entry = OverlayEntry( - builder: (context) => Stack( - children: [ - Positioned( - left: offset.dx, - top: offset.dy, - child: _buildFloating(), - ), - ], - )); - -///绘制悬浮控件 -_buildFloating() => GestureDetector( - onPanDown: (details) { - offset = details.globalPosition - const Offset(radius / 2, radius / 2); - entry.markNeedsBuild(); - }, - onPanUpdate: (DragUpdateDetails details) { - offset = offset + details.delta; - entry.markNeedsBuild(); - }, - onLongPress: hideFloating, - child: Material( - color: Colors.transparent, - child: Container( - height: radius, - width: radius, - alignment: Alignment.center, - decoration: const BoxDecoration( - shape: BoxShape.circle, - image: DecorationImage( - image: AssetImage('assets/images/icon_head.webp')), - ), - ), - )); - -showFloating(BuildContext context) { - if (!show) { - Overlay.of(context)?.insert(entry); - show = true; - } -} - -hideFloating() { - if (show) { - entry.remove(); - show = false; - } -} diff --git a/packages/widgets/lib/StatefulWidget/PageView/node1_base.dart b/packages/widgets/lib/StatefulWidget/PageView/node1_base.dart deleted file mode 100644 index 94a33414b..000000000 --- a/packages/widgets/lib/StatefulWidget/PageView/node1_base.dart +++ /dev/null @@ -1,63 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-28 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 165, -// "name": 'PageView基本使用', -// "priority": 1, -// "subtitle": -// "【children】 : 子组件列表 【List】\n" -// "【onPageChanged】 : 点击事件 【ValueChanged】", -// } -class CustomPageView extends StatelessWidget { - CustomPageView({Key? key}) : super(key: key); - - final List data = [ - Colors.green[50]!, - Colors.green[100]!, - Colors.green[200]!, - Colors.green[300]!, - Colors.green[400]!, - Colors.green[500]!, - Colors.green[600]!, - Colors.green[700]!, - Colors.green[800]!, - Colors.green[900]!, - ]; - - TextStyle get textStyle => - const TextStyle(color: Colors.white, fontSize: 24, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2, - ), - ]); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 150, - child: PageView( - onPageChanged: (position) => print(position), - children: data - .map((color) => Container( - alignment: Alignment.center, - width: 90, - color: color, - child: Text( - colorString(color), - style: textStyle, - ), - )) - .toList(), - ), - ); - } - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/packages/widgets/lib/StatefulWidget/PageView/node2_direction.dart b/packages/widgets/lib/StatefulWidget/PageView/node2_direction.dart deleted file mode 100644 index b2056220a..000000000 --- a/packages/widgets/lib/StatefulWidget/PageView/node2_direction.dart +++ /dev/null @@ -1,67 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-28 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 165, -// "name": 'PageView滑动方向', -// "priority": 2, -// "subtitle": -// "【scrollDirection】 : 滑动方向 【Axis】\n" -// "【reverse】 : 是否反向 【bool】", -// } -class DirectionPageView extends StatelessWidget { - DirectionPageView({Key? key}) : super(key: key); - - final List data = [ - Colors.orange[50]!, - Colors.orange[100]!, - Colors.orange[200]!, - Colors.orange[300]!, - Colors.orange[400]!, - Colors.orange[500]!, - Colors.orange[600]!, - Colors.orange[700]!, - Colors.orange[800]!, - Colors.orange[900]!, - ]; - - TextStyle get textStyle => - const TextStyle(color: Colors.white, fontSize: 24, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2, - ), - ]); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 150, - child: PageView( - scrollDirection: Axis.vertical, - reverse: true, - onPageChanged: (position) { - print(position); - }, - children: data - .map((color) => Container( - alignment: Alignment.center, - width: 90, - color: color, - child: Text( - colorString(color), - style: textStyle, - ), - )) - .toList(), - ), - ); - } - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/packages/widgets/lib/StatefulWidget/PageView/node3_controller.dart b/packages/widgets/lib/StatefulWidget/PageView/node3_controller.dart deleted file mode 100644 index fea68213c..000000000 --- a/packages/widgets/lib/StatefulWidget/PageView/node3_controller.dart +++ /dev/null @@ -1,86 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-28 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 165, -// "name": 'PageView控制器简单实用', -// "priority": 3, -// "subtitle": -// "【controller】 : 页面控制器 【PageController】", -// } -class CtrlPageView extends StatefulWidget { - const CtrlPageView({Key? key}) : super(key: key); - - @override - _CtrlPageViewState createState() => _CtrlPageViewState(); -} - -class _CtrlPageViewState extends State { - final List data = [ - Colors.orange[50]!, - Colors.orange[100]!, - Colors.orange[200]!, - Colors.orange[300]!, - Colors.orange[400]!, - Colors.orange[500]!, - Colors.orange[600]!, - Colors.orange[700]!, - Colors.orange[800]!, - Colors.orange[900]!, - ]; - - late PageController _controller; - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - @override - void initState() { - super.initState(); - _controller=PageController( - viewportFraction: 0.8, - initialPage: (data.length/2).round() - ); - } - TextStyle get textStyle => - const TextStyle(color: Colors.white, fontSize: 24, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2, - ), - ]); - @override - Widget build(BuildContext context) { - return SizedBox( - height: 150, - child: PageView( - controller: _controller, - onPageChanged: (position) { - print(position); - }, - children: data - .map((color) => - Container( - alignment: Alignment.center, - width: 90, - color: color, - child: Text( - colorString(color), - style: textStyle, - ), - )) - .toList(), - ), - ); - } - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} \ No newline at end of file diff --git a/packages/widgets/lib/StatefulWidget/PaginatedDataTable/node1_base.dart b/packages/widgets/lib/StatefulWidget/PaginatedDataTable/node1_base.dart deleted file mode 100644 index e7208c43f..000000000 --- a/packages/widgets/lib/StatefulWidget/PaginatedDataTable/node1_base.dart +++ /dev/null @@ -1,225 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-04-01 -/// contact me by email 1981462002@qq.com -/// 说明: 235 PaginatedDataTable 可分页表格 -/// 一个功能丰富的可分页表格组件,可指定分页数、排列、页码前后切换。 -/// link: 110,102 -// { -// "widgetId": 235 , -// "name": 'PaginatedDataTable 使用', -// "priority": 1, -// "subtitle": -// "【header】 : 表名 【Widget】\n" -// "【rowsPerPage】 : 每页记录数 【int】\n" -// "【actions】 : 操作组件 【List】\n" -// "【columns】 : 数据列 【List】\n" -// "【sortColumnIndex】 : 排序列索引 【int】\n" -// "【sortAscending】 : 是否升序 【bool】\n" -// "【onSelectAll】 : 全选回调 【ValueSetter】\n" -// "【onRowsPerPageChanged】 : 分页改变监听 【ValueChanged】\n" -// "【availableRowsPerPage】 : 可用分页列表 【List】\n" -// "【source】 : 数据源 【DataTableSource】", -// } -class PaginatedDataTableDemo extends StatefulWidget { - const PaginatedDataTableDemo({Key? key}) : super(key: key); - - @override - State createState() => _PaginatedDataTableDemoState(); -} - -class _PaginatedDataTableDemoState extends State { - int _rowsPerPage = 5; - - int _sortColumnIndex = 0; - bool _sortAscending = true; - - final DessertDataSource _dessertsDataSource = DessertDataSource(); - - void sort( - Comparable Function(HeroInfo d) getField, - int columnIndex, - bool ascending, - ) { - _dessertsDataSource.sort(getField, ascending); - setState(() { - _sortColumnIndex = columnIndex; - _sortAscending = ascending; - }); - } - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 300, - width: 350, - child: SingleChildScrollView( - child: PaginatedDataTable( - actions: const [ - IconButton(icon: Icon(Icons.add), onPressed: null), - ], - header: const Text( - '《旷古奇书》-角色预设', - style: TextStyle(color: Colors.blue), - ), - rowsPerPage: _rowsPerPage, - availableRowsPerPage: const [5, 8, 10, 15], - onRowsPerPageChanged: (int? value) { - setState(() { - _rowsPerPage = value ?? 0; - }); - }, - sortColumnIndex: _sortColumnIndex, - sortAscending: _sortAscending, - onSelectAll: _dessertsDataSource._selectAll, - columns: [ - DataColumn( - label: const Text('角色名称'), - onSort: (int columnIndex, bool ascending) => sort( - (HeroInfo d) => d.name, columnIndex, ascending)), - DataColumn( - label: const Text('主场卷部'), - tooltip: '人物主要出场的作品.', - numeric: true, - onSort: (int columnIndex, bool ascending) => sort( - (HeroInfo d) => d.calories, columnIndex, ascending)), - DataColumn( - label: const Text('种族'), - numeric: true, - onSort: (int columnIndex, bool ascending) => sort( - (HeroInfo d) => d.fat, columnIndex, ascending)), - DataColumn( - label: const Text('性别'), - numeric: true, - onSort: (int columnIndex, bool ascending) => sort( - (HeroInfo d) => d.carbs, columnIndex, ascending)), - ], - source: _dessertsDataSource), - )); - } -} - -class HeroInfo { - HeroInfo(this.name, this.calories, this.fat, this.carbs); - - final String name; - final String calories; - final String fat; - final String carbs; - bool selected = false; -} - -class DessertDataSource extends DataTableSource { - final List _desserts = [ - HeroInfo('捷特', '《幻将录》', "人族", "男"), - HeroInfo('龙少', '《幻将录》', "人族", "男"), - HeroInfo('巫缨', '《幻将录》', "人族", "女"), - HeroInfo('林兮', '《幻将录》', "人族", "男"), - HeroInfo('九方玄玉', '《风神传》', "神族", "男"), - HeroInfo('七日洪荒', '《风神传》', "魔族", "男"), - HeroInfo('林昔瑶', '《封妖志》', "鬼族", "女"), - HeroInfo('林兮鬼帝', '《封妖志》', "鬼族", "男"), - HeroInfo('艾隆', '《封妖志》', "鬼族", "男"), - HeroInfo('语熙华', '《风神传》', "道族", "男"), - HeroInfo('雪玉宛如', '《幻将录》', "人族", "女"), - HeroInfo('破千', '《幻将录》', "人族", "男"), - HeroInfo('浪封', '《幻将录》', "人族", "男"), - HeroInfo('虎翼穷奇', '《封妖志》', "妖族", "男"), - HeroInfo('凯', '《幻将录》', "人族", "男"), - HeroInfo('荆棘', '《幻将录》', "人族", "女"), - HeroInfo('龙右', '《幻将录》', "人族", "男"), - HeroInfo('梦千', '《幻将录》', "人族", "男"), - HeroInfo('梦小梦', '《幻将录》', "人族", "女"), - HeroInfo('梦瞳', '《幻将录》', "人族", "男"), - HeroInfo('十戈', '《幻将录》', "人族", "男"), - HeroInfo('计画天', '《幻将录》', "人族", "女"), - HeroInfo('士方', '《幻将录》', "人族", "男"), - HeroInfo('巫妻孋', '《幻将录》', "人族", "女"), - HeroInfo('木时黎', '《永恒传说》', "人族", "男"), - HeroInfo('木艾奇', '《永恒传说》', "人族", "男"), - HeroInfo('张风', '《永恒传说》', "人族", "男"), - HeroInfo('薛剑儿', '《永恒传说》', "人族", "男"), - HeroInfo('李月', '《永恒传说》', "人族", "女"), - HeroInfo('刘雪', '《永恒传说》', "人族", "女"), - HeroInfo('葛心', '《永恒传说》', "人族", "女"), - HeroInfo('步映容', '《幻将录》', "人族", "女"), - HeroInfo('莫慈良', '《幻将录》', "人族", "男"), - HeroInfo('莫向阳', '《幻将录》', "人族", "男"), - HeroInfo('莫子薇', '《永恒传说》', "人族", "女"), - HeroInfo('藏凯阳', '《永恒传说》', "人族", "男"), - HeroInfo('奇雨歆', '《永恒传说》', "人族", "女"), - HeroInfo('林天蕊', '《永恒传说》', "人族", "女"), - HeroInfo('吴灏然', '《永恒传说》', "人族", "男"), - HeroInfo('何解连', '《永恒传说》', "人族", "男"), - HeroInfo('步络尘', '《幻将录》', "人族", "男"), - HeroInfo('拓雷', '《幻将录》', "人族", "男"), - HeroInfo('炽阳骑', '《幻将录》', "人族", "男"), - HeroInfo('正构', '《幻将录》', "人族", "男"), - HeroInfo('烈', '《幻将录》', "人族", "男"), - HeroInfo('梦华君', '《幻将录》', "人族", "男"), - HeroInfo('初星', '《幻将录》', "人族", "男"), - HeroInfo('梦飞烟', '《幻将录》', "人族", "男"), - HeroInfo('武落英', '《幻将录》', "人族", "女"), - HeroInfo('古千缘', '《幻将录》', "人族", "男"), - ]; - - void sort( - Comparable Function(HeroInfo d) getField, - bool ascending, - ) { - _desserts.sort((HeroInfo a, HeroInfo b) { - if (!ascending) { - final HeroInfo c = a; - a = b; - b = c; - } - final Comparable aValue = getField(a); - final Comparable bValue = getField(b); - return Comparable.compare(aValue, bValue); - }); - notifyListeners(); - } - - int _selectedCount = 0; - - @override - DataRow? getRow(int index) { - if (index >= _desserts.length) return null; - final HeroInfo dessert = _desserts[index]; - return DataRow.byIndex( - index: index, - selected: dessert.selected, - onSelectChanged: (bool? value) { - if (dessert.selected != value && value != null) { - _selectedCount += value ? 1 : -1; - assert(_selectedCount >= 0); - dessert.selected = value; - notifyListeners(); - } - }, - cells: [ - DataCell(Center(child: Text(dessert.name))), - DataCell(Center(child: Text(dessert.calories))), - DataCell(Center(child: Text(dessert.fat))), - DataCell(Center(child: Text(dessert.carbs))), - ]); - } - - @override - bool get isRowCountApproximate => false; - - @override - int get rowCount => _desserts.length; - - @override - int get selectedRowCount => _selectedCount; - - void _selectAll(bool? checked) { - if (checked == null) return; - for (HeroInfo dessert in _desserts) { - dessert.selected = checked; - } - _selectedCount = checked ? _desserts.length : 0; - notifyListeners(); - } -} diff --git a/packages/widgets/lib/StatefulWidget/PopupMenuButton/node1_base.dart b/packages/widgets/lib/StatefulWidget/PopupMenuButton/node1_base.dart deleted file mode 100644 index 9ba7d0cb7..000000000 --- a/packages/widgets/lib/StatefulWidget/PopupMenuButton/node1_base.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:widgets/utils/dialog_about.dart'; - - -/// create by 张风捷特烈 on 2020-03-16 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 56, -// "name": 'PopupMenuButton基本使用', -// "priority": 1, -// "subtitle": -// "【itemBuilder】 : 构造器 【PopupMenuItemBuilder】\n" -// "【offset】 : 偏移 【Offset】\n" -// "【color】 : 背景颜色 【Color】\n" -// "【shape】 : 形状 【ShapeBorder】\n" -// "【elevation】 : 影深 【double】\n" -// "【onCanceled】 : 取消事件 【Function()】\n" -// "【onSelected】 : 选择事件 【Function(T)】", -// } -class CustomPopupMenuButton extends StatefulWidget { - const CustomPopupMenuButton({Key? key}) : super(key: key); - - @override - _CustomPopupMenuButtonState createState() => _CustomPopupMenuButtonState(); -} - -class _CustomPopupMenuButtonState extends State { - final Map map = const { - "关于": Icons.info_outline, - "帮助": Icons.help_outline, - "问题反馈": Icons.add_comment, - }; - - @override - Widget build(BuildContext context) { - return PopupMenuButton( - itemBuilder: (context) => buildItems(), - offset: const Offset(0, 50), - color: const Color(0xffF4FFFA), - elevation: 1, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(20), - bottomRight: Radius.circular(20), - topRight: Radius.circular(5), - bottomLeft: Radius.circular(5), - )), - onSelected: (e) { - print(e); - if (e == '关于') { - DialogAbout.show(context); - } - }, - onCanceled: () => print('onCanceled'), - ); - } - - List> buildItems() { - return map.keys - .toList() - .map((e) => PopupMenuItem( - value: e, - child: Wrap( - spacing: 10, - children: [ - Icon(map[e], color: Colors.blue), - Text(e), - ], - ))) - .toList(); - } -} diff --git a/packages/widgets/lib/StatefulWidget/PopupMenuDivider/node1_base.dart b/packages/widgets/lib/StatefulWidget/PopupMenuDivider/node1_base.dart deleted file mode 100644 index e16919b6a..000000000 --- a/packages/widgets/lib/StatefulWidget/PopupMenuDivider/node1_base.dart +++ /dev/null @@ -1,75 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:widgets/utils/dialog_about.dart'; - - -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 174, -// "name": 'PopupMenuDivider基本使用', -// "priority": 1, -// "subtitle": -// "【height】 : 高度 【double】", -// } -class CustomPopupMenuDivider extends StatelessWidget { - const CustomPopupMenuDivider({Key? key}) : super(key: key); - - final Map map = const { - "关于": Icons.info_outline, - "帮助": Icons.help_outline, - "问题反馈": Icons.add_comment, - }; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - _buildPopupMenuButton(context), - const PopupMenuDivider(), - ], - ); - } - - PopupMenuButton _buildPopupMenuButton(BuildContext context) { - return PopupMenuButton( - itemBuilder: (context) => [ - ...buildItems().sublist(0, 2), - const PopupMenuDivider(), - ...buildItems().sublist(2, 3) - ], - offset: const Offset(0, 50), - color: const Color(0xffF4FFFA), - elevation: 1, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(20), - bottomRight: Radius.circular(20), - topRight: Radius.circular(5), - bottomLeft: Radius.circular(5), - )), - onSelected: (e) { - print(e); - if (e == '关于') { - DialogAbout.show(context); - } - }, - onCanceled: () => print('onCanceled'), - ); - } - - List> buildItems() { - return map.keys - .toList() - .map((e) => PopupMenuItem( - value: e, - child: Wrap( - spacing: 10, - children: [ - Icon(map[e], color: Colors.blue), - Text(e), - ], - ))) - .toList(); - } -} diff --git a/packages/widgets/lib/StatefulWidget/PositionedTransition/node1_base.dart b/packages/widgets/lib/StatefulWidget/PositionedTransition/node1_base.dart deleted file mode 100644 index d90763a7f..000000000 --- a/packages/widgets/lib/StatefulWidget/PositionedTransition/node1_base.dart +++ /dev/null @@ -1,67 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 93, -// "name": 'PositionedTransition基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】\n" -// "【rect】 : 动画 【Animation】\n" -// " PositionedTransition组件只能在Stack内起作用", -// } -class CustomPositionedTransition extends StatefulWidget { - const CustomPositionedTransition({Key? key}) : super(key: key); - - @override - _CustomPositionedTransitionState createState() => - _CustomPositionedTransitionState(); -} - -class _CustomPositionedTransitionState extends State - with SingleTickerProviderStateMixin { - late AnimationController _ctrl; - - @override - void initState() { - _ctrl = AnimationController( - vsync: this, - duration: const Duration(seconds: 2), - ); - _ctrl.forward(); - super.initState(); - } - - @override - void dispose() { - _ctrl.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: () => _ctrl.forward(from: 0), - child: Container( - color: Colors.grey.withAlpha(33), - width: 200, - height: 100, - child: Stack( - children: [ - PositionedTransition( - rect: RelativeRectTween( - begin: const RelativeRect.fromLTRB(0, 50, 150, 100), - end: const RelativeRect.fromLTRB(60, 0, 150, -50), - ).animate(_ctrl), - child: const Icon( - Icons.android, - color: Colors.green, - size: 50, - ), - ) - ], - ), - )); - } -} diff --git a/packages/widgets/lib/StatefulWidget/Radio/node1_base.dart b/packages/widgets/lib/StatefulWidget/Radio/node1_base.dart deleted file mode 100644 index b2005847f..000000000 --- a/packages/widgets/lib/StatefulWidget/Radio/node1_base.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 45, -// "name": 'Radio基本使用', -// "priority": 1, -// "subtitle": -// "【value】 : 选钮值 【T】\n" -// "【groupValue】 : 当前匹配值 【T】\n" -// "【activeColor】 : 激活颜色 【Color】\n" -// "【onChanged】 : 改变时回调 【Function(T)】", -// } -class CustomRadio extends StatefulWidget { - const CustomRadio({Key? key}) : super(key: key); - - @override - _CustomRadioState createState() => _CustomRadioState(); -} - -class _CustomRadioState extends State { - List data = [1, 2, 3, 4, 5]; - double _value = 1; - - @override - Widget build(BuildContext context) { - return Row( - mainAxisSize: MainAxisSize.min, - children: data - .map((e) => Radio( - activeColor: Colors.orangeAccent, - value: e, - groupValue: _value, - onChanged: (v) => setState(() => _value = v??0))) - .toList(), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/RangeSlider/node1_base.dart b/packages/widgets/lib/StatefulWidget/RangeSlider/node1_base.dart deleted file mode 100644 index 28c35587a..000000000 --- a/packages/widgets/lib/StatefulWidget/RangeSlider/node1_base.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 44, -// "name": 'RangeSlider基本使用', -// "priority": 1, -// "subtitle": "【values】 : 数值 【RangeValues】\n" -// "【min】 : 最小值 【double】\n" -// "【max】 : 最大值 【double】\n" -// "【divisions】 : 分段数 【int】\n" -// "【label】 : 提示气泡文字 【String】\n" -// "【activeColor】 : 激活颜色 【Color】\n" -// "【inactiveColor】 : 非激活颜色 【Color】\n" -// "【onChangeStart】 : 开始滑动时监听 【Function(RangeValues)】\n" -// "【onChangeEnd】 : 滑动结束时监听 【Function(RangeValues)】\n" -// "【onChanged】 : 改变时回调 【Function(RangeValues)】", -// } -class CustomRangeSlider extends StatefulWidget { - const CustomRangeSlider({Key? key}) : super(key: key); - - @override - _CustomRangeSliderState createState() => _CustomRangeSliderState(); -} - -class _CustomRangeSliderState extends State { - RangeValues _rangeValues = const RangeValues(90, 270); - - @override - Widget build(BuildContext context) { - return RangeSlider( - values: _rangeValues, - divisions: 180, - min: 0.0, - max: 360.0, - labels: RangeLabels(_rangeValues.start.toStringAsFixed(1), - _rangeValues.end.toStringAsFixed(1)), - activeColor: Colors.orangeAccent, - inactiveColor: Colors.green.withAlpha(99), - onChangeStart: (value) { - print('开始滑动:$value'); - }, - onChangeEnd: (value) { - print('滑动结束:$value'); - }, - onChanged: (value) { - setState(() { - _rangeValues = value; - }); - }); - } -} diff --git a/packages/widgets/lib/StatefulWidget/RawChip/node1_press.dart b/packages/widgets/lib/StatefulWidget/RawChip/node1_press.dart deleted file mode 100644 index cd1c437f6..000000000 --- a/packages/widgets/lib/StatefulWidget/RawChip/node1_press.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-26 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 153, -// "name": 'RawChip点击效果', -// "priority": 1, -// "subtitle": -// "【label】: 中间组件 【Widget】\n" -// "【padding】 : 内边距 【EdgeInsetsGeometry】\n" -// "【labelPadding】 : label边距 【EdgeInsetsGeometry】\n" -// "【shadowColor】: 阴影色 【Color】\n" -// "【avatar】: 左侧组件 【Widget】\n" -// "【elevation】: 影深 【double】\n" -// "【pressElevation】: 点击时影深 【double】\n" -// "【onPressed】 : 点击事件 【Function()】", -// } -class PressRawChip extends StatelessWidget { - const PressRawChip({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return RawChip( - padding: const EdgeInsets.all(5), - labelPadding: const EdgeInsets.all(3), - label: const Text('张风捷特烈'), - avatar: Image.asset("assets/images/icon_head.webp"), - elevation: 3, - pressElevation: 5, - shadowColor: Colors.orangeAccent, - onPressed: () => Navigator.of(context).pushNamed('AboutMePage'), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/RawChip/node2_select.dart b/packages/widgets/lib/StatefulWidget/RawChip/node2_select.dart deleted file mode 100644 index 8949f8618..000000000 --- a/packages/widgets/lib/StatefulWidget/RawChip/node2_select.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-26 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 153, -// "name": 'RawChip选中和删除效果', -// "priority": 2, -// "subtitle": -// "【selected】: 是否选中 【bool】\n" -// "【deleteIconColor】: 尾部图标色 【Color】\n" -// "【selectedColor】: 选中色 【Color】\n" -// "【deleteIcon】: 尾部组件 【Widget】\n" -// "【onSelected】: 选中事件 【Function(bool)】\n" -// "【onDeleted】 : 尾部事件 【Function()】", -// } -class SelectRawChip extends StatefulWidget { - const SelectRawChip({Key? key}) : super(key: key); - - @override - _SelectRawChipState createState() => _SelectRawChipState(); -} - -class _SelectRawChipState extends State { - bool _selected = false; - @override - Widget build(BuildContext context) { - return RawChip( - selected: _selected, - padding: const EdgeInsets.all(5), - labelPadding: const EdgeInsets.all(3), - deleteIconColor: Colors.red, - selectedColor: Colors.orangeAccent.withAlpha(44), - label: const Text('张风捷特烈'), - avatar: Image.asset("assets/images/icon_head.webp"), - elevation: 3, - pressElevation: 5, - shadowColor: Colors.orangeAccent, - onSelected: (v)=> setState(() => _selected=v), - onDeleted: () => Navigator.of(context).pushNamed('AboutMePage'), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatefulWidget/RawGestureDetector/node1_base.dart b/packages/widgets/lib/StatefulWidget/RawGestureDetector/node1_base.dart deleted file mode 100644 index 5b5c2a58c..000000000 --- a/packages/widgets/lib/StatefulWidget/RawGestureDetector/node1_base.dart +++ /dev/null @@ -1,70 +0,0 @@ - -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 248 RawGestureDetector 可以用来检测给定手势工厂描述的手势,在开发自己的手势识别器时非常有用。对于常见的手势,使用 GestureRecognizer。 -// { -// "widgetId": 248, -// "name": 'RawGestureDetector基本使用', -// "priority": 1, -// "subtitle": -// "【behavior】 : 侦测行为 【HitTestBehavior】\n" -// "【gestures】 : 手势映射 【Map】\n" -// "【child】 : 子组件 【Widget】", -// } - -class RawGestureDetectorDemo extends StatefulWidget { - const RawGestureDetectorDemo({Key? key}) : super(key: key); - - @override - _RawGestureDetectorDemoState createState() => _RawGestureDetectorDemoState(); -} - -class _RawGestureDetectorDemoState extends State { - String _last = ""; - - @override - Widget build(BuildContext context) { - return RawGestureDetector( - gestures: { - TapGestureRecognizer: - GestureRecognizerFactoryWithHandlers( - () => TapGestureRecognizer(), - init, - ), - }, - child: Container( - width: 300.0, - height: 100.0, - alignment: Alignment.center, - color: Colors.yellow, - child: Text(_last)), - ); - } - - void init(TapGestureRecognizer instance) { - instance..onTapDown = (TapDownDetails details) { - setState(() { - _last = 'down'; - }); - } - ..onTapUp = (TapUpDetails details) { - setState(() { - _last = 'up'; - }); - } - ..onTap = () { - setState(() { - _last = 'tap'; - }); - } - ..onTapCancel = () { - setState(() { - _last = 'cancel'; - }); - } - ; - } -} diff --git a/packages/widgets/lib/StatefulWidget/RawKeyboardListener/node1_base.dart b/packages/widgets/lib/StatefulWidget/RawKeyboardListener/node1_base.dart deleted file mode 100644 index 6152e70cb..000000000 --- a/packages/widgets/lib/StatefulWidget/RawKeyboardListener/node1_base.dart +++ /dev/null @@ -1,65 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 254 RawKeyboardListener 可以用来检测键盘按键和松键的事件,目前只能检测到物理键盘,可在桌面端使用。 -// { -// "widgetId": 254, -// "name": 'RawGestureDetector基本使用', -// "priority": 1, -// "subtitle": "【onKey】 : 键盘事件 【ValueChanged】\n" -// "【focusNode】 : 焦点 【FocusNode】\n" -// "【autofocus】 : 是否自动聚焦 【bool】\n" -// "【child】 : 子组件 【Widget】", -// } - -class RawKeyboardListenerDemo extends StatefulWidget { - const RawKeyboardListenerDemo({Key? key}) : super(key: key); - - @override - _RawKeyboardListenerDemoState createState() => _RawKeyboardListenerDemoState(); -} - -class _RawKeyboardListenerDemoState extends State { - String _info = ""; - - final FocusNode node = FocusNode(); - - @override - Widget build(BuildContext context) { - return RawKeyboardListener( - focusNode: node, - onKey: _onKey, - child: SizedBox( - width: 300, - child: Row( - children: [ - const Expanded( - child: TextField( - decoration: InputDecoration( - border: OutlineInputBorder() - ), - ), - ), - const SizedBox(width: 20,), - Text(_info) - ], - ), - ), - ); - } - - void _onKey(RawKeyEvent value) { - print(value); - if(value is RawKeyDownEvent){ - _info = "按下: ${value.logicalKey.debugName}\nid: 0x${value.logicalKey.keyId.toRadixString(16).padLeft(9,"0")}"; - } - if(value is RawKeyUpEvent){ - _info = "抬起: ${value.logicalKey.debugName}\nid: 0x${value.logicalKey.keyId.toRadixString(16).padLeft(9,"0")}"; - } - setState(() { - - }); - } -} diff --git a/packages/widgets/lib/StatefulWidget/RawMaterialButton/node1_base.dart b/packages/widgets/lib/StatefulWidget/RawMaterialButton/node1_base.dart deleted file mode 100644 index 69468522a..000000000 --- a/packages/widgets/lib/StatefulWidget/RawMaterialButton/node1_base.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'package:flutter/material.dart'; - - -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 175, -// "name": 'RawMaterialButton基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 子组件 【Widget】\n" -// "【elevation】 : 影深 【double】\n" -// "【fillColor】 : 填充色 【Color】\n" -// "【splashColor】 : 水波纹色 【Color】\n" -// "【textStyle】 : 文字样式 【TextStyle】\n" -// "【onLongPress】 : 长按事件 【Function()】\n" -// "【onPressed】 : 点击事件 【Function()】", -// } -class CustomRawMaterialButton extends StatelessWidget { - const CustomRawMaterialButton({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 20, - children: [ - RawMaterialButton( - elevation: 2, - fillColor: Colors.green, - splashColor: Colors.orange, - textStyle: const TextStyle(color: Colors.white), - onLongPress: ()=>print('onLongPress'), - child: const Icon(Icons.remove), - onPressed: ()=>print('onPressed'), - ), - RawMaterialButton( - elevation: 2, - fillColor: Colors.blue, - splashColor: Colors.orange, - textStyle: const TextStyle(color: Colors.white), - onLongPress: ()=>print('onLongPress'), - child: const Text('Push'), - onPressed: ()=>print('onPressed'), - ), - RawMaterialButton( - elevation: 2, - fillColor: Colors.red, - splashColor: Colors.orange, - textStyle: const TextStyle(color: Colors.white), - onLongPress: ()=>print('onLongPress'), - child: const Icon(Icons.add), - onPressed: ()=>print('onPressed'), - ), - - ], - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatefulWidget/RawMaterialButton/node2_shape.dart b/packages/widgets/lib/StatefulWidget/RawMaterialButton/node2_shape.dart deleted file mode 100644 index 8fb42d22a..000000000 --- a/packages/widgets/lib/StatefulWidget/RawMaterialButton/node2_shape.dart +++ /dev/null @@ -1,62 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 175, -// "name": 'RawMaterialButton高亮和形状', -// "priority": 2, -// "subtitle": -// "【highlightElevation】 : 高亮影深 【double】\n" -// "【shape】 : 形状 【ShapeBorder】", -// } -class ShapeRawMaterialButton extends StatelessWidget { - const ShapeRawMaterialButton({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 20, - children: [ - RawMaterialButton( - elevation: 2, - shape: const CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFDFDFDF)), - ), - fillColor: Colors.green, - splashColor: Colors.orange, - textStyle: const TextStyle(color: Colors.white), - onLongPress: () => print('onLongPress'), - child: const Icon(Icons.remove), - onPressed: () => print('onPressed'), - ), - RawMaterialButton( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(15))), - elevation: 0, - highlightElevation: 0, - fillColor: Colors.blue, - splashColor: Colors.orange, - textStyle: const TextStyle(color: Colors.white), - onLongPress: () => print('onLongPress'), - child: const Text('Push'), - onPressed: () => print('onPressed'), - ), - RawMaterialButton( - elevation: 2, - shape: const CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFDFDFDF)), - ), - fillColor: Colors.red, - splashColor: Colors.orange, - textStyle: const TextStyle(color: Colors.white), - onLongPress: () => print('onLongPress'), - child: const Icon(Icons.add), - onPressed: () => print('onPressed'), - ), - ], - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatefulWidget/RefreshIndicator/node1_base.dart b/packages/widgets/lib/StatefulWidget/RefreshIndicator/node1_base.dart deleted file mode 100644 index 6b2ba5b5b..000000000 --- a/packages/widgets/lib/StatefulWidget/RefreshIndicator/node1_base.dart +++ /dev/null @@ -1,55 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 49, -// "name": 'RefreshIndicator基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子(可滑动) 【Widget】\n" -// "【displacement】 : 指示器悬浮高度 【double】\n" -// "【color】 : 指示器颜色 【Color】\n", -// "【backgroundColor】 : 指示器背景色 【Color】\n" -// "【onRefresh】 : 异步函数 【Future Function()】" -// } -class CustomRefreshIndicator extends StatefulWidget { - const CustomRefreshIndicator({Key? key}) : super(key: key); - - @override - _CustomRefreshIndicatorState createState() => _CustomRefreshIndicatorState(); -} - -class _CustomRefreshIndicatorState extends State { - int _count = 0; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 200, - width: 200, - child: RefreshIndicator( - onRefresh: _increment, - displacement: 20, - color: Colors.orange, - backgroundColor: Colors.white, - child: SingleChildScrollView( - child: Container( - alignment: Alignment.center, - width: 200, - height: 300, - color: Colors.blue, - child: Text('$_count',style: const TextStyle(color: Colors.white,fontSize: 40)), - ), - ), - ), - ); - } - - Future _increment() async { - await Future.delayed(const Duration(seconds: 2)); - setState(() { - _count++; - }); - } -} diff --git a/packages/widgets/lib/StatefulWidget/RelativePositionedTransition/node1_base.dart b/packages/widgets/lib/StatefulWidget/RelativePositionedTransition/node1_base.dart deleted file mode 100644 index bc9883e6f..000000000 --- a/packages/widgets/lib/StatefulWidget/RelativePositionedTransition/node1_base.dart +++ /dev/null @@ -1,73 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 115, -// "name": 'RelativePositionedTransition基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】\n" -// "【size】 : 左和上的偏移量 【Size】\n" -// "【rect】 : 动画 【Animation】\n" -// " PositionedTransition组件只能在Stack内起作用", -// } -class CustomRelativePositionedTransition extends StatefulWidget { - const CustomRelativePositionedTransition({Key? key}) : super(key: key); - - @override - _CustomRelativePositionedTransitionState createState() => - _CustomRelativePositionedTransitionState(); -} - -class _CustomRelativePositionedTransitionState - extends State - with SingleTickerProviderStateMixin { - late AnimationController _ctrl; - late Animation rectAnimation; - - @override - void initState() { - _ctrl = AnimationController( - vsync: this, - duration: const Duration(seconds: 2), - ); - rectAnimation = RectTween( - begin: const Rect.fromLTRB(0, 0, 50, 50), - end: const Rect.fromLTRB(0, 0, 50, 50).translate(100, 50), - ).animate(_ctrl); - _ctrl.forward(); - super.initState(); - } - - @override - void dispose() { - _ctrl.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: () => _ctrl.forward(from: 0), - child: Container( - color: Colors.grey.withAlpha(33), - width: 200, - height: 100, - child: Stack( - children: [ - RelativePositionedTransition( - size: const Size(200, 100), - rect: rectAnimation as Animation, - child: const Icon( - Icons.android, - color: Colors.green, - size: 50, - ), - ) - ], - ), - )); - } -} diff --git a/packages/widgets/lib/StatefulWidget/ReorderableListView/node1_base.dart b/packages/widgets/lib/StatefulWidget/ReorderableListView/node1_base.dart deleted file mode 100644 index d485ed002..000000000 --- a/packages/widgets/lib/StatefulWidget/ReorderableListView/node1_base.dart +++ /dev/null @@ -1,84 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 177, -// "name": 'ReorderableListView基本使用', -// "priority": 1, -// "subtitle": -// "【children】 : 子组件列表 【List】\n" -// "【header】 : 头部组件 【Widget】\n" -// "【padding】 : 内边距 【EdgeInsets】\n" -// "【onReorder】 : 调换时回调 【ReorderCallback】", -// } -class CustomReorderableListView extends StatefulWidget { - const CustomReorderableListView({Key? key}) : super(key: key); - - @override - _CustomReorderableListViewState createState() => _CustomReorderableListViewState(); -} - -class _CustomReorderableListViewState extends State { - -final List data = [ - Colors.yellow[50]!, - Colors.yellow[100]!, - Colors.yellow[200]!, - Colors.yellow[300]!, - Colors.yellow[400]!, - Colors.yellow[500]!, - Colors.yellow[600]!, - Colors.yellow[700]!, - Colors.yellow[800]!, - Colors.yellow[900]!, - ]; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 250, - child: ReorderableListView( - padding: const EdgeInsets.all(10), - header: Container( - color: Colors.blue, - alignment: Alignment.center, - height: 50, - child: const Text('长按拖拽进行换位',style: TextStyle(color: Colors.white),)), - onReorder: _handleReorder, - children: data.map((color) => _buildItem(color)).toList(), - ), - ); - } - - void _handleReorder(int oldIndex, int newIndex) { - if (oldIndex < newIndex) { - newIndex -= 1; - } - - setState(() { - final element = data.removeAt(oldIndex); - data.insert(newIndex, element); - }); - - } - - Widget _buildItem(Color color) { - return Container( - key: ValueKey(color) , - alignment: Alignment.center, - height: 50, - color: color, - child: Text( - colorString(color), - style: const TextStyle(color: Colors.white, shadows: [ - Shadow(color: Colors.black, offset: Offset(.5, .5), blurRadius: 2) - ]), - ), - ); - } - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} \ No newline at end of file diff --git a/packages/widgets/lib/StatefulWidget/ReorderableListView/node2_direction.dart b/packages/widgets/lib/StatefulWidget/ReorderableListView/node2_direction.dart deleted file mode 100644 index d46129403..000000000 --- a/packages/widgets/lib/StatefulWidget/ReorderableListView/node2_direction.dart +++ /dev/null @@ -1,83 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-30 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 177, -// "name": 'ReorderableListView滑动方向', -// "priority": 2, -// "subtitle": -// "【scrollDirection】 : 滑动方向 【Axis】\n" -// "【reverse】 : 是否反向 【bool】", -// } -class DirectionReorderableListView extends StatefulWidget { - const DirectionReorderableListView({Key? key}) : super(key: key); - - @override - _DirectionReorderableListViewState createState() => - _DirectionReorderableListViewState(); -} - -class _DirectionReorderableListViewState extends State { - final List data = [ - Colors.yellow[50]!, - Colors.yellow[100]!, - Colors.yellow[200]!, - Colors.yellow[300]!, - Colors.yellow[400]!, - Colors.yellow[500]!, - Colors.yellow[600]!, - Colors.yellow[700]!, - Colors.yellow[800]!, - Colors.yellow[900]!, - ]; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 200, - child: ReorderableListView( - scrollDirection: Axis.horizontal, - reverse: false, - onReorder: _handleReorder, - children: data.map((color) => _buildItem(color)).toList(), - ), - ); - } - - void _handleReorder(int oldIndex, int newIndex) { - if (oldIndex < newIndex) { - newIndex -= 1; - } - - setState(() { - final element = data.removeAt(oldIndex); - data.insert(newIndex, element); - }); - - } - - Widget _buildItem(Color color) { - return Container( - key: ValueKey(color) , - alignment: Alignment.center, - width: 80, - color: color, - child: Text( - colorString(color), - style: const TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2, - ) - ]), - ), - ); - } - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} \ No newline at end of file diff --git a/packages/widgets/lib/StatefulWidget/RotationTransition/node1_base.dart b/packages/widgets/lib/StatefulWidget/RotationTransition/node1_base.dart deleted file mode 100644 index e28a145af..000000000 --- a/packages/widgets/lib/StatefulWidget/RotationTransition/node1_base.dart +++ /dev/null @@ -1,64 +0,0 @@ - -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 90, -// "name": 'RotationTransition基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】\n" -// "【turns】 : 是否消失 【Animation】", -// } -class CustomRotationTransition extends StatefulWidget { - const CustomRotationTransition({Key? key}) : super(key: key); - - @override - _CustomRotationTransitionState createState() => _CustomRotationTransitionState(); -} - -class _CustomRotationTransitionState extends State with SingleTickerProviderStateMixin{ - - late AnimationController _ctrl; - - @override - void initState() { - super.initState(); - _ctrl = AnimationController( - vsync: this, - duration: const Duration(seconds: 2), - ); - _ctrl.forward(); - } - - @override - void dispose() { - _ctrl.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: () => _ctrl.forward(from: 0), - child: Container( - color: Colors.grey.withAlpha(22), - width: 100, - height: 100, - child: RotationTransition( - turns: CurvedAnimation( - parent: _ctrl, - curve: Curves.linear, - ), - child: const Icon( - Icons.camera_outlined, - color: Colors.green, - size: 60, - ), - ), - ), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/Scaffold/node1_base.dart b/packages/widgets/lib/StatefulWidget/Scaffold/node1_base.dart deleted file mode 100644 index 0cfbf92ac..000000000 --- a/packages/widgets/lib/StatefulWidget/Scaffold/node1_base.dart +++ /dev/null @@ -1,127 +0,0 @@ -import 'package:flutter/material.dart'; -import '../PopupMenuButton/node1_base.dart'; -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 64, -// "name": 'Scaffold基本用法', -// "priority": 1, -// "subtitle": -// "【appBar】 : 头部组件 【PreferredSizeWidget】\n" -// "【bottomNavigationBar】 : 底部组件 【Widget】\n" -// "【drawer】 : 左侧滑组件 【Widget】\n" -// "【endDrawer】 : 右侧滑组件 【Widget】\n" -// "【body】 : 内容组件 【Widget】\n" -// "【backgroundColor】 : 背景色 【Color】\n" -// "【floatingActionButton】 : 浮动按钮 【Widget】\n" -// "【floatingActionButtonLocation】 : 浮动按钮位置 【FloatingActionButtonLocation】", -// } -class CustomScaffold extends StatefulWidget { - const CustomScaffold({Key? key}) : super(key: key); - - @override - State createState() => _CustomScaffoldState(); -} - -// AppBar 默认的实例,有状态 -class _CustomScaffoldState extends State with SingleTickerProviderStateMixin { - final List tabs = const ['风画庭', '雨韵舍', '雷鸣殿', '电疾堂', '霜寒阁', '雪月楼']; - int _position = 0; - final Map iconsMap = { - "图鉴": Icons.home, - "动态": Icons.toys, - "喜欢": Icons.favorite, - "手册": Icons.class_, - "我的": Icons.account_circle, - }; - final List _colors = [ - Colors.blue, - Colors.red, - Colors.yellow, - Colors.green, - Colors.purple, - ]; - - late TabController _tabController; - - @override - void initState() { - super.initState(); - _tabController = TabController(vsync: this, length: tabs.length); - } - - @override - void dispose() { - _tabController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return SizedBox( - width: MediaQuery.of(context).size.width, - height: MediaQuery.of(context).size.height - 300, - child: Scaffold( - floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, - floatingActionButton: FloatingActionButton( - child: const Icon(Icons.add), - onPressed: () {}, - ), - drawer: _buildLeftDrawer(), - endDrawer: _buildLeftDrawer(), - appBar: AppBar( - title: const Text('风雅六社'), - backgroundColor: Colors.blue, - centerTitle: true, - actions: const [Icon(Icons.star), CustomPopupMenuButton()], - bottom: _buildTabBar(), - ), - body: _buildTableBarView(), - bottomNavigationBar: _buildBottomNavigationBar(), - ), - ); - } - - Drawer _buildLeftDrawer() => Drawer( - elevation: 1, - child: Image.asset( - 'assets/images/sabar.webp', - fit: BoxFit.cover, - ), - ); - - PreferredSizeWidget _buildTabBar() => TabBar( - isScrollable: true, - controller: _tabController, - indicatorColor: Colors.orangeAccent, - tabs: tabs.map((e) => Tab(text: e)).toList(), - ); - - Widget _buildBottomNavigationBar() => BottomNavigationBar( - onTap: (position) => setState(() => _position = position), - currentIndex: _position, - elevation: 1, - backgroundColor: Colors.white, - iconSize: 25, - selectedLabelStyle: const TextStyle(fontWeight: FontWeight.bold), - showUnselectedLabels: false, - showSelectedLabels: true, - items: iconsMap.keys - .map((key) => BottomNavigationBarItem( - label: key, - icon: Icon(iconsMap[key]), - backgroundColor: _colors[_position])) - .toList(), - ); - - Widget _buildTableBarView() => TabBarView( - controller: _tabController, - children: tabs - .map((e) => Center( - child: Text( - e, - style: const TextStyle(color: Colors.blue, fontSize: 20), - ))) - .toList()); -} diff --git a/packages/widgets/lib/StatefulWidget/ScaleTransition/node1_base.dart b/packages/widgets/lib/StatefulWidget/ScaleTransition/node1_base.dart deleted file mode 100644 index 9afc2afd9..000000000 --- a/packages/widgets/lib/StatefulWidget/ScaleTransition/node1_base.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 91, -// "name": 'ScaleTransition基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】\n" -// "【scale】 : 动画 【Animation】", -// } -class CustomScaleTransition extends StatefulWidget { - const CustomScaleTransition({Key? key}) : super(key: key); - - @override - _CustomScaleTransitionState createState() => _CustomScaleTransitionState(); -} - -class _CustomScaleTransitionState extends State - with SingleTickerProviderStateMixin { - late AnimationController _ctrl; - - @override - void initState() { - _ctrl = AnimationController(vsync: this, duration: const Duration(seconds: 2)); - _ctrl.forward(); - super.initState(); - } - - @override - void dispose() { - _ctrl.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: () => _ctrl.forward(from: 0), - child: Container( - color: Colors.grey.withAlpha(22), - width: 100, - height: 100, - child: ScaleTransition( - scale: CurvedAnimation(parent: _ctrl, curve: Curves.linear), - child: const Icon(Icons.android, color: Colors.green, size: 60), - ), - )); - } -} diff --git a/packages/widgets/lib/StatefulWidget/Scrollable/node1_base.dart b/packages/widgets/lib/StatefulWidget/Scrollable/node1_base.dart deleted file mode 100644 index dd95e0ce6..000000000 --- a/packages/widgets/lib/StatefulWidget/Scrollable/node1_base.dart +++ /dev/null @@ -1,69 +0,0 @@ -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; - -/// create by 张风捷特烈 on 2020/8/2 -/// contact me by email 1981462002@qq.com -/// 说明: 253 Scrollable 可滑动组件 实现了一个可滚动组件的交互模型,需要viewportBuilder进的viewport的构造。是ScrollView的核心实现组件之一,一般不直接使用。 - -// { -// "widgetId": 253, -// "name": "Scrollable的基本使用", -// "priority": 1, -// "subtitle": "【viewportBuilder】 : 视口构造器 【ViewportBuilder】\n" -// "【axisDirection】: 滑动方向 【AxisDirection】\n" -// "【controller】: 滑动控制器 【ScrollController】\n" -// "【dragStartBehavior】: t拖动行为 【DragStartBehavior】\n" -// "【physics】: 滚动现象 【ScrollPhysics】\n", -// } - -class ScrollableDemo extends StatelessWidget { - ScrollableDemo({Key? key}) : super(key: key); - - final List data = List.generate(32, (i) => Color(0xFF6600FF - 2 * i)); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 250, - child: Scrollable( - axisDirection: AxisDirection.down, - physics: const BouncingScrollPhysics(), - dragStartBehavior: DragStartBehavior.start, - viewportBuilder: (ctx, position) => Viewport( - cacheExtent: 200, - cacheExtentStyle: CacheExtentStyle.pixel, - offset: position, - slivers: [_buildSliverList()], - ), - ), - ); - } - - Widget _buildSliverList() => SliverList( - delegate: SliverChildBuilderDelegate( - (_, int index) => Container( - margin: const EdgeInsets.only(top: 1), - alignment: Alignment.center, - width: 100, - height: 60, - color: data[index], - child: Text( - colorString(data[index]), - style: const TextStyle( - color: Colors.white, - shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2) - ], - ), - ), - ), - childCount: data.length), - ); - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/packages/widgets/lib/StatefulWidget/Scrollbar/node1_base.dart b/packages/widgets/lib/StatefulWidget/Scrollbar/node1_base.dart deleted file mode 100644 index 97fff91c5..000000000 --- a/packages/widgets/lib/StatefulWidget/Scrollbar/node1_base.dart +++ /dev/null @@ -1,64 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-31 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 194, -// "name": 'Scrollbar基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 子组件 【Widget】\n" -// "【controller】 : 控制器 【ScrollController】", -// } -class CustomScrollbar extends StatelessWidget { - CustomScrollbar({Key? key}) : super(key: key); - - final List data = [ - Colors.purple[50]!, - Colors.purple[100]!, - Colors.purple[200]!, - Colors.purple[300]!, - Colors.purple[400]!, - Colors.purple[500]!, - Colors.purple[600]!, - Colors.purple[700]!, - Colors.purple[800]!, - Colors.purple[900]!, - ]; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 200, - child: Scrollbar( - child: ListView( - padding: const EdgeInsets.symmetric(horizontal: 5), - children: data - .map((color) => Container( - alignment: Alignment.center, - width: 100, - height: 50, - color: color, - child: Text( - colorString(color), - style: const TextStyle( - color: Colors.white, - shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2) - ], - ), - ), - )) - .toList(), - ), - ), - ); - } - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} \ No newline at end of file diff --git a/packages/widgets/lib/StatefulWidget/SelectableText/node1_base.dart b/packages/widgets/lib/StatefulWidget/SelectableText/node1_base.dart deleted file mode 100644 index 398040c82..000000000 --- a/packages/widgets/lib/StatefulWidget/SelectableText/node1_base.dart +++ /dev/null @@ -1,41 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-03-31 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 53, -// "name": 'SelectableText基本使用', -// "priority": 1, -// "subtitle": -// "【入参】 : 显示文字 【String】\n" -// "【style】 : 文字样式 【TextStyle】\n" -// "【cursorRadius】 : 光标半径 【Radius】\n" -// "【cursorColor】 : 光标颜色 【Color】\n" -// "【cursorWidth】 : 光标宽度 【double】\n" -// "【showCursor】 : 是否显示光标 【bool】\n" -// "【autofocus】 : 自动聚焦 【bool】", -// } -class CustomSelectableText extends StatelessWidget { - const CustomSelectableText({Key? key}) : super(key: key); - - final String text = " 始臣之解牛之时,所见无非牛者。三年之后,未尝见全牛也。方今之时," - "臣以神遇而不以目视,官知止而神欲行。依乎天理,批大郤,导大窾,因其固然," - "技经肯綮之未尝,而况大軱乎!良庖岁更刀,割也;族庖月更刀,折也。" - "今臣之刀十九年矣,所解数千牛矣,而刀刃若新发于硎。彼节者有间,而刀刃者无厚;" - "以无厚入有间,恢恢乎其于游刃必有余地矣,是以十九年而刀刃若新发于硎。" - "虽然,每至于族,吾见其难为,怵然为戒,视为止,行为迟。动刀甚微,謋然已解,如土委地。" - "提刀而立,为之四顾,为之踌躇满志,善刀而藏之."; - - @override - Widget build(BuildContext context) { - return SelectableText( - text, - style: const TextStyle(fontSize: 18, color: Colors.orange), - cursorColor: Colors.green, - cursorRadius: const Radius.circular(3), - cursorWidth: 5, - showCursor: true, - autofocus: false, - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/SelectableText/node2_align.dart b/packages/widgets/lib/StatefulWidget/SelectableText/node2_align.dart deleted file mode 100644 index edfe022bc..000000000 --- a/packages/widgets/lib/StatefulWidget/SelectableText/node2_align.dart +++ /dev/null @@ -1,77 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-31 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 53, -// "name": 'SelectableText对齐属性', -// "priority": 2, -// "subtitle": -// "【textAlign】 : 对齐方式*6 【textAlign】\n" -// "【textDirection】 : 文字方向*2 【TextDirection】", -// } -class AlignSelectableText extends StatefulWidget { - const AlignSelectableText({Key? key}) : super(key: key); - - @override - _AlignSelectableTextState createState() => _AlignSelectableTextState(); -} - -class _AlignSelectableTextState extends State { - final String text = - "The [SelectableText] widget displays a string of text with a single style." - "The string might break across multiple lines or might all be displayed on" - "the same line depending on the layout constraints."; - TextAlign _textAlign = TextAlign.left; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - _buildSelector(), - SelectableText( - text, - style: const TextStyle(fontSize: 18, color: Colors.red), - cursorColor: Colors.green, - cursorRadius: const Radius.circular(3), - cursorWidth: 5, - showCursor: true, - textAlign: _textAlign, - textDirection: TextDirection.ltr, - autofocus: false, - ), - ], - ); - } - - Widget _buildSelector() { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - const Text( - "textAlign属性选择:", - style: TextStyle( - fontSize: 16, - color: Colors.blue, - fontWeight: FontWeight.bold, - ), - ), - DropdownButton( - underline: Container(), - value: _textAlign, - items: TextAlign.values - .map((e) => DropdownMenuItem( - value: e, - child: Text(e.toString()), - )) - .toList(), - onChanged: (e) { - setState(() { - _textAlign = e??_textAlign; - }); - }), - ], - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/SizeTransition/node1_base.dart b/packages/widgets/lib/StatefulWidget/SizeTransition/node1_base.dart deleted file mode 100644 index a196c71fd..000000000 --- a/packages/widgets/lib/StatefulWidget/SizeTransition/node1_base.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-03-31 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 92, -// "name": 'SizeTransition基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】\n" -// "【axis】 : 轴向*2 【Axis】\n" -// "【sizeFactor】 : 动画 【Animation】", -// } -class CustomSizeTransition extends StatefulWidget { - const CustomSizeTransition({Key? key}) : super(key: key); - - @override - _CustomSizeTransitionState createState() => _CustomSizeTransitionState(); -} - -class _CustomSizeTransitionState extends State - with SingleTickerProviderStateMixin { - late AnimationController _ctrl; - - @override - void initState() { - _ctrl = - AnimationController(vsync: this, duration: const Duration(seconds: 1)); - _ctrl.forward(); - super.initState(); - } - - @override - void dispose() { - _ctrl.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: () => _ctrl.forward(from: 0), - child: Wrap( - runSpacing: 20, - children: [ - SizeTransition( - axis: Axis.horizontal, - sizeFactor: CurvedAnimation(parent: _ctrl, curve: Curves.linear), - child: Container( - width: MediaQuery.of(context).size.width, - color: Colors.orange, - child: - const Icon(Icons.android, color: Colors.green, size: 80)), - ), - SizeTransition( - axis: Axis.vertical, - sizeFactor: CurvedAnimation(parent: _ctrl, curve: Curves.linear), - child: Container( - width: MediaQuery.of(context).size.width, - color: Colors.orange, - child: - const Icon(Icons.android, color: Colors.green, size: 80)), - ), - ], - ), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/SlideTransition/node1_base.dart b/packages/widgets/lib/StatefulWidget/SlideTransition/node1_base.dart deleted file mode 100644 index 4bf9ceb1b..000000000 --- a/packages/widgets/lib/StatefulWidget/SlideTransition/node1_base.dart +++ /dev/null @@ -1,76 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 112, -// "name": 'SlideTransition 基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】\n" -// "【textDirection】 : x轴方向 【TextDirection】\n" -// "【position】 : 动画 【Animation】", -// } -class CustomSlideTransition extends StatefulWidget { - const CustomSlideTransition({Key? key}) : super(key: key); - - @override - _CustomSlideTransitionState createState() => _CustomSlideTransitionState(); -} - -class _CustomSlideTransitionState extends State - with SingleTickerProviderStateMixin { - late AnimationController _ctrl; - late Animation animation; - - @override - void initState() { - super.initState(); - _ctrl = AnimationController( - vsync: this, - duration: const Duration(seconds: 2), - )..forward(); - - animation = Tween( - begin: Offset.zero, - end: const Offset(0.5, 0.5), - ).animate(_ctrl); - } - - @override - void dispose() { - _ctrl.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: () => _ctrl.forward(from: 0), - child: Container( - width: 200, - color: Colors.grey.withAlpha(33), - height: 100, - child: Stack( - fit: StackFit.expand, - children: [ - SlideTransition( - textDirection: TextDirection.ltr, - position: animation, - child: _buildChild(), - ), - SlideTransition( - textDirection: TextDirection.rtl, - position: animation, - child: _buildChild(), - ), - ], - ), - )); - } - Widget _buildChild() => const Icon( - Icons.accessible_forward_sharp, - color: Colors.green, - size: 25, - ); -} diff --git a/packages/widgets/lib/StatefulWidget/Slider/node1_base.dart b/packages/widgets/lib/StatefulWidget/Slider/node1_base.dart deleted file mode 100644 index 278635c36..000000000 --- a/packages/widgets/lib/StatefulWidget/Slider/node1_base.dart +++ /dev/null @@ -1,48 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-03-31 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 42, -// "name": 'Slider基本使用', -// "priority": 1, -// "subtitle": -// "【value】 : 数值 【double】\n" -// "【min】 : 最小值 【double】\n" -// "【max】 : 最大值 【double】\n" -// "【activeColor】 : 激活颜色 【Color】\n" -// "【inactiveColor】 : 非激活颜色 【Color】\n" -// "【onChanged】 : 改变时回调 【Function(double)】", -// } -class CustomSlider extends StatefulWidget { - const CustomSlider({Key? key}) : super(key: key); - - @override - _CustomSliderState createState() => _CustomSliderState(); -} - -class _CustomSliderState extends State { - double _value = 0.0; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Text('当前值:${_value.toStringAsFixed(1)}'), - Slider( - value: _value, - min: 0.0, - max: 360.0, - activeColor: Colors.orangeAccent, - inactiveColor: Colors.green.withAlpha(99), - onChanged: _onChange), - ], - ); - } - - void _onChange(value) { - setState(() { - _value = value; - }); - } -} diff --git a/packages/widgets/lib/StatefulWidget/Slider/node2_lable.dart b/packages/widgets/lib/StatefulWidget/Slider/node2_lable.dart deleted file mode 100644 index e11d52275..000000000 --- a/packages/widgets/lib/StatefulWidget/Slider/node2_lable.dart +++ /dev/null @@ -1,48 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-31 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 42, -// "name": 'Slider的分段与标签', -// "priority": 2, -// "subtitle": -// "【divisions】 : 分段数 【int】\n" -// "【label】 : 提示气泡文字 【String】\n" -// "【onChangeStart】 : 开始滑动时监听 【Function(double)】\n" -// "【onChangeEnd】 : 滑动结束时监听 【Function(double)】", -// } -class DivisionsSlider extends StatefulWidget { - const DivisionsSlider({Key? key}) : super(key: key); - - @override - _DivisionsSliderState createState() => _DivisionsSliderState(); -} - -class _DivisionsSliderState extends State { - double _value = 0.0; - - @override - Widget build(BuildContext context) { - return Slider( - value: _value, - min: 0.0, - max: 360.0, - divisions: 10, - label: _value.toStringAsFixed(1), - activeColor: Colors.orangeAccent, - inactiveColor: Colors.green.withAlpha(99), - onChangeStart: (value) { - print('开始滑动:$value'); - }, - onChangeEnd: (value) { - print('滑动结束:$value'); - }, - onChanged: (value) { - setState(() { - _value = value; - }); - }); - } -} diff --git a/packages/widgets/lib/StatefulWidget/StatefulBuilder/node1_base.dart b/packages/widgets/lib/StatefulWidget/StatefulBuilder/node1_base.dart deleted file mode 100644 index a40aa0eac..000000000 --- a/packages/widgets/lib/StatefulWidget/StatefulBuilder/node1_base.dart +++ /dev/null @@ -1,33 +0,0 @@ - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 242 StatefulBuilder 需要传入 builder 属性进行构造组件,在 builder 中可以使用 StateSetter 改变构造子组件的状态,即可以不用创建类而实现一个局部刷新的组件。 -// { -// "widgetId": 242, -// "name": 'StatefulBuilder基本使用', -// "priority": 1, -// "subtitle": -// "【builder】 : 组件构造器 【StatefulWidgetBuilder】", -// } - -class StatefulBuilderDemo extends StatelessWidget { - const StatefulBuilderDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - int count = 0; - - return StatefulBuilder( - builder: (ctx, setState) => ElevatedButton( - child: Text("当前数字: $count"), - onPressed: () { - setState(() { - count++; - }); - }, - ), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/StatusTransitionWidget/node1_base.dart b/packages/widgets/lib/StatefulWidget/StatusTransitionWidget/node1_base.dart deleted file mode 100644 index 27e59ba19..000000000 --- a/packages/widgets/lib/StatefulWidget/StatusTransitionWidget/node1_base.dart +++ /dev/null @@ -1,84 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 233 StatusTransitionWidget 状态转变组件 -/// 抽象类,可以根据提供的动画器状态变化触发刷新。在 Flutter 框架层没有实现的子类,也没有使用的场景,感觉用处不是很大。 -/// -// { -// "widgetId": 233, -// "name": 'StatusTransitionWidget 介绍', -// "priority": 1, -// "subtitle": -// "【animation】 : 子组件 【Animation】\n" -// "这里自定义 ColorStatusTransitionWidget 进行使用,在动画器的状态改变时构建不同的颜色。", -// } - - -class StatusTransitionWidgetDemo extends StatefulWidget { - const StatusTransitionWidgetDemo({Key? key}) : super(key: key); - - @override - _StatusTransitionWidgetDemoState createState() => - _StatusTransitionWidgetDemoState(); -} - -class _StatusTransitionWidgetDemoState extends State - with SingleTickerProviderStateMixin { - late AnimationController _ctrl; - - @override - void initState() { - super.initState(); - _ctrl = AnimationController( - vsync: this, - duration: const Duration(seconds: 1), - )..forward(); - } - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: (){ - _ctrl.forward(from: 0); - }, - child: ColorStatusTransitionWidget( - anim: _ctrl, - ), - ); - } -} - -class ColorStatusTransitionWidget extends StatusTransitionWidget { - final Animation anim; - - const ColorStatusTransitionWidget({Key? key,required this.anim}) - : super(key: key, animation: anim); - - @override - Widget build(BuildContext context) { - Color color = Colors.blue; - switch (animation.status) { - case AnimationStatus.dismissed: - color = Colors.black; - break; - case AnimationStatus.forward: - color = Colors.blue; - break; - case AnimationStatus.reverse: - color = Colors.red; - break; - case AnimationStatus.completed: - color = Colors.green; - break; - } - - return Container( - alignment: Alignment.center, - width: 80, - height: 80, - decoration: BoxDecoration(color: color, shape: BoxShape.circle), - child: Text('${animation.status}'.split('.')[1],style: const TextStyle(color: Colors.white),), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/Stepper/node1_base.dart b/packages/widgets/lib/StatefulWidget/Stepper/node1_base.dart deleted file mode 100644 index 6274ec801..000000000 --- a/packages/widgets/lib/StatefulWidget/Stepper/node1_base.dart +++ /dev/null @@ -1,115 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-04-01 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 200, -// "name": 'Stepper基本使用', -// "priority": 1, -// "subtitle": -// "【steps】 : 步骤列表 【List】\n" -// "【currentStep】 : 当前步骤 【double】\n" -// "【onStepTapped】 : 点击回调 【ValueChanged】\n" -// "【onStepCancel】 : 上一步回调 【VoidCallback】\n" -// "【controlsBuilder】 : 控制器构造 【ControlsWidgetBuilder】", -// } -class StepperDemo extends StatefulWidget { - const StepperDemo({Key? key}) : super(key: key); - - @override - _StepperDemoState createState() => _StepperDemoState(); -} - -class _StepperDemoState extends State { - int _position = 0; - - final Map stepsData = { - "填写表单": '请按表单填写个人信息。', - "邮箱校验": '已将邮件发送至您的邮箱,请按照相关指示对您的账号进行邮箱校验。', - "注册完成": '恭喜您,注册完成!', - }; - - final List steps = const [ - Step( - title: Text("填写表单"), - content: SizedBox(height: 60, child: Text("请按表单填写个人信息")), - ), - Step(title: Text("邮箱校验"), content: Text("请对您的账号进行邮箱校验")), - Step(title: Text("注册完成"), content: Text("恭喜您,注册完成")), - ]; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 200, - child: Stepper( - type: StepperType.horizontal, - currentStep: _position, - onStepTapped: (index) { - setState(() { - _position = index; - }); - }, - onStepContinue: () { - setState(() { - if (_position < 2) { - _position++; - } - }); - }, - onStepCancel: () { - if (_position > 0) { - setState(() { - _position--; - }); - } - }, - controlsBuilder: (_, ControlsDetails details) { - return Row( - children: [ - ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Colors.blue, - shape: const CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFDFDFDF)), - )), - onPressed: details.onStepContinue, - child: const Icon( - Icons.check, - color: Colors.white, - ), - ), - ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Colors.red, - shape: const CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFDFDFDF)), - )), - onPressed: details.onStepCancel, - child: const Icon( - Icons.keyboard_backspace, - color: Colors.white, - ), - ), - ], - ); - }, - steps: stepsData.keys.map((e){ - bool isActive = stepsData.keys.toList().indexOf(e) ==_position; - return Step( - title: Text(e,style: TextStyle(color: isActive?Colors.blue:Colors.black),), - isActive: isActive, - state: _getState(stepsData.keys.toList().indexOf(e)), - content: SizedBox(height: 60, child: Text(stepsData[e]!)), - ); - }).toList()), - ); - } - - StepState _getState(index){ - if(_position==index) return StepState.editing; - if(_position>index) return StepState.complete; - return StepState.indexed; - } -} diff --git a/packages/widgets/lib/StatefulWidget/Stepper/node2_type.dart b/packages/widgets/lib/StatefulWidget/Stepper/node2_type.dart deleted file mode 100644 index 60fc4a282..000000000 --- a/packages/widgets/lib/StatefulWidget/Stepper/node2_type.dart +++ /dev/null @@ -1,112 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-04-01 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 200, -// "name": 'Stepper的方向', -// "priority": 2, -// "subtitle": -// "【type】 : 方向 【StepperType】", -// } -class VerticalStepper extends StatefulWidget { - const VerticalStepper({Key? key}) : super(key: key); - - @override - _VerticalStepperState createState() => _VerticalStepperState(); -} - -class _VerticalStepperState extends State { - int _position = 0; - - final Map stepsData = { - "填写表单": '请按表单填写个人信息。', - "邮箱校验": '已将邮件发送至您的邮箱,请按照相关指示对您的账号进行邮箱校验。', - "注册完成": '恭喜您,注册完成!', - }; - - final List steps = const[ - Step( - title: Text("填写表单"), - content: SizedBox(height: 60, child: Text("请按表单填写个人信息")), - ), - Step(title: Text("邮箱校验"), content: Text("请对您的账号进行邮箱校验")), - Step(title: Text("注册完成"), content: Text("恭喜您,注册完成")), - ]; - - @override - Widget build(BuildContext context) { - return Stepper( - type: StepperType.vertical, - currentStep: _position, - onStepTapped: (index) { - setState(() { - _position = index; - }); - }, - onStepContinue: () { - setState(() { - if (_position < 2) { - _position++; - } - }); - }, - onStepCancel: () { - if (_position > 0) { - setState(() { - _position--; - }); - } - }, - controlsBuilder: (_,ControlsDetails details) { - return Row( - children: [ - ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Colors.blue, - shape: const CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFDFDFDF)), - )), - onPressed: details.onStepContinue, - child: const Icon( - Icons.check, - color: Colors.white, - ), - ), - ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Colors.red, - shape: const CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFDFDFDF)), - )), - onPressed: details.onStepCancel, - child: const Icon( - Icons.keyboard_backspace, - color: Colors.white, - ), - ), - ], - ); - }, - steps: stepsData.keys.map((e) { - bool isActive = stepsData.keys.toList().indexOf(e) == _position; - return Step( - title: Text( - e, - style: TextStyle(color: isActive ? Colors.blue : Colors.black), - ), - isActive: isActive, - state: _getState(stepsData.keys.toList().indexOf(e)), - content: SizedBox(height: 60, child: Text(stepsData[e]!)), - ); - }).toList()); - } - - StepState _getState(index) { - if (_position == index) return StepState.editing; - if (_position > index) return StepState.complete; - return StepState.indexed; - } -} diff --git a/packages/widgets/lib/StatefulWidget/StreamBuilder/node1_base.dart b/packages/widgets/lib/StatefulWidget/StreamBuilder/node1_base.dart deleted file mode 100644 index b7708e77b..000000000 --- a/packages/widgets/lib/StatefulWidget/StreamBuilder/node1_base.dart +++ /dev/null @@ -1,111 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 173, -// "name": 'StreamBuilder基本使用', -// "priority": 1, -// "subtitle": -// "【stream】 : 子组件 【Stream】\n" -// "【initialData】 : 初始数据 【T】\n" -// "【builder】 : 点击事件 【AsyncWidgetBuilder】", -// } -class CustomStreamBuilder extends StatefulWidget { - const CustomStreamBuilder({Key? key}) : super(key: key); - - @override - _CustomStreamBuilderState createState() => _CustomStreamBuilderState(); -} - -class _CustomStreamBuilderState extends State { - final CountGenerator _generator = CountGenerator()..increment(); - - @override - void dispose() { - _generator.dispose(); //关闭控制器 - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Row( - mainAxisSize: MainAxisSize.min, - children: [ - ElevatedButton( - style: ElevatedButton.styleFrom( - shape: const CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFDFDFDF)), - )), - child: const Icon( - Icons.add, - color: Colors.white, - ), - onPressed: () async { - await _generator.increment(); - }, - ), - _buildStreamBuilder(), - ElevatedButton( - style: ElevatedButton.styleFrom( - shape: const CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFDFDFDF)), - )), - child: const Icon( - Icons.remove, - color: Colors.white, - ), - onPressed: () async { - await _generator.minus(); - }, - ), - ], - ); - } - - Widget _buildStreamBuilder() => StreamBuilder( - stream: _generator.state, - builder: (BuildContext context, AsyncSnapshot snap) { - print(snap); - if (snap.connectionState == ConnectionState.done) { - return const Text('Done'); - } - if (snap.connectionState == ConnectionState.active) { - return Text( - snap.data.toString(), - style: Theme.of(context).textTheme.bodyText2, - ); - } - if (snap.connectionState == ConnectionState.waiting) { - return const CircularProgressIndicator(); - } - if (snap.hasError) { - return const Text('Error'); - } - return Container(); - }); -} - -class CountGenerator { - int _count = 0; //计数器数据 - final StreamController _controller = StreamController(); //控制器 - - Stream get state => _controller.stream; //获取状态流 - int get count => _count; //获取计数器数据 - - void dispose() {//关闭控制器 - _controller.close(); - } - - Future increment() async {//增加记数方法 - _controller.add(++_count); - } - - Future minus() async {//增加记数方法 - _controller.add(--_count); - } -} diff --git a/packages/widgets/lib/StatefulWidget/Switch/node1_base.dart b/packages/widgets/lib/StatefulWidget/Switch/node1_base.dart deleted file mode 100644 index 654212747..000000000 --- a/packages/widgets/lib/StatefulWidget/Switch/node1_base.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-14 -/// contact me by email 1981462002@qq.com -/// 说明: - -/// { -// "widgetId": 40, -// "name": 'Switch基础用法', -// "priority": 1, -// "subtitle": -// "【inactiveThumbColor】 : 未选中小圈颜色 【Color】\n" -// "【inactiveTrackColor】 : 未选中滑槽颜色 【Color】\n" -// "【activeColor】 : 选中时小圈颜色 【Color】\n" -// "【activeTrackColor】 : 选中时滑槽颜色 【Color】\n" -// "【onChanged】 : 切换回调 【Function(double)】" -// " onChanged时,回调true、null、false三种状态", -// } -class CustomSwitch extends StatefulWidget { - const CustomSwitch({Key? key}) : super(key: key); - - @override - _CustomSwitchState createState() => _CustomSwitchState(); -} - -class _CustomSwitchState extends State { - final List colors = const[ - Colors.red, - Colors.yellow, - Colors.blue, - Colors.green - ]; - bool _checked = false; - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 10, - children: colors - .map((e) => Switch( - value: _checked, - inactiveThumbColor: e, - inactiveTrackColor: Colors.grey.withAlpha(88), - activeColor: Colors.green, - activeTrackColor: Colors.orange, - onChanged: (v) { - setState(() => _checked = v); - })) - .toList(), - ); - } -} - diff --git a/packages/widgets/lib/StatefulWidget/Switch/node2_image.dart b/packages/widgets/lib/StatefulWidget/Switch/node2_image.dart deleted file mode 100644 index ce5d7225c..000000000 --- a/packages/widgets/lib/StatefulWidget/Switch/node2_image.dart +++ /dev/null @@ -1,48 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-14 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 40, -// "name": 'Switch图片', -// "priority": 2, -// "subtitle": -// "【inactiveThumbImage】 : 未选中小圈图片 【ImageProvider】\n" -// "【activeThumbImage】 : 选中小圈图片 【ImageProvider】", -// } -class ImageSwitch extends StatefulWidget { - const ImageSwitch({Key? key}) : super(key: key); - - @override - _ImageSwitchState createState() => _ImageSwitchState(); -} - -class _ImageSwitchState extends State { - final List imgs = const [ - "assets/images/head_icon/icon_5.webp", - "assets/images/head_icon/icon_6.webp", - "assets/images/head_icon/icon_7.webp", - "assets/images/head_icon/icon_8.webp" - ]; - bool _checked = false; - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 10, - children: imgs - .map((e) => - Switch( - value: _checked, - inactiveThumbImage: AssetImage(e), - activeThumbImage: const AssetImage('assets/images/icon_head.webp'), - onChanged: (v) { - setState(() => _checked = v); - })) - .toList(), - ); - } -} - diff --git a/packages/widgets/lib/StatefulWidget/TabBarView/node1_base.dart b/packages/widgets/lib/StatefulWidget/TabBarView/node1_base.dart deleted file mode 100644 index b284591ef..000000000 --- a/packages/widgets/lib/StatefulWidget/TabBarView/node1_base.dart +++ /dev/null @@ -1,81 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 59, -// "name": 'TabBarView需要与TabBar联用', -// "priority": 1, -// "subtitle": "【controller】 : 控制器 【TabController】\n" -// "【children】 : 孩子们 【指示器颜色】\n" -// "【physics】 : 表现 【ScrollPhysics】", -// } -class CustomTabBarView extends StatefulWidget { - const CustomTabBarView({Key? key}) : super(key: key); - - @override - _CustomTabBarViewState createState() => _CustomTabBarViewState(); -} - -class _CustomTabBarViewState extends State with SingleTickerProviderStateMixin { - final List tabs = const [ - '风画庭', - '雨韵舍', - '雷鸣殿', - '电疾堂', - '霜寒阁', - '雪月楼', - ]; - late TabController _tabController; - - @override - void initState() { - super.initState(); - _tabController = TabController(vsync: this, length: tabs.length); - } - - @override - void dispose() { - _tabController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Column( - children: [ - _buildTabBar(), - Container( - color: Colors.purple, - width: MediaQuery.of(context).size.width, - height: 200, - child: _buildTableBarView()) - ], - ); - } - - Widget _buildTabBar() => TabBar( - onTap: (tab) => print(tab), - labelStyle: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), - unselectedLabelStyle: const TextStyle(fontSize: 16), - isScrollable: true, - controller: _tabController, - labelColor: Colors.blue, - indicatorWeight: 3, - indicatorPadding: const EdgeInsets.symmetric(horizontal: 10), - unselectedLabelColor: Colors.grey, - indicatorColor: Colors.orangeAccent, - tabs: tabs.map((e) => Tab(text: e)).toList(), - ); - - Widget _buildTableBarView() => TabBarView( - controller: _tabController, - children: tabs.map((e) => Center( - child: Text( - e, - style: const TextStyle( - color: Colors.white, - fontSize: 20, - ), - ))).toList()); -} diff --git a/packages/widgets/lib/StatefulWidget/TableRowInkWell/node1_base.dart b/packages/widgets/lib/StatefulWidget/TableRowInkWell/node1_base.dart deleted file mode 100644 index 9750631d4..000000000 --- a/packages/widgets/lib/StatefulWidget/TableRowInkWell/node1_base.dart +++ /dev/null @@ -1,92 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-26 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 151, -// "name": 'TableRowInkWell基本事件', -// "priority": 1, -// "subtitle": -// "【child】 : 子组件 【Widget】\n" -// "【onTap】 : 点击事件 【Function()】\n" -// "【onDoubleTap】 : 双击事件 【Function()】\n" -// "【onLongPress】 : 长按事件 【Function()】\n" -// "【onHighlightChanged】 : 高亮变化回调 【Function(bool)】", -// } -class CustomTableRowInkWell extends StatelessWidget { - const CustomTableRowInkWell({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - _ItemBean title = _ItemBean("单位称", "量纲", "单位", "单位名称", "单位符号"); - _ItemBean m = _ItemBean("长度", "L", "1m", "米", "m"); - _ItemBean kg = _ItemBean("质量", "M", "1Kg", "千克", "Kg"); - _ItemBean s = _ItemBean("时间", "T", "1s", "秒", "s"); - _ItemBean a = _ItemBean("安培", "Ι", "1A", "安培", "A"); - _ItemBean k = _ItemBean("热力学温度", "θ", "1K", "开尔文", "K"); - _ItemBean mol = _ItemBean("物质的量", "N", "1mol", "摩尔", "mol"); - _ItemBean cd = _ItemBean("发光强度", "J", "1cd", "坎德拉", "cd"); - - List<_ItemBean> data = [title, m, kg, s, a, k, mol, cd]; - - return SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Table( - columnWidths: const { - 0: FixedColumnWidth(80.0), - 1: FixedColumnWidth(80.0), - 2: FixedColumnWidth(80.0), - 3: FixedColumnWidth(80.0), - 4: FixedColumnWidth(80.0), - }, - defaultVerticalAlignment: TableCellVerticalAlignment.middle, - border: TableBorder.all( - color: Colors.orangeAccent, width: 1.0, style: BorderStyle.solid), - children: data - .map((item) => TableRow(children: [ - TableRowInkWell( - onTap: () => print('onTap'), - onDoubleTap: () => print('onDoubleTap'), - onLongPress: () => print('onLongPress'), - onHighlightChanged: (v) => print('onHighlightChanged:$v'), - child: Center( - child: Text( - item.name, - style: const TextStyle(color: Colors.blue), - )), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Center(child: Text(item.symbol)), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Center(child: Text(item.unitSymbol)), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Center(child: Text(item.unitName)), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Center(child: Text(item.unit)), - ), - ])) - .toList(), - ), - ); - } -} - -class _ItemBean { - String name; - String symbol; - String unit; - String unitName; - String unitSymbol; - - _ItemBean(this.name, this.symbol, this.unit, this.unitName, this.unitSymbol); -} - diff --git a/packages/widgets/lib/StatefulWidget/TextButton/node1_base.dart b/packages/widgets/lib/StatefulWidget/TextButton/node1_base.dart deleted file mode 100644 index 666738616..000000000 --- a/packages/widgets/lib/StatefulWidget/TextButton/node1_base.dart +++ /dev/null @@ -1,44 +0,0 @@ -import 'package:flutter/material.dart'; - - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 353 TextButton Material风格的文字按钮,默认只有文字,点击时有水波纹。可通过样式更改边框、颜色、阴影等属性。 -// { -// "widgetId": 353, -// "name": 'TextButton基本使用', -// "priority": 1, -// "subtitle": "【child】 : 是否具有滚动主体 【Widget】\n" -// "【onPressed】 : 点击事件 【VoidCallback】\n" -// "【onLongPress】 : 长按事件 【VoidCallback】", -// } - -class TextButtonDemo extends StatelessWidget { - const TextButtonDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - alignment: Alignment.center, - height: 60, - child: Wrap( - spacing: 20, - children: [ - TextButton( - child: const Text('TextButton 文字'), - onPressed: _onPressed, - onLongPress: _onLongPress, - ), - const TextButton( - child: Text('TextButton 禁用'), - onPressed: null, - onLongPress: null, - ), - ], - )); - } - - void _onPressed() {} - - void _onLongPress() {} -} diff --git a/packages/widgets/lib/StatefulWidget/TextButton/node2_style.dart b/packages/widgets/lib/StatefulWidget/TextButton/node2_style.dart deleted file mode 100644 index 7bb672ead..000000000 --- a/packages/widgets/lib/StatefulWidget/TextButton/node2_style.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 353, -// "name": 'TextButton样式', -// "priority": 2, -// "subtitle": "【style】 : 按钮样式 【ButtonStyle】\n" -// "【focusNode】 : 焦点 【FocusNode】\n" -// "【clipBehavior】 : 裁剪行为 【Clip】\n" -// "【autofocus】 : 自动聚焦 【bool】", -// } - -class TextButtonStyleDemo extends StatelessWidget { - const TextButtonStyleDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - alignment: Alignment.center, - child: Wrap( - spacing: 10, - children: [ - TextButton( - style: TextButton.styleFrom( - backgroundColor: Colors.blue, - padding: const EdgeInsets.symmetric(horizontal: 8), - primary: Colors.white, - elevation: 2, - shadowColor: Colors.orangeAccent), - child: const Text('TextButton 样式'), - onPressed: _onPressed, - onLongPress: _onLongPress, - ), - TextButton( - style: TextButton.styleFrom( - backgroundColor: Colors.white, - primary: Colors.black, - side: const BorderSide(color: Colors.blue, width: 1), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10))), - // elevation: 2, - shadowColor: Colors.orangeAccent), - child: const Text('TextButton 边线'), - autofocus: false, - onPressed: _onPressed, - onLongPress: _onLongPress, - ), - ], - ), - ); - } - - void _onPressed() {} - - void _onLongPress() {} -} diff --git a/packages/widgets/lib/StatefulWidget/TextField/node1_base.dart b/packages/widgets/lib/StatefulWidget/TextField/node1_base.dart deleted file mode 100644 index e95678cea..000000000 --- a/packages/widgets/lib/StatefulWidget/TextField/node1_base.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 54, -// "name": 'TextField基本用法', -// "priority": 1, -// "subtitle": -// "【controller】 : 控制器 【TextEditingController】\n" -// "【style】 : 文字样式 【TextStyle】\n" -// "【decoration】 : 装饰线 【InputDecoration】\n" -// "【onEditingComplete】 : 输入完成事件 【Function()】\n" -// "【onSubmitted】 : 提交事件 【Function(String)】\n" -// "【onChanged】 : 输入事件 【Function(String)】", -// } -class CustomTextField extends StatefulWidget { - const CustomTextField({Key? key}) : super(key: key); - - @override - _CustomTextFieldState createState() => _CustomTextFieldState(); -} - -class _CustomTextFieldState extends State { - final FocusNode _focusNode = FocusNode(); - late TextEditingController _controller; - - @override - void initState() { - super.initState(); - _controller = TextEditingController(); - } - - @override - void dispose() { - _controller.dispose(); - _focusNode.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return SizedBox( - width: 300, - child: TextField( - controller: _controller, - style: const TextStyle(color: Colors.blue), - decoration: const InputDecoration( - border: OutlineInputBorder(), - labelText: 'username', - ), - onEditingComplete: () { - print('onEditingComplete'); - }, - onChanged: (v) { - print('onChanged:' + v); - }, - onSubmitted: (v) { - FocusScope.of(context).requestFocus(_focusNode); - print('onSubmitted:' + v); - _controller.clear(); - }, - )); - } -} - diff --git a/packages/widgets/lib/StatefulWidget/TextField/node2_cursor.dart b/packages/widgets/lib/StatefulWidget/TextField/node2_cursor.dart deleted file mode 100644 index db6859016..000000000 --- a/packages/widgets/lib/StatefulWidget/TextField/node2_cursor.dart +++ /dev/null @@ -1,73 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 54, -// "name": 'TextField行数和cursor', -// "priority": 2, -// "subtitle": -// "【minLines】 : 最小行数 【int】\n" -// "【maxLines】 : 最大行数 【int】\n" -// "【cursorRadius】 : 光标半径 【Radius】\n" -// "【cursorColor】 : 光标颜色 【Color】\n" -// "【cursorWidth】 : 光标宽度 【double】\n" -// "【showCursor】 : 是否显示光标 【bool】\n" -// "【autofocus】 : 自动聚焦 【bool】", -// } -class CursorTextField extends StatefulWidget { - const CursorTextField({Key? key}) : super(key: key); - - @override - _CursorTextFieldState createState() => _CursorTextFieldState(); -} - -class _CursorTextFieldState extends State { - final FocusNode _focusNode = FocusNode(); - - @override - void dispose() { - _focusNode.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - _buildSubmitBtn(), - _buildTextField(context), - ], - ); - } - - Widget _buildTextField(BuildContext context) { - return SizedBox( - width: 300, - child: TextField( - style: const TextStyle(color: Colors.blue), - minLines: 3, - maxLines: 5, - cursorColor: Colors.green, - cursorRadius: const Radius.circular(3), - cursorWidth: 5, - showCursor: true, - decoration: const InputDecoration( - contentPadding: EdgeInsets.all(10), - hintText: "请输入...", - border: OutlineInputBorder(), - ), - onChanged: (v) {}, - ), - ); - } - - Widget _buildSubmitBtn() => ElevatedButton( - child: const Text( - "提交", - style: TextStyle(color: Colors.white, fontSize: 16), - ), - onPressed: () => FocusScope.of(context).requestFocus(_focusNode)); -} diff --git a/packages/widgets/lib/StatefulWidget/TextField/node3_decoration.dart b/packages/widgets/lib/StatefulWidget/TextField/node3_decoration.dart deleted file mode 100644 index 72b4e60d7..000000000 --- a/packages/widgets/lib/StatefulWidget/TextField/node3_decoration.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 54, -// "name": 'decoration的复杂装饰', -// "priority": 3, -// "subtitle": -// "InputDecoration有非常多的装饰点,对应点缀见代码:\n" -// "border: 边线相关\n" -// "helper: 左下角相关提示\n" -// "counter: 右下角相关提示\n" -// "prefix: 输入框内部最左侧\n" -// "suffix: 输入框内部最右侧\n" -// "label: 无焦点时文字\n" -// "label: 无焦点时文字\n" -// "hint: 提示文字相关\n" -// "border: 边线相关", -// } -class ComplexTextField extends StatelessWidget { - const ComplexTextField({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return const TextField( - decoration: InputDecoration( - border: OutlineInputBorder(), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.blue), - borderRadius: BorderRadius.only( - topLeft: Radius.circular(10), bottomLeft: Radius.circular(10))), - enabledBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.deepPurpleAccent), - borderRadius: BorderRadius.only( - topLeft: Radius.circular(10), bottomLeft: Radius.circular(10))), - labelText: 'username', - labelStyle: TextStyle(color: Colors.purple), - helperText: "help me", - helperStyle: TextStyle(color: Colors.blue), - - suffixText: "suffix", - suffixIcon: Icon(Icons.done), - suffixStyle: TextStyle(color: Colors.green), - - counterText: "counter", - counterStyle: TextStyle(color: Colors.orange), - - prefixText: "ID ", - prefixStyle: TextStyle(color: Colors.blue), - prefixIcon: Icon(Icons.language), - - fillColor: Color(0x110099ee), - filled: true, - - // errorText: "error", - // errorMaxLines: 1, - // errorStyle: TextStyle(color: Colors.red), - // errorBorder: UnderlineInputBorder(), - - hintText: "请输入用户名", - hintMaxLines: 1, - hintStyle: TextStyle(color: Colors.black38), - icon: Icon(Icons.assignment_ind), - )); - } -} diff --git a/packages/widgets/lib/StatefulWidget/TextFormField/node1_base.dart b/packages/widgets/lib/StatefulWidget/TextFormField/node1_base.dart deleted file mode 100644 index 9b2b372b7..000000000 --- a/packages/widgets/lib/StatefulWidget/TextFormField/node1_base.dart +++ /dev/null @@ -1,83 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-04-01 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 199, -// "name": 'TextFormField基本使用', -// "priority": 1, -// "subtitle": -// " 基本属性和TextField一致,详见之\n" -// "【validator】 : 验证函数 【FormFieldValidator 】\n" -// "【onFieldSubmitted】 : 提交回调 【ValueChanged】\n" -// "【onSaved】 : 表单save时回调 【FormFieldSetter】", -// } -class CustomTextFormField extends StatefulWidget { - const CustomTextFormField({Key? key}) : super(key: key); - - @override - _CustomTextFormFieldState createState() => _CustomTextFormFieldState(); -} - -class _CustomTextFormFieldState extends State { - final GlobalKey _formKey = GlobalKey(); - - @override - Widget build(BuildContext context) { - return Row( - children: [ - const SizedBox(width: 40), - Expanded( - child: TextFormField( - style: const TextStyle(textBaseline: TextBaseline.alphabetic), - decoration: const InputDecoration( - border: OutlineInputBorder(), - labelText: 'username', - ), - validator: _validateUsername, - onFieldSubmitted: _onFieldSubmitted, - onSaved: _onSaved, - ), - ), - _buildSubmitButton(context), - ], - ); - } - - String? _validateUsername(value) { - if (value.isEmpty) { - return '用户名不能为空'; - } - return null; - } - - void _onSaved(value) { - print('onSaved:' + value); - } - - void _onFieldSubmitted(value) { - print('onFieldSubmitted:' + value); - } - - Widget _buildSubmitButton(BuildContext context) => ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Colors.blue, - shape: const CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFDFDFDF)), - )), - onPressed: _onSubmit, - child: const Icon( - Icons.check, - color: Colors.white, - ), - ); - - void _onSubmit() { - if (_formKey.currentState == null) return; - _formKey.currentState!.save(); - if (_formKey.currentState!.validate()) { - FocusScope.of(context).requestFocus(FocusNode()); - } - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatefulWidget/Tooltip/node1_base.dart b/packages/widgets/lib/StatefulWidget/Tooltip/node1_base.dart deleted file mode 100644 index 4246fab43..000000000 --- a/packages/widgets/lib/StatefulWidget/Tooltip/node1_base.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-04-01 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 50, -// "name": 'Tooltip基本使用', -// "priority": 1, -// "subtitle": -// "【preferBelow】 : 是否首选下方 【bool】\n" -// "【padding】 : 内边距 【EdgeInsetsGeometry】\n" -// "【margin】 : 外边距 【EdgeInsetsGeometry】\n" -// "【message】 : 消息内容 【String】\n" -// "【showDuration】 : 展示时间 【Duration】\n" -// "【waitDuration】 : 悬浮出现时间 【Duration】\n" -// "【child】 : 孩子 【Widget】", -// } -class CustomTooltip extends StatelessWidget { - const CustomTooltip({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return const Tooltip( - preferBelow: true, - padding: EdgeInsets.all(5), - margin: EdgeInsets.all(5), - message: "天王盖地虎", - showDuration: Duration(seconds: 3), - waitDuration: Duration(milliseconds: 200), - child: Icon(Icons.info_outline), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/Tooltip/node2_decoration.dart b/packages/widgets/lib/StatefulWidget/Tooltip/node2_decoration.dart deleted file mode 100644 index 7e6d2207f..000000000 --- a/packages/widgets/lib/StatefulWidget/Tooltip/node2_decoration.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-04-01 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 50, -// "name": 'Tooltip的装饰', -// "priority": 2, -// "subtitle": -// "【decoration】 : 装饰对象 【Decoration】\n" -// "【textStyle】 : 文字样式 【double】", -// } -class DecorationTooltip extends StatelessWidget { - const DecorationTooltip({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return const Tooltip( - preferBelow: false, - padding: EdgeInsets.all(5), - margin: EdgeInsets.all(2), - message: "宝塔镇河妖", - textStyle: TextStyle( - color: Colors.red, - shadows: [Shadow(color: Colors.white, - offset: Offset(1, 1))]), - decoration: BoxDecoration(boxShadow: [ - BoxShadow( - color: Colors.orangeAccent, - offset: Offset(1, 1), blurRadius: 8) - ]), - child: Icon(Icons.info_outline)); - } -} diff --git a/packages/widgets/lib/StatefulWidget/TweenAnimationBuilder/node1_base.dart b/packages/widgets/lib/StatefulWidget/TweenAnimationBuilder/node1_base.dart deleted file mode 100644 index 262fd484e..000000000 --- a/packages/widgets/lib/StatefulWidget/TweenAnimationBuilder/node1_base.dart +++ /dev/null @@ -1,62 +0,0 @@ - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 226 TweenAnimationBuilder 渐变动画构造器 -/// 通过渐变器 Tween 对相关属性进行渐变动画,通过 builder 进行局部构建,减少刷新范围。不需要自定义动画器,可指定动画时长、曲线、结束回调。 -/// -// { -// "widgetId": 226, -// "name": 'TweenAnimationBuilder 使用案例', -// "priority": 1, -// "subtitle": -// "【tween】 : *渐变器 【Tween】\n" -// "【duration】 : *时长 【Duration】\n" -// "【builder】 : *构造器 【ValueWidgetBuilder】\n" -// "【curve】 : 动画曲线 【Curve】\n" -// "【onEnd】 : 结束回调 【VoidCallback】\n" -// "【child】 : 子组件 【Widget】", -// } - -class TweenAnimationBuilderDemo extends StatefulWidget { - const TweenAnimationBuilderDemo({Key? key}) : super(key: key); - - @override - _TweenAnimationBuilderDemoState createState() => - _TweenAnimationBuilderDemoState(); -} - -class _TweenAnimationBuilderDemoState extends State { - Color _value = Colors.red; - - @override - Widget build(BuildContext context) { - return TweenAnimationBuilder( - tween: ColorTween(begin: Colors.blue, end: _value), - duration: const Duration(milliseconds: 800), - builder: (BuildContext context, Color? color, Widget? child) { - return GestureDetector( - onTap: () { - setState(() { - _value = _value == Colors.red ? Colors.blue : Colors.red; - }); - }, - child: Container( - width: 40, - height: 40, - child: child, - decoration: BoxDecoration( - color: color, - borderRadius: BorderRadius.circular(5) - ), - ), - ); - }, - child: const Icon( - Icons.android_outlined, - color: Colors.white, - ), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/UniqueWidget/node1_base.dart b/packages/widgets/lib/StatefulWidget/UniqueWidget/node1_base.dart deleted file mode 100644 index 39ecd1dc5..000000000 --- a/packages/widgets/lib/StatefulWidget/UniqueWidget/node1_base.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 243 UniqueWidget 唯一组件 -/// 抽象类,必须提供一个 GlobalKey 进行身份标识,该类型组件只会 inflated 一个实例,同一时刻也只会有一个状态,可以通过 currentState 属性获取状态。 -/// -// { -// "widgetId": 243, -// "name": 'UniqueWidget 介绍', -// "priority": 1, -// "subtitle": "【child】 : 子组件 【Widget】", -// } - -class UniqueWidgetDemo extends StatelessWidget { - const UniqueWidgetDemo({Key? key}) : super(key: key); - - final String info = - '该类是抽象类,在 Flutter 框架层没有实现类,也没有其他源码使用到它,说明它基本上没啥用。' - '本质上它也非常简单,就是为组件添加一个 GlobalKey,在 Element#inflateWidget 时,会校验组件是否有 GlobalKey ,' - '如果有,则根据 key 找到之前的对应的 Element,就不会触发 Widget#createElement。为了方便获取 State,该类暴露 currentState 属性。' - '你瞄一下源码,就能看到这个组件是多么简单,简单到可以自己完成,以至于没什么大用。'; - - @override - Widget build(BuildContext context) { - return Container( - color: Colors.blue.withOpacity(0.1), - padding: const EdgeInsets.all(10), - margin: const EdgeInsets.all(10), - child: Text(info), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/ValueListenableBuilder/node1_base.dart b/packages/widgets/lib/StatefulWidget/ValueListenableBuilder/node1_base.dart deleted file mode 100644 index a2a44aead..000000000 --- a/packages/widgets/lib/StatefulWidget/ValueListenableBuilder/node1_base.dart +++ /dev/null @@ -1,56 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/7/21 -/// contact me by email 1981462002@qq.com -/// 说明: 255 ValueListenableBuilder 1 可以监听一个值,当其变化时通过builder回调能重建界面,避免使用setState刷新。 -// { -// "widgetId": 255, -// "name": 'ValueListenableBuilder基本使用', -// "priority": 1, -// "subtitle": "【builder】: 组件构造器 【ValueWidgetBuilder】\n" -// "【valueListenable】: 监听值 【ValueListenable】\n" -// "【child】: 子组件 【Widget】", -// } - -class ValueListenableBuilderDemo extends StatelessWidget { - ValueListenableBuilderDemo({Key? key}) : super(key: key); - - final ValueNotifier _counter = ValueNotifier(0); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 200, - child: Scaffold( - appBar: AppBar(title: const Text("ValueListenableBuilder")), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text('You have pushed the button this many times:'), - ValueListenableBuilder( - builder: _buildWithValue, - valueListenable: _counter, - child: const Text('I am Child!'), - ) - ], - ), - ), - floatingActionButton: FloatingActionButton( - child: const Icon(Icons.plus_one), - onPressed: () => _counter.value += 1, - ), - ), - ); - } - - Widget _buildWithValue(BuildContext context, int value, Widget? child) { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Text('$value'), - child ?? const SizedBox.shrink(), - ], - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/WidgetInspector/node1_base.dart b/packages/widgets/lib/StatefulWidget/WidgetInspector/node1_base.dart deleted file mode 100644 index f44c6116b..000000000 --- a/packages/widgets/lib/StatefulWidget/WidgetInspector/node1_base.dart +++ /dev/null @@ -1,63 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/8/16 -/// contact me by email 1981462002@qq.com -/// 说明: 234 WidgetInspector 该组件可以让你很方便地查看子组件层级结构,是Flutter Inspector插件的功能之一。 -// { -// "widgetId": 234, -// "name": "WidgetInspector基本使用", -// "priority": 1, -// "subtitle": "【child】 : 子组件 【Widget】\n" -// "【selectButtonBuilder】: *选择按钮构造器 【InspectorSelectButtonBuilder】", -// } -class WidgetInspectorDemo extends StatelessWidget { - const WidgetInspectorDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 200, - child: WidgetInspector( - child: const HomePage(), - selectButtonBuilder: _selectButtonBuilder, - ), - ); - } - - Widget _selectButtonBuilder(BuildContext context, onPressed) { - onPressed(); - return Container(); - } -} - -class HomePage extends StatefulWidget { - const HomePage({Key? key}) : super(key: key); - - @override - _HomePageState createState() => _HomePageState(); -} - -class _HomePageState extends State { - int _count = 0; - - @override - Widget build(BuildContext context) { - return Scaffold( - body: Container( - alignment: const Alignment(0, 0.7), - child: Text( - '你点击了$_count次', - style: const TextStyle(fontSize: 18, color: Colors.blue), - ), - ), - floatingActionButton: FloatingActionButton( - child: const Icon(Icons.add), - onPressed: () { - setState(() { - _count++; - }); - }, - ), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/WidgetsApp/node1_base.dart b/packages/widgets/lib/StatefulWidget/WidgetsApp/node1_base.dart deleted file mode 100644 index 0a671a60a..000000000 --- a/packages/widgets/lib/StatefulWidget/WidgetsApp/node1_base.dart +++ /dev/null @@ -1,132 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/8/16 -/// contact me by email 1981462002@qq.com -/// 说明: 236 WidgetsApp 集合一个应用程序需要的部件,如路由、语言、一些调试开关等。也是实现MaterialApp和CupertinoApp的核心组件。 -// { -// "widgetId": 236, -// "name": "WidgetsApp基本使用", -// "priority": 1, -// "subtitle": "【pageRouteBuilder】 : *路由构造器 【PageRouteFactory】\n" -// "【color】: *颜色 【Color】\n" -// "【debugShowWidgetInspector】: 是否显示z组件查看器 【bool】\n" -// "其他属性基本上同MaterialApp,详见之。", -// } -class WidgetsAppDemo extends StatefulWidget { - const WidgetsAppDemo({Key? key}) : super(key: key); - - @override - _WidgetsAppDemoState createState() => _WidgetsAppDemoState(); -} - -class _WidgetsAppDemoState extends State { - bool _debugShowCheckedModeBanner = false; - bool _debugShowWidgetInspector = false; - bool _showPerformanceOverlay = false; - - @override - Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - _buildSwitchers(), - SizedBox( - height: 250, - child: WidgetsApp( - color: Colors.blue, - debugShowCheckedModeBanner: _debugShowCheckedModeBanner, - showPerformanceOverlay: _showPerformanceOverlay, - debugShowWidgetInspector: _debugShowWidgetInspector, - pageRouteBuilder: - (RouteSettings settings, WidgetBuilder builder) { - return MaterialPageRoute(settings: settings, builder: builder); - }, - home: const HomePage(), - ), - ), - ], - ); - } - - Widget _buildSwitchers() { - return DefaultTextStyle( - style: const TextStyle(color: Colors.blue), - child: Wrap( - spacing: 10, - children: [ - Column( - children: [ - Switch( - value: _showPerformanceOverlay, - onChanged: (v) { - setState(() { - _showPerformanceOverlay = v; - }); - }, - ), - const Text('性能浮层') - ], - ), - Column( - children: [ - Switch( - value: _debugShowCheckedModeBanner, - onChanged: (v) { - setState(() { - _debugShowCheckedModeBanner = v; - }); - }, - ), - const Text('开启角标') - ], - ), - Column( - children: [ - Switch( - value: _debugShowWidgetInspector, - onChanged: (v) { - setState(() { - _debugShowWidgetInspector = v; - }); - }, - ), - const Text('检查器') - ], - ) - ], - ), - ); - } -} - -class HomePage extends StatefulWidget { - const HomePage({Key? key}) : super(key: key); - - @override - _HomePageState createState() => _HomePageState(); -} - -class _HomePageState extends State { - int _count = 0; - - @override - Widget build(BuildContext context) { - return Scaffold( - body: Container( - alignment: const Alignment(0, 0.7), - child: Text( - '你点击了$_count次', - style: const TextStyle(fontSize: 18, color: Colors.blue), - ), - ), - floatingActionButton: FloatingActionButton( - child: const Icon(Icons.add), - onPressed: () { - setState(() { - _count++; - }); - }, - ), - ); - } -} diff --git a/packages/widgets/lib/StatefulWidget/WillPopScope/node1_base.dart b/packages/widgets/lib/StatefulWidget/WillPopScope/node1_base.dart deleted file mode 100644 index e81e2f59d..000000000 --- a/packages/widgets/lib/StatefulWidget/WillPopScope/node1_base.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: - - -// { -// "widgetId": 170, -// "name": 'WillPopScope使用', -// "priority": 1, -// "subtitle": "【child】 : 子组件 【Widget】\n" -// "【onWillPop】 : 返回回调 【WillPopCallback】", -// } -class CustomWillPopScope extends StatelessWidget { - const CustomWillPopScope({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return WillPopScope( - child: const BackButton(), - onWillPop: () => _willPop(context), - ); - } - - Future _willPop(context) async { - bool? exit = await showDialog( - context: context, - builder: (ctx) => AlertDialog( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10))), - title: const Text('提示'), - content: const Text('你确定要离开此页吗?'), - actions: [ - ElevatedButton( - onPressed: () => Navigator.of(ctx).pop(true), - child: const Text('确定'), - ), - ElevatedButton( - onPressed: () => Navigator.of(ctx).pop(false), - child: const Text('取消'), - ), - ], - ), - ); - print('====_willPop==:$exit========'); - - return exit??false; - } -} diff --git a/packages/widgets/lib/StatefulWidget/YearPicker/node1_base.dart b/packages/widgets/lib/StatefulWidget/YearPicker/node1_base.dart deleted file mode 100644 index 886a2837e..000000000 --- a/packages/widgets/lib/StatefulWidget/YearPicker/node1_base.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 136, -// "name": 'YearPicker基本使用', -// "priority": 1, -// "subtitle": "【selectedDate】 : 选中日期 【DateTime】\n" -// "【firstDate】 : 最前日期限制 【DateTime】\n" -// "【lastDate】 : 最后日期限制 【DateTime】\n" -// "【onChanged】 : 点击回调 【Function(DateTime)】", -// } -class CustomYearPicker extends StatefulWidget { - const CustomYearPicker({Key? key}) : super(key: key); - - @override - _CustomYearPickerState createState() => _CustomYearPickerState(); -} - -class _CustomYearPickerState extends State { - DateTime _date = DateTime.now(); - - @override - Widget build(BuildContext context) { - return SizedBox( - height:150, - child: YearPicker( - selectedDate: _date, - onChanged: (date) => setState(() => _date = date), - firstDate: DateTime(2018), - lastDate: DateTime(2030), - ), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/AboutDialog/node1_base.dart b/packages/widgets/lib/StatelessWidget/AboutDialog/node1_base.dart deleted file mode 100644 index 470f0e8fd..000000000 --- a/packages/widgets/lib/StatelessWidget/AboutDialog/node1_base.dart +++ /dev/null @@ -1,76 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 130, -// "name": 'AboutDialog基本使用', -// "priority": 1, -// "subtitle": -// "【applicationIcon】 : 左上图标 【Widget】\n" -// "【applicationVersion】 : 版本号 【String】\n" -// "【applicationName】 : 应用名 【String】\n" -// "【applicationLegalese】 : 应用律术 【String】\n" -// "【children】 : 子组件列表 【List】", -// } - -class CustomAboutDialog extends StatelessWidget { - const CustomAboutDialog({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Stack( - children: [ - _buildAboutDialog(), - Positioned(top: 50, right: 20, child: _buildRaisedButton(context)), - ], - ); - } - - Widget _buildRaisedButton(BuildContext context) => ElevatedButton( - style: TextButton.styleFrom( - backgroundColor: Colors.blue, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10)), - ), - ), - onPressed: () { - showDialog(context: context, builder: (ctx) => _buildAboutDialog()); - }, - child: const Text( - 'Just Show It', - style: TextStyle(color: Colors.white), - ), - ); - - AboutDialog _buildAboutDialog() { - return AboutDialog( - applicationIcon: const FlutterLogo(), - applicationVersion: 'v0.0.1', - applicationName: 'Flutter Unit', - applicationLegalese: 'Copyright© 2018-2020 张风捷特烈', - children: [ - Container( - margin: const EdgeInsets.only(top: 20), - width: 80, - height: 80, - child: Image.asset('assets/images/icon_head.webp')), - Container( - margin: const EdgeInsets.only(top: 10), - alignment: Alignment.center, - child: const Text( - 'The King Of Coder.', - style: TextStyle( - color: Colors.white, - fontSize: 20, - shadows: [ - Shadow( - color: Colors.blue, offset: Offset(.5, .5), blurRadius: 3) - ], - ), - )) - ], - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/AboutListTile/node1_base.dart b/packages/widgets/lib/StatelessWidget/AboutListTile/node1_base.dart deleted file mode 100644 index 72c6e4943..000000000 --- a/packages/widgets/lib/StatelessWidget/AboutListTile/node1_base.dart +++ /dev/null @@ -1,46 +0,0 @@ - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 193, -// "name": 'AboutListTile基本使用', -// "priority": 1, -// "subtitle": -// "【icon】 : 左图标 【Widget】\n" -// "【applicationIcon】 : 左上图标 【Widget】\n" -// "【applicationVersion】 : 版本号 【String】\n" -// "【applicationName】 : 应用名 【String】\n" -// "【applicationLegalese】 : 应用律术 【String】\n" -// "【aboutBoxChildren】 : 弹框内容组件 【List】", -// } - -class AboutListTileDemo extends StatelessWidget { - const AboutListTileDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return const AboutListTile( - icon: Icon(Icons.info), - applicationIcon: FlutterLogo(), - applicationName: 'Flutter Unit', - applicationVersion: 'v0.0.1', - applicationLegalese: 'Copyright© 2018-2020 张风捷特烈', - aboutBoxChildren: [ - Padding( - padding: EdgeInsets.all(10.0), - child: Text( - ' FlutterUnit是【张风捷特烈】的开源项目,' - '收录Flutter的300+组件,并附加详细介绍以及操作交互,' - '希望帮助广大编程爱好者入门Flutter。' - '更多知识可以关注掘金账号、公众号【编程之王】。', - style: TextStyle(color: Color(0xff999999), fontSize: 16), - textAlign: TextAlign.justify, - ), - ), - ], - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/ActionChip/node1_base.dart b/packages/widgets/lib/StatelessWidget/ActionChip/node1_base.dart deleted file mode 100644 index e73d2e379..000000000 --- a/packages/widgets/lib/StatelessWidget/ActionChip/node1_base.dart +++ /dev/null @@ -1,38 +0,0 @@ - -/// create by 张风捷特烈 on 2020/4/25 -/// contact me by email 1981462002@qq.com -/// 说明: -/// -// { -/// "widgetId": 13, -// "priority": 1, -// "name": "ActionChip的普通表现如下", -// "subtitle": "【onPressed】: 点击事件 【Function】\n" -// "【pressElevation】: 按下时影深 【double】\n" -// "其他属性同Chip组件,无右侧组件。", -// } -/// - - -import 'package:flutter/material.dart'; -import 'package:widgets/utils/dialog_about.dart'; - - -class CustomActionChip extends StatelessWidget { - const CustomActionChip({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return ActionChip( - padding: const EdgeInsets.all(5), - labelPadding: const EdgeInsets.all(3), - label: const Text("This is a ActionChip."), - backgroundColor: Colors.grey.withAlpha(66), - avatar: Image.asset("assets/images/icon_head.webp"), - shadowColor: Colors.orangeAccent, - elevation: 3, - pressElevation: 5, - onPressed: ()=> DialogAbout.show(context), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/AlertDialog/node1_base.dart b/packages/widgets/lib/StatelessWidget/AlertDialog/node1_base.dart deleted file mode 100644 index 233be35ff..000000000 --- a/packages/widgets/lib/StatelessWidget/AlertDialog/node1_base.dart +++ /dev/null @@ -1,116 +0,0 @@ - - -/// create by 张风捷特烈 on 2020-03-24 -/// contact me by email 1981462002@qq.com -/// 说明: -/// -// { -// "widgetId": 127, -// "name": 'AlertDialog基本使用', -// "priority": 1, -// "subtitle": -// "【title】 : 顶部组件 【Widget】\n" -// "【content】 : 内容组件 【Widget】\n" -// "【titleTextStyle】 : 顶部文字样式 【TextStyle】\n" -// "【contentTextStyle】 : 内容文字样式 【TextStyle】\n" -// "【titlePadding】 : 顶部内边距 【EdgeInsetsGeometry】\n" -// "【contentPadding】 : 内容内边距 【EdgeInsetsGeometry】\n" -// "【actions】 : 右下角组件列表 【List】\n" -// "【backgroundColor】 : 背景色 【Color】\n" -// "【elevation】 : 影深 【double】\n" -// "【shape】 : 形状 【ShapeBorder】", -// } - -import 'package:flutter/material.dart'; - -class CustomAlertDialog extends StatelessWidget { - const CustomAlertDialog({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Column( - children: [ - _buildRaisedButton(context), - _buildAlertDialog(), - ], - ); - } - - Widget _buildRaisedButton(BuildContext context) => ElevatedButton( - style: TextButton.styleFrom( - backgroundColor: Colors.blue, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10)), - ), - ), - onPressed: () { - showDialog(context: context, builder: (ctx) => _buildAlertDialog()); - }, - child: const Text( - 'Just Show It !', - style: TextStyle(color: Colors.white), - ), - ); - - Widget _buildAlertDialog() { - return AlertDialog( - title: _buildTitle(), - titleTextStyle: const TextStyle(fontSize: 20, color: Colors.black), - titlePadding: const EdgeInsets.only( - top: 5, - left: 20, - ), - contentPadding: const EdgeInsets.symmetric(horizontal: 5), - backgroundColor: Colors.white, - content: _buildContent(), - actions: const [ - Icon(Icons.android, color: Colors.blue,), - Icon(Icons.add, color: Colors.blue,), - Icon(Icons.g_translate, color: Colors.blue,), - Icon(Icons.games, color: Colors.blue,), - ], - elevation: 4, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10))), - ); - } - - Widget _buildTitle() { - return Row( - //标题 - children: [ - Image.asset( - "assets/images/icon_head.webp", - width: 30, - height: 30, - ), - const SizedBox(width: 10,), - const Expanded( - child: Text( - "关于", - style: TextStyle(fontSize: 18), - )), - const CloseButton() - ], - ); - } - - Widget _buildContent() { - return Column( - mainAxisSize: MainAxisSize.min, - children: const [ - Padding( - padding: EdgeInsets.all(10.0), - child: Text( - ' FlutterUnit是【张风捷特烈】的开源项目,' - '收录Flutter的200+组件,并附加详细介绍以及操作交互,' - '希望帮助广大编程爱好者入门Flutter。' - '更多知识可以关注掘金账号、公众号【编程之王】。', - style: TextStyle(color: Color(0xff999999), fontSize: 16), - textAlign: TextAlign.justify, - ), - ), - ], - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/AnimatedIcon/node1_base.dart b/packages/widgets/lib/StatelessWidget/AnimatedIcon/node1_base.dart deleted file mode 100644 index 3621c672c..000000000 --- a/packages/widgets/lib/StatelessWidget/AnimatedIcon/node1_base.dart +++ /dev/null @@ -1,69 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 125, -// "name": 'AnimatedIcon基本使用', -// "priority": 1, -// "subtitle": -// "【icon】 : 动画图标数据 【AnimatedIcons】\n" -// "【size】 : 大小 【double】\n" -// "【color】 : 颜色 【Color】\n" -// "【progress】 : 动画 【Animation】", -// } -class CustomAnimatedIcon extends StatefulWidget { - const CustomAnimatedIcon({Key? key}) : super(key: key); - - @override - _CustomAnimatedIconState createState() => _CustomAnimatedIconState(); -} - -class _CustomAnimatedIconState extends State - with SingleTickerProviderStateMixin { - late AnimationController _ctrl; - - @override - void initState() { - _ctrl = AnimationController(vsync: this, duration: const Duration(seconds: 1)); - _ctrl.forward(); - super.initState(); - } - - @override - void dispose() { - _ctrl.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: () => _ctrl.forward(from: 0), - child: Wrap( - runSpacing: 30, - children: _buildChildren(), - ), - ); - } - - final Map data = { - Colors.orange: AnimatedIcons.menu_arrow, - Colors.blue: AnimatedIcons.ellipsis_search, - Colors.red: AnimatedIcons.close_menu, - Colors.green: AnimatedIcons.arrow_menu, - Colors.cyanAccent: AnimatedIcons.play_pause, - Colors.purple: AnimatedIcons.pause_play, - }; - - List _buildChildren() => - data.keys - .map((Color color) => AnimatedIcon( - size: 50, - color: color, - icon: data[color]!, - progress: _ctrl, - )) - .toList(); -} diff --git a/packages/widgets/lib/StatelessWidget/Autocomplete/node1_base.dart b/packages/widgets/lib/StatelessWidget/Autocomplete/node1_base.dart deleted file mode 100644 index 52c906ba9..000000000 --- a/packages/widgets/lib/StatelessWidget/Autocomplete/node1_base.dart +++ /dev/null @@ -1,47 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2022-04-18 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 356, -// "name": 'Autocomplete基本使用', -// "priority": 1, -// "subtitle": "【optionsBuilder】 : 选项构造器 【AutocompleteOptionsBuilder】\n" -// "【onSelected】 : 选择时回调 【AutocompleteOnSelected】", -// } -class AutocompleteDemo extends StatelessWidget { - const AutocompleteDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Autocomplete( - optionsBuilder: buildOptions, - onSelected: onSelected, - ); - } - - void onSelected(String selection) { - debugPrint('当前选择了 $selection'); - } - - Future> buildOptions( - TextEditingValue textEditingValue, - ) async { - if (textEditingValue.text == '') { - return const Iterable.empty(); - } - return searchByArgs(textEditingValue.text); - } - - Future> searchByArgs(String args) async{ - // 模拟网络请求 - await Future.delayed(const Duration(milliseconds: 200)); - const List data = [ - 'toly', 'toly49', 'toly42', 'toly56', - 'card', 'ls', 'alex', 'fan sha', - ]; - return data.where((String name) => name.contains(args)); - } -} diff --git a/packages/widgets/lib/StatelessWidget/Autocomplete/node2_type.dart b/packages/widgets/lib/StatelessWidget/Autocomplete/node2_type.dart deleted file mode 100644 index 66ac21242..000000000 --- a/packages/widgets/lib/StatelessWidget/Autocomplete/node2_type.dart +++ /dev/null @@ -1,227 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2022-04-18 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 356, -// "name": 'Autocomplete的泛型', -// "priority": 2, -// "subtitle": "【optionsViewBuilder】 : 面板构造器 【AutocompleteOptionsViewBuilder】\n" -// "【fieldViewBuilder】 : 输入构造器 【AutocompleteFieldViewBuilder】\n" -// "【displayStringForOption】 : 文字展示 【AutocompleteOptionToString】\n", -// } -class AutocompleteType extends StatefulWidget { - const AutocompleteType({Key? key}) : super(key: key); - - @override - State createState() => _AutocompleteTypeState(); -} - -class _AutocompleteTypeState extends State { - late TextEditingController _controller; - User? user; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 300, - child: Scaffold( - appBar: AppBar( - automaticallyImplyLeading: false, - title: buildAutocomplete(), - ), - body: Center( - child: Text( - user != null ? user!.name : '未选择', - style: const TextStyle(fontSize: 40), - )), - ), - ); - } - - Widget buildAutocomplete() { - return Autocomplete( - optionsBuilder: buildOptions, - onSelected: onSelected, - optionsViewBuilder: _buildOptionsView, - fieldViewBuilder: _buildFieldView, - displayStringForOption: (user) => user.name, - ); - } - - @override - void dispose() { - // _controller.dispose(); - super.dispose(); - } - - void onSelected(User selection) { - debugPrint('当前选择了 $selection'); - setState(() { - user = selection; - }); - } - - Future> buildOptions( - TextEditingValue textEditingValue, - ) async { - if (textEditingValue.text == '') { - return const Iterable.empty(); - } - return searchByArgs(textEditingValue.text); - } - - Future> searchByArgs(String args) async { - // 模拟网络请求 - await Future.delayed(const Duration(milliseconds: 200)); - const List data = [ - User('toly', true, 'icon_5.webp'), - User('toly49', false, 'icon_6.webp'), - User('toly42', true, 'icon_7.webp'), - User('toly56', false, 'icon_8.webp'), - User('card', true, 'icon_5.webp'), - User('ls', true, 'icon_6.webp'), - User('alex', true, 'icon_7.webp'), - User('fan sha', false, 'icon_8.webp'), - ]; - return data.where((User user) => user.name.contains(args)); - } - - Widget _buildFieldView( - BuildContext context, - TextEditingController textEditingController, - FocusNode focusNode, - VoidCallback onFieldSubmitted) { - _controller = textEditingController; - return SizedBox( - height: 34, - child: TextFormField( - controller: textEditingController, - decoration: const InputDecoration( - filled: true, - fillColor: Color(0xffF7F8FA), - prefixIcon: Icon(Icons.search), - contentPadding: EdgeInsets.only(top: 1), - border: UnderlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.all(Radius.circular(19)), - ), - hintText: "输入用户名 (toly)", - hintStyle: TextStyle(fontSize: 13)), - focusNode: focusNode, - onFieldSubmitted: (String value) { - onFieldSubmitted(); - }, - ), - ); - } - - Widget _buildOptionsView(BuildContext context, - AutocompleteOnSelected onSelected, Iterable options) { - return Align( - alignment: Alignment.topCenter, - child: Padding( - padding: const EdgeInsets.only(top: 20), - child: Material( - child: ConstrainedBox( - constraints: BoxConstraints(maxHeight: 150), - child: ListView.builder( - padding: EdgeInsets.zero, - itemBuilder: (_, index) { - final User option = options.elementAt(index); - return _UserItem( - onSelected: onSelected, - user: option, - args: _controller.text, - ); - }, - itemCount: options.length, - ), - ), - ), - ), - ); - } -} - -class _UserItem extends StatelessWidget { - final AutocompleteOnSelected? onSelected; - final String args; - final User user; - - const _UserItem({ - Key? key, - this.onSelected, - required this.user, - required this.args, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return InkWell( - onTap: () => onSelected?.call(user), - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 6), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - CircleAvatar( - foregroundColor: Colors.transparent, - backgroundImage: - AssetImage('assets/images/head_icon/${user.image}'), - ), - const SizedBox(width: 20), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Text.rich(formSpan(user.name, args)), - Text( - '性别: ${user.man ? '男' : '女'}', - style: const TextStyle(color: Colors.grey), - ), - ], - ), - ], - ), - ), - ); - } - - final TextStyle lightTextStyle = const TextStyle( - color: Colors.blue, - fontWeight: FontWeight.bold, - ); - - InlineSpan formSpan(String src, String pattern) { - List span = []; - List parts = src.split(pattern); - if (parts.length > 1) { - for (int i = 0; i < parts.length; i++) { - span.add(TextSpan(text: parts[i])); - if (i != parts.length - 1) { - span.add(TextSpan(text: pattern, style: lightTextStyle)); - } - } - } else { - span.add(TextSpan(text: src)); - } - return TextSpan(children: span); - } -} - -class User { - final String name; - final bool man; - final String image; - - const User(this.name, this.man, this.image); - - @override - String toString() { - return 'User{name: $name, man: $man, image: $image}'; - } -} diff --git a/packages/widgets/lib/StatelessWidget/BackButton/node1_base.dart b/packages/widgets/lib/StatelessWidget/BackButton/node1_base.dart deleted file mode 100644 index 90d65cfcc..000000000 --- a/packages/widgets/lib/StatelessWidget/BackButton/node1_base.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 31, -// "priority": 1, -// "name": "BackButton属性", -// "subtitle": "【color】: 颜色 【Color】\n" -// "【style】: 按钮样式 【ButtonStyle?】\n" -// "【onPressed】: 点击事件 【Function】\n" -// " onPressed为空会退出当前栈", -// } -class CustomBackButton extends StatelessWidget { - CustomBackButton({Key? key}) : super(key: key); - - final List data = [ - Colors.red, - Colors.yellow, - Colors.blue, - Colors.green - ]; - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 10, - children: data - .map((e) => BackButton( - color: e, - )) - .toList()); - } -} diff --git a/packages/widgets/lib/StatelessWidget/BackButtonIcon/node1_base.dart b/packages/widgets/lib/StatelessWidget/BackButtonIcon/node1_base.dart deleted file mode 100644 index db69b2aa1..000000000 --- a/packages/widgets/lib/StatelessWidget/BackButtonIcon/node1_base.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2023/11/28 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 272, -// "priority": 1, -// "name": "BackButtonIcon 效果", -// "subtitle": "通过 _ActionIcon 组件根据 ActionIconTheme 主题,来适配不同平台的返回按钮图标。", -// } -class BackButtonIconDemo extends StatelessWidget { - const BackButtonIconDemo({super.key}); - - @override - Widget build(BuildContext context) { - return const BackButtonIcon(); - } -} diff --git a/packages/widgets/lib/StatelessWidget/Badge/node_1.dart b/packages/widgets/lib/StatelessWidget/Badge/node_1.dart deleted file mode 100644 index c0fd4359f..000000000 --- a/packages/widgets/lib/StatelessWidget/Badge/node_1.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2023/01/27 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 258, -// "name": 'Badge 圆点标记的使用', -// "priority": 1, -// "subtitle": "【backgroundColor】 : 背景色 【Color?】\n" -// "【isLabelVisible】 : 是否显示标记 【bool】\n" -// "【smallSize】 : 无标签时直径 【double?】\n" -// "【child】 : 子组件 【Widget?】", -// } - -class BadgeDemo extends StatelessWidget { - const BadgeDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return const Wrap( - spacing: 20, - children: [ - Badge( - backgroundColor: Colors.redAccent, - smallSize: 10, - child: Icon(Icons.update,size: 36,color: Colors.green,), - ), - Badge( - backgroundColor: Colors.redAccent, - smallSize: 10, - isLabelVisible: false, - child: Icon(Icons.update,size: 36,color: Colors.green,), - ), - ], - ); - } -} - diff --git a/packages/widgets/lib/StatelessWidget/Badge/node_2.dart b/packages/widgets/lib/StatelessWidget/Badge/node_2.dart deleted file mode 100644 index 028649c65..000000000 --- a/packages/widgets/lib/StatelessWidget/Badge/node_2.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2023/01/27 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 258, -// "name": 'Badge 标签标记', -// "priority": 2, -// "subtitle": "【label】 : 标题组件 【Widget?】\n" -// "【textStyle】 : 标题颜色 【TextStyle?】\n" -// "【textColor】 : 标题样式 【Color?】\n" -// "【padding】 : 标题边距 【EdgeInsetsGeometry?】\n" -// "【largeSize】 : 有标签时高度 【double?】\n", -// } - -class BadgeLabelDemo extends StatelessWidget { - const BadgeLabelDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return const Badge( - backgroundColor: Colors.red, - label: Text('99'), - textStyle: TextStyle(fontSize: 8,color: Colors.red), - padding: EdgeInsets.symmetric(horizontal: 8,vertical: 2), - largeSize: 14, - child: Icon(Icons.message,size: 36,color: Colors.indigo,), - ); - } -} - diff --git a/packages/widgets/lib/StatelessWidget/Badge/node_3.dart b/packages/widgets/lib/StatelessWidget/Badge/node_3.dart deleted file mode 100644 index 4ad90b415..000000000 --- a/packages/widgets/lib/StatelessWidget/Badge/node_3.dart +++ /dev/null @@ -1,60 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2023/01/27 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 258, -// "name": 'Badge 的偏移量', -// "priority": 3, -// "subtitle": "【offset】 : 标记偏移量 【Offset?】\n" -// "【alignment】 : 标题偏移 【AlignmentDirectional?】", -// } - -class BadgeAlignOffsetDemo extends StatelessWidget { - const BadgeAlignOffsetDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - final List info = ['默认偏移和对齐', '偏移-4,-4', '偏移-2,-2;右下角']; - final List offsets = [ - null, - const Offset(-4, -4), - const Offset(-2, -2) - ]; - final List alignments = [null, null, Alignment.bottomRight]; - - return Wrap( - spacing: 40, - children: info - .asMap() - .keys - .map((int i) => _buildShowItem(info[i], offsets[i], alignments[i])) - .toList(), - ); - } - - Widget _buildShowItem(String info, Offset? offset, Alignment? alignment) { - return Wrap( - spacing: 8, - direction: Axis.vertical, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Badge( - backgroundColor: Colors.red, - label: const Text('99'), - textStyle: const TextStyle(fontSize: 8, color: Colors.red), - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), - largeSize: 14, - offset: offset, - alignment: alignment, - child: const Icon(Icons.message, size: 36, color: Colors.indigo), - ), - Text( - info, - style: const TextStyle(fontSize: 10, color: Colors.grey), - ) - ], - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/Banner/node1_base.dart b/packages/widgets/lib/StatelessWidget/Banner/node1_base.dart deleted file mode 100644 index 22a1938d0..000000000 --- a/packages/widgets/lib/StatelessWidget/Banner/node1_base.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 5, -// "priority": 1, -// "name": "用于显示一个角标", -// "subtitle": "【message】 : 显示的文字信息 【String】\n" -// "【location】 : 位置*4 【BannerLocation】\n" -// "【color】: 角标颜色 【Color】\n" -// "【child】: 孩子 【Widget】\n" -// "【textStyle】: 文字样式 【TextStyle】", -// } - -class CustomBanner extends StatelessWidget { - CustomBanner({Key? key}) : super(key: key); - - final Map data = { - BannerLocation.topStart: Colors.red, - BannerLocation.topEnd: Colors.blue, - BannerLocation.bottomStart: Colors.green, - BannerLocation.bottomEnd: Colors.yellow, - }; - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 10, - runSpacing: 10, - children: data.keys - .map((BannerLocation location) => Container( - color: const Color(0xffD8F5FF), - width: 150, - height: 150 * 0.618, - child: Banner( - message: "Flutter 2.2.3发布", - location: location, - color: data[location]!, - child: const Padding( - padding: EdgeInsets.all(20), - child: FlutterLogo( - textColor: Colors.blue, - style: FlutterLogoStyle.horizontal, - )), - ), - )).toList()); - } -} diff --git a/packages/widgets/lib/StatelessWidget/BottomSheet/node1_base.dart b/packages/widgets/lib/StatelessWidget/BottomSheet/node1_base.dart deleted file mode 100644 index 0020befe7..000000000 --- a/packages/widgets/lib/StatelessWidget/BottomSheet/node1_base.dart +++ /dev/null @@ -1,64 +0,0 @@ -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 142, -// "name": 'BottomSheet基本使用', -// "priority": 1, -// "subtitle": -// "【builder】 : 组件构造器 【WidgetBuilder】\n" -// "【backgroundColor】 : 背景色 【Color】\n" -// "【elevation】 : 影深 【double】\n" -// "【shape】 : 形状 【ShapeBorder】\n" -// "【onClosing】 : 关闭回调 【Function()】", -// } - -import 'package:flutter/material.dart'; - -class CustomBottomSheet extends StatefulWidget { - const CustomBottomSheet({Key? key}) : super(key: key); - - @override - _CustomBottomSheetState createState() => _CustomBottomSheetState(); -} - -class _CustomBottomSheetState extends State { - bool opened = false; - - @override - Widget build(BuildContext context) { - return ElevatedButton( - onPressed: () { - opened = !opened; - opened - ? Scaffold.of(context).showBottomSheet((_) => _buildBottomSheet()) - : Navigator.of(context).pop(); - }, - child: const Text( - '点我显隐BottomSheet', - style: TextStyle(color: Colors.white), - )); - } - - Widget _buildBottomSheet() => BottomSheet( - enableDrag: true, - elevation: 4, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topRight: Radius.circular(60), - topLeft: Radius.circular(60), - )), - backgroundColor: Colors.transparent, - onClosing: () => print('onClosing'), - builder: (_) => (Container( - height: 250, - decoration: const BoxDecoration( - image: DecorationImage( - image: AssetImage('assets/images/sabar_bar.webp'), - fit: BoxFit.cover), - borderRadius: BorderRadius.only( - topRight: Radius.circular(60), - topLeft: Radius.circular(60), - )), - ))); -} diff --git a/packages/widgets/lib/StatelessWidget/BoxScrollView/node1_base.dart b/packages/widgets/lib/StatelessWidget/BoxScrollView/node1_base.dart deleted file mode 100644 index afa8a1f3f..000000000 --- a/packages/widgets/lib/StatelessWidget/BoxScrollView/node1_base.dart +++ /dev/null @@ -1,88 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 350 BoxScrollView 盒滑动视图 -/// BoxScrollView 类是一个继承自 ScrollView 的抽象类,所以无法直接使用,它的子类有 ListView、GridView。一般不自己实现子类使用它。 -/// link 183,162,163 -/// -// { -// "widgetId": 350, -// "name": 'BoxScrollView 介绍', -// "priority": 1, -// "subtitle": -// "【reverse】 : 是否反向 【bool】\n" -// "【scrollDirection】 : 滑动方向 【Axis】\n" -// "【cacheExtent】 : 缓存长 【double】\n" -// "【dragStartBehavior】 : 拖动行为 【DragStartBehavior】\n" -// "【clipBehavior】 : 裁剪行为 【ClipBehavior】\n" -// "【controller】 : 控制器 【ScrollController】", -// } - -class BoxScrollViewDemo extends StatelessWidget { - const BoxScrollViewDemo({Key? key}) : super(key: key); - - final String info = - 'BoxScrollView 是 ScrollView 的子类,实现了它的抽象方法,且暴露出另一个抽象方法 buildChildLayout,返回 Sliver 家族 Widget,' - '其子类有 ListView 和 GridView,分别使用 Sliver 家族相关List、Gird列表组件实现的。'; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 300, - child: Column( - children: [ - Container( - color: Colors.blue.withOpacity(0.1), - padding: const EdgeInsets.all(10), - margin: const EdgeInsets.all(10), - child: Text(info), - ), - Expanded(child: MyBoxScrollView()), - ], - ), - ); - } -} - -class MyBoxScrollView extends BoxScrollView { - MyBoxScrollView({Key? key}) : super(key: key); - - final List data = [ - Colors.purple[50]!, - Colors.purple[100]!, - Colors.purple[200]!, - Colors.purple[300]!, - Colors.purple[400]!, - Colors.purple[500]!, - Colors.purple[600]!, - Colors.purple[700]!, - Colors.purple[800]!, - Colors.purple[900]!, - ]; - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; - - @override - Widget buildChildLayout(BuildContext context)=> SliverFixedExtentList( - itemExtent: 60, - delegate: SliverChildBuilderDelegate( - (_, int index) => Container( - alignment: Alignment.center, - width: 100, - height: 50, - color: data[index], - child: Text( - colorString(data[index]), - style: const TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2) - ]), - ), - ), - childCount: data.length), - ); -} diff --git a/packages/widgets/lib/StatelessWidget/Builder/node1_base.dart b/packages/widgets/lib/StatelessWidget/Builder/node1_base.dart deleted file mode 100644 index 452b06979..000000000 --- a/packages/widgets/lib/StatelessWidget/Builder/node1_base.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/5/3 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 202, -// "name": 'Builder的使用', -// "priority": 1, -// "subtitle": "【builder】 : 组件构造器 【WidgetBuilder】\n" -// "同一个类中使用`XXX.of(context)`获取某类状态对象方法会存在`上下文滞后`的错误,使用Builder解决。", -// } - -class BuilderDemo extends StatelessWidget { - const BuilderDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 200, - child: Scaffold( - appBar: AppBar( - title: const Text('Builder'), - ), - floatingActionButton: Builder( - builder: (ctx) => FloatingActionButton( - onPressed: () { - ScaffoldMessenger.of(ctx).showSnackBar(const SnackBar(content: Text('hello builder'))); - }, - child: const Icon(Icons.add), - ), - ), - ), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/ButtonBar/node1_base.dart b/packages/widgets/lib/StatelessWidget/ButtonBar/node1_base.dart deleted file mode 100644 index 70eb7e03c..000000000 --- a/packages/widgets/lib/StatelessWidget/ButtonBar/node1_base.dart +++ /dev/null @@ -1,38 +0,0 @@ -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 29, -// "priority": 1, -// "name": "ButtonBar对齐方式", -// "subtitle": "【alignment】: 对齐方式 【MainAxisAlignment】\n" -// "【children】: 子组件集 【List】", -// } - -import 'package:flutter/material.dart'; -import 'package:widgets/utils/dialog_about.dart'; - - -class CustomButtonBar extends StatelessWidget { - const CustomButtonBar({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return ButtonBar( - alignment: MainAxisAlignment.center, - children: [ - ElevatedButton( - child: const Text("ElevatedButton"), - onPressed: () => DialogAbout.show(context)), - OutlinedButton( - child: const Text("Outlined"), - onPressed: () => DialogAbout.show(context)), - TextButton( - onPressed: () => DialogAbout.show(context), - child: const Text("TextButton"), - ) - ], - ); - } -} - diff --git a/packages/widgets/lib/StatelessWidget/ButtonBar/node2_padding.dart b/packages/widgets/lib/StatelessWidget/ButtonBar/node2_padding.dart deleted file mode 100644 index 113943da4..000000000 --- a/packages/widgets/lib/StatelessWidget/ButtonBar/node2_padding.dart +++ /dev/null @@ -1,39 +0,0 @@ - -import 'package:flutter/material.dart'; -import 'package:widgets/utils/dialog_about.dart'; - - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 29, -// "priority": 2, -// "name": "ButtonBar边距和高", -// "subtitle": "【buttonPadding】: 内边距 【EdgeInsetsGeometry】\n" -// "【buttonHeight】: 高 【double】", -// } -class PaddingButtonBar extends StatelessWidget { - const PaddingButtonBar({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return ButtonBar( - alignment: MainAxisAlignment.center, - buttonHeight: 40, - buttonPadding: const EdgeInsets.only(left: 15,right: 15), - children: [ - ElevatedButton( - child: const Text("ElevatedButton"), - onPressed: () => DialogAbout.show(context)), - OutlinedButton( - child: const Text("Outlined"), - onPressed: () => DialogAbout.show(context)), - TextButton( - onPressed: () => DialogAbout.show(context), - child: const Text("TextButton"), - ) - ], - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/Card/node1_base.dart b/packages/widgets/lib/StatelessWidget/Card/node1_base.dart deleted file mode 100644 index 36aa99b25..000000000 --- a/packages/widgets/lib/StatelessWidget/Card/node1_base.dart +++ /dev/null @@ -1,36 +0,0 @@ - - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/25 -/// contact me by email 1981462002@qq.com -/// 说明: -/// -// { -// "widgetId": 3, -// "priority": 1, -// "name": "Card可以让一个组件卡片化", -// "subtitle": "【elevation】 : 影深 【double】\n" -// "【margin】: 外边距 【double】\n" -// "【color】: 颜色 【Color】\n" -// "【child】: 孩子 【Widget】", -// } -class CustomCard extends StatelessWidget { - const CustomCard({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Card( - color: const Color(0xffB3FE65), - elevation: 4, - margin: const EdgeInsets.all(10), - child: Container( - alignment: Alignment.topLeft, - width: 200, - height: 0.618*200, - margin: const EdgeInsets.all(10), - child: const Text("Card", style: TextStyle(fontSize: 20)), - ), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/Card/node2_shape.dart b/packages/widgets/lib/StatelessWidget/Card/node2_shape.dart deleted file mode 100644 index 25bbca878..000000000 --- a/packages/widgets/lib/StatelessWidget/Card/node2_shape.dart +++ /dev/null @@ -1,59 +0,0 @@ - - -/// create by 张风捷特烈 on 2020/4/25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 3, -// "priority": 2, -// "name": "可以通过shape属性实现裁切效果", -// "subtitle": "【shape】 : 形状 【ShapeBorder】\n" -// "【margin】: 外边距 【double】\n" -// "【color】: 颜色 【Color】\n" -// "【child】: 孩子 【Widget】", -// } - -import 'package:flutter/material.dart'; -import 'package:widgets/utils/pather.dart'; - -class ShapeCard extends StatelessWidget { - const ShapeCard({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Card( - color: const Color(0xffB3FE65), - elevation: 6, - shape: StarShapeBorder(), - child: Container( - alignment: Alignment.center, - width: 100, - height: 100, - child: const Text("Card", style: TextStyle(fontSize: 20)), - ), - ); - } -} - -class StarShapeBorder extends ShapeBorder { - @override - EdgeInsetsGeometry get dimensions => EdgeInsets.zero; - - @override - Path getInnerPath(Rect rect, {TextDirection? textDirection}) { - return Path(); - } - - @override - Path getOuterPath(Rect rect, {TextDirection? textDirection}) => - Pather.create.nStarPath(9, 50, 40, dx: 50, dy: 50); - - @override - void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) { - } - - @override - ShapeBorder scale(double t) { - return this; - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/CheckboxListTile/node1_base.dart b/packages/widgets/lib/StatelessWidget/CheckboxListTile/node1_base.dart deleted file mode 100644 index 8ffe39fe5..000000000 --- a/packages/widgets/lib/StatelessWidget/CheckboxListTile/node1_base.dart +++ /dev/null @@ -1,45 +0,0 @@ - -/// create by 张风捷特烈 on 2020/4/25 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 17, -// "priority": 1, -// "name": "CheckBoxListTile的基本表现如下", -// "subtitle": "【secondary】: 左侧组件 【Widget】\n" -// "【checkColor】: ✔️颜色 【Color】\n" -// "【activeColor】: 选中时外框颜色 【Color】\n" -// "【title】: 中间上组件 【Widget】\n" -// "【subtitle】: 中间下组件 【Widget】\n" -// "【onChanged】: 选中事件 【Function(bool)】", -// } -import 'package:flutter/material.dart'; - -class CustomCheckBoxListTile extends StatefulWidget { - const CustomCheckBoxListTile({Key? key}) : super(key: key); - - @override - _CustomCheckBoxListTileState createState() => _CustomCheckBoxListTileState(); -} - -class _CustomCheckBoxListTileState extends State { - bool _selected = false; - - @override - Widget build(BuildContext context) { - return Container( - margin: const EdgeInsets.all(10), - color: Colors.grey.withAlpha(22), - child: CheckboxListTile( - value: _selected, - checkColor: Colors.yellow, - activeColor: Colors.orangeAccent, - secondary: Image.asset("assets/images/icon_head.webp"), - title: const Text("张风捷特烈"), - subtitle: const Text("@万花过尽知无物"), - onChanged: (v) => setState(() => _selected = !_selected), - ), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/CheckboxListTile/node2_select.dart b/packages/widgets/lib/StatelessWidget/CheckboxListTile/node2_select.dart deleted file mode 100644 index 9b626012b..000000000 --- a/packages/widgets/lib/StatelessWidget/CheckboxListTile/node2_select.dart +++ /dev/null @@ -1,41 +0,0 @@ - -/// create by 张风捷特烈 on 2020/4/25 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 17, -// "priority": 2, -// "name": "CheckBoxListTile的选中效果", -// "subtitle": "【selected】: 是否选中 【bool】", -// } -import 'package:flutter/material.dart'; - -class SelectCheckBoxListTile extends StatefulWidget { - const SelectCheckBoxListTile({Key? key}) : super(key: key); - - @override - _SelectCheckBoxListTileState createState() => _SelectCheckBoxListTileState(); -} - -class _SelectCheckBoxListTileState extends State { - bool _selected = false; - - @override - Widget build(BuildContext context) { - return Container( - margin: const EdgeInsets.all(10), - color: Colors.grey.withAlpha(22), - child: CheckboxListTile( - value: _selected, - selected: _selected, - checkColor: Colors.yellow, - activeColor: Colors.orangeAccent, - secondary: Image.asset("assets/images/icon_head.webp"), - title: const Text("张风捷特烈"), - subtitle: const Text("@万花过尽知无物"), - onChanged: (v) => setState(() => _selected = !_selected), - ), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/CheckboxListTile/node3_dense.dart b/packages/widgets/lib/StatelessWidget/CheckboxListTile/node3_dense.dart deleted file mode 100644 index 5f990ffe5..000000000 --- a/packages/widgets/lib/StatelessWidget/CheckboxListTile/node3_dense.dart +++ /dev/null @@ -1,41 +0,0 @@ -/// create by 张风捷特烈 on 2020/4/25 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 17, -// "priority": 3, -// "name": "CheckBoxListTile的密排属性", -// "subtitle": "【dense】: 是否密排 【bool】", -// } - -import 'package:flutter/material.dart'; - -class DenseCheckBoxListTile extends StatefulWidget { - const DenseCheckBoxListTile({Key? key}) : super(key: key); - - @override - _DenseCheckBoxListTileState createState() => _DenseCheckBoxListTileState(); -} - -class _DenseCheckBoxListTileState extends State { - bool _selected = false; - - @override - Widget build(BuildContext context) { - return Container( - margin: const EdgeInsets.all(10), - color: Colors.grey.withAlpha(22), - child: CheckboxListTile( - value: _selected, - dense: true, - checkColor: Colors.yellow, - activeColor: Colors.orangeAccent, - secondary: Image.asset("assets/images/icon_head.webp"), - title: const Text("张风捷特烈"), - subtitle: const Text("@万花过尽知无物"), - onChanged: (v) => setState(() => _selected = !_selected), - ), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/CheckedModeBanner/node1_base.dart b/packages/widgets/lib/StatelessWidget/CheckedModeBanner/node1_base.dart deleted file mode 100644 index f2d853a7f..000000000 --- a/packages/widgets/lib/StatelessWidget/CheckedModeBanner/node1_base.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 215 CheckedModeBanner 仅在debug运行模式中显示右上角角标,没什么太大卵用。在 MaterialApp 组件源码中有使用场景。 -// { -// "widgetId": 215, -// "name": 'CheckedModeBanner基本使用', -// "priority": 1, -// "subtitle": "【child】 : 组件 【Widget】", -// } - -class CheckedModeBannerDemo extends StatelessWidget { - const CheckedModeBannerDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return CheckedModeBanner( - child: Container( - alignment: Alignment.center, - width: 250, - height: 150, - color: Theme.of(context).primaryColor, - child: const Text( - "CheckedModeBanner", - style: TextStyle(color: Colors.white, fontSize: 20), - ), - ), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/Chip/node1_base.dart b/packages/widgets/lib/StatelessWidget/Chip/node1_base.dart deleted file mode 100644 index a0d183405..000000000 --- a/packages/widgets/lib/StatelessWidget/Chip/node1_base.dart +++ /dev/null @@ -1,43 +0,0 @@ - - -/// create by 张风捷特烈 on 2020/4/25 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 11, -// "priority": 1, -// "name": "Chip的普通表现如下", -// "subtitle": "【avatar】: 左侧组件 【Widget】\n" -// "【label】: 中间组件 【Widget】\n" -// "【padding】 : 内边距 【EdgeInsetsGeometry】\n" -// "【labelPadding】: label边距 【EdgeInsetsGeometry】", -// } - -import 'package:flutter/material.dart'; -class CustomChip extends StatelessWidget { - const CustomChip({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 20, - children: [ - Chip( - avatar: Image.asset("assets/images/icon_head.webp"), - label: const Text("张风捷特烈"), - padding: const EdgeInsets.all(5), - labelPadding: const EdgeInsets.all(5), - ), - const Chip( - avatar: CircleAvatar( - backgroundImage: - AssetImage("assets/images/wy_200x300.webp")), - label: Text("百里巫缨"), - padding: EdgeInsets.all(8), - labelPadding: EdgeInsets.all(6), - ), - ], - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/Chip/node2_color.dart b/packages/widgets/lib/StatelessWidget/Chip/node2_color.dart deleted file mode 100644 index 3b49e4efa..000000000 --- a/packages/widgets/lib/StatelessWidget/Chip/node2_color.dart +++ /dev/null @@ -1,45 +0,0 @@ - - -/// create by 张风捷特烈 on 2020/4/25 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 11, -// "priority": 2, -// "name": "可以设置颜色和阴影", -// "subtitle": "【backgroundColor】: 背景色 【Color】\n" -// "【shadowColor】: 阴影色 【Color】\n" -// "【elevation】: 影深 【double】", -// } -import 'package:flutter/material.dart'; -class ColorOfChip extends StatelessWidget { - const ColorOfChip({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 20, - children: [ - Chip( - avatar: Image.asset("assets/images/icon_head.webp"), - label: const Text("张风捷特烈"), - padding: const EdgeInsets.all(5), - labelPadding: const EdgeInsets.all(5), - backgroundColor: Colors.grey.withAlpha(66), - shadowColor: Colors.orangeAccent, - elevation: 3, - ), - Chip( - avatar: Image.asset("assets/images/icon_head.webp"), - label: const Text("张风捷特烈"), - padding: const EdgeInsets.all(5), - labelPadding: const EdgeInsets.all(5), - backgroundColor: Colors.cyanAccent.withAlpha(11), - shadowColor: Colors.blue.withAlpha(88), - elevation: 4, - ), - ], - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/Chip/node3_delete.dart b/packages/widgets/lib/StatelessWidget/Chip/node3_delete.dart deleted file mode 100644 index 3ebe27b8b..000000000 --- a/packages/widgets/lib/StatelessWidget/Chip/node3_delete.dart +++ /dev/null @@ -1,36 +0,0 @@ - - -/// create by 张风捷特烈 on 2020/4/25 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 11, -// "priority": 3, -// "name": "可以设置右侧点击按钮", -// "subtitle": "【deleteIcon】: 右侧组件(通常为Icon) 【Widget】\n" -// "【deleteIconColor】: 右侧组件颜色 【Color】\n" -// "【onDeleted】: 右侧组件点击事件 【Function】", -// } -import 'package:flutter/material.dart'; -import 'package:widgets/utils/dialog_about.dart'; - -class DeleteOfChip extends StatelessWidget { - const DeleteOfChip({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Chip( - avatar: Image.asset("assets/images/icon_head.webp"), - label: const Text("张风捷特烈"), - padding: const EdgeInsets.all(5), - labelPadding: const EdgeInsets.all(3), - backgroundColor: Colors.grey.withAlpha(66), - shadowColor: Colors.orangeAccent, -// deleteIcon: Icon(Icons.close,size: 18), - deleteIconColor: Colors.red, - onDeleted: () => DialogAbout.show(context), - elevation: 3, - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/ChoiceChip/node1_base.dart b/packages/widgets/lib/StatelessWidget/ChoiceChip/node1_base.dart deleted file mode 100644 index 6d1364478..000000000 --- a/packages/widgets/lib/StatelessWidget/ChoiceChip/node1_base.dart +++ /dev/null @@ -1,48 +0,0 @@ - -/// create by 张风捷特烈 on 2020/4/25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 12, -// "priority": 1, -// "name": "ChoiceChip的普通表现如下", -// "subtitle": "【selectedColor】: 选中时颜色 【Color】\n" -// "【selectedShadowColor】: 选中时阴影颜色 【Color】\n" -// "【onSelected】: 选中事件 【Fuction(bool)】\n" -// " 其他属性同Chip组件,无右侧组件。", -// } - -import 'package:flutter/material.dart'; - -class CustomChoiceChip extends StatefulWidget { - const CustomChoiceChip({Key? key}) : super(key: key); - - @override - _CustomChoiceChipState createState() => _CustomChoiceChipState(); -} - -class _CustomChoiceChipState extends State { - bool _select = false; - - @override - Widget build(BuildContext context) { - return ChoiceChip( - selected: _select, - padding: const EdgeInsets.all(5), - labelPadding: const EdgeInsets.all(5), - label: Text( - _select ? - "You are selected it." : - "This is a ChoiceChip.", - style: const TextStyle(fontSize: 16), - ), - backgroundColor: Colors.grey.withAlpha(66), - avatar: Image.asset("assets/images/icon_head.webp"), - selectedColor: Colors.orangeAccent.withAlpha(44), - selectedShadowColor: Colors.blue, - shadowColor: Colors.orangeAccent, - elevation: 3, - onSelected: (value) => setState(() => _select = value), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/CircleAvatar/node1_base.dart b/packages/widgets/lib/StatelessWidget/CircleAvatar/node1_base.dart deleted file mode 100644 index ff2fd147b..000000000 --- a/packages/widgets/lib/StatelessWidget/CircleAvatar/node1_base.dart +++ /dev/null @@ -1,34 +0,0 @@ -/// create by 张风捷特烈 on 2020/4/25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 9, -// "priority": 1, -// "name": "CircleAvatar的表现", -// "subtitle": "【radius】 : 半径 【double】\n" -// "【backgroundImage】 : 图片资源 【ImageProvider】\n" -// "【foregroundColor】: 前景色 【Color】\n" -// "【backgroundColor】: 背景色 【Color】\n" -// "【minRadius】: 最小半径 【double】\n" -// "【maxRadius】: 最大半径 【double】\n" -// "【child】: 孩子组件 【Child】", -// } - -import 'package:flutter/material.dart'; - -class CustomCircleAvatar extends StatelessWidget { - const CustomCircleAvatar({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return const CircleAvatar( - radius: 50, - backgroundImage: AssetImage("assets/images/wy_200x300.webp"), - foregroundColor: Colors.white, - child: Icon( - Icons.check, - size: 50, - ), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/CloseButton/node1_base.dart b/packages/widgets/lib/StatelessWidget/CloseButton/node1_base.dart deleted file mode 100644 index c15715bc8..000000000 --- a/packages/widgets/lib/StatelessWidget/CloseButton/node1_base.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 32, -// "priority": 1, -// "name": "CloseButton点击事件", -// "subtitle": "【onPressed】 : 点击事件 【VoidCallback?】\n" -// "【style】: 按钮样式 【ButtonStyle?】\n" -// "【color】: 颜色 【Color】\n" -// "onPressed 为空时,点击时会退出当前界面。", -// } - -class CustomCloseButton extends StatelessWidget { - const CustomCloseButton({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return const CloseButton(); - } -} diff --git a/packages/widgets/lib/StatelessWidget/CloseButtonIcon/node1_base.dart b/packages/widgets/lib/StatelessWidget/CloseButtonIcon/node1_base.dart deleted file mode 100644 index 50c0380f3..000000000 --- a/packages/widgets/lib/StatelessWidget/CloseButtonIcon/node1_base.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2023/11/28 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 274, -// "priority": 1, -// "name": "CloseButtonIcon 效果", -// "subtitle": "通过 _ActionIcon 组件根据 ActionIconTheme 主题,来适配不同平台的关闭按钮图标。", -// } -class CloseButtonIconDemo extends StatelessWidget { - const CloseButtonIconDemo({super.key}); - - @override - Widget build(BuildContext context) { - return const CloseButtonIcon(); - } -} diff --git a/packages/widgets/lib/StatelessWidget/Container/node1_base.dart b/packages/widgets/lib/StatelessWidget/Container/node1_base.dart deleted file mode 100644 index eee3ff0f5..000000000 --- a/packages/widgets/lib/StatelessWidget/Container/node1_base.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 1, -// "name": '可用于显示一个指定宽高的区域', -// "priority": 1, -// "subtitle": "【width】 : 宽 【int】\n" -// "【高】: 外边距 【int】\n" -// "【color】: 子组件 【Color】", -// } -class CustomContainer extends StatelessWidget { - const CustomContainer({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - alignment: Alignment.topLeft, - width: 200, - height: 200 * 0.618, - color: Colors.red.withAlpha(88), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/Container/node2_child.dart b/packages/widgets/lib/StatelessWidget/Container/node2_child.dart deleted file mode 100644 index 1c77f6892..000000000 --- a/packages/widgets/lib/StatelessWidget/Container/node2_child.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 1, -// "name": '可以在区域中放入一个子组件', -// "priority": 2, -// "subtitle": "【padding】 : 内边距 【EdgeInsetsGeometry】\n" -// "【margin】: 外边距 【EdgeInsetsGeometry】\n" -// "【child】: 子组件 【Widget】", -// } -class ContainerWithChild extends StatelessWidget { - const ContainerWithChild({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - alignment: Alignment.topLeft, - padding: const EdgeInsets.all(20), - margin: const EdgeInsets.all(10), - width: 200, - height: 200 * 0.618, - color: Colors.grey.withAlpha(88), - child: const Icon(Icons.android), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/Container/node3_alignment.dart b/packages/widgets/lib/StatelessWidget/Container/node3_alignment.dart deleted file mode 100644 index af324e076..000000000 --- a/packages/widgets/lib/StatelessWidget/Container/node3_alignment.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 1, -// "name": '可对子组件进行对齐定位', -// "priority": 3, -// "subtitle": "【alignment】 : 对齐定位 【AlignmentGeometry】", -// } -class ContainerAlignment extends StatelessWidget { - const ContainerAlignment({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - alignment: Alignment.bottomRight, - width: 200, - height: 200 * 0.618, - color: Colors.grey.withAlpha(88), - child: const Icon( - Icons.android, - color: Colors.green, - ), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/Container/node4_decoration.dart b/packages/widgets/lib/StatelessWidget/Container/node4_decoration.dart deleted file mode 100644 index 96e94caa7..000000000 --- a/packages/widgets/lib/StatelessWidget/Container/node4_decoration.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 1, -// "name": '可对子组件进行装饰', -// "priority": 4, -// "subtitle": -// "【decoration】 : 装饰 【Decoration】\n " -// "可装饰: 边线、圆弧、颜色、渐变色、阴影、图片等内容", -// } -class ContainerDecoration extends StatelessWidget { - ContainerDecoration({Key? key}) : super(key: key); - - final List rainbow = [ - 0xffff0000, - 0xffFF7F00, - 0xffFFFF00, - 0xff00FF00, - 0xff00FFFF, - 0xff0000FF, - 0xff8B00FF - ]; - - final List stops = [0.0, 1 / 6, 2 / 6, 3 / 6, 4 / 6, 5 / 6, 1.0]; - - @override - Widget build(BuildContext context) { - - return Container( - alignment: Alignment.center, - width: 200, - height: 200 * 0.618, - margin: const EdgeInsets.all(20), - padding: const EdgeInsets.all(20), - decoration: BoxDecoration(//添加渐变色 - gradient: LinearGradient( - stops: stops, - colors: rainbow.map((e) => Color(e)).toList()), - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(50), - bottomRight: Radius.circular(50)), - boxShadow: const [ - BoxShadow( - color: Colors.grey, - offset: Offset(1, 1), - blurRadius: 10, - spreadRadius: 1), - ]), - child: const Text( - "Container", - style: TextStyle(fontSize: 20), - ), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/Container/node5_transform.dart b/packages/widgets/lib/StatelessWidget/Container/node5_transform.dart deleted file mode 100644 index a37e21893..000000000 --- a/packages/widgets/lib/StatelessWidget/Container/node5_transform.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 1, -// "name": 'Container还具有变换性', -// "priority": 5, -// "subtitle": "【transform】 : 变换矩阵 【Matrix4】\n " -// "基于Matrix4的矩阵变换,变换详情见线性代数", -// } -class ContainerTransform extends StatelessWidget { - const ContainerTransform({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - alignment: Alignment.center, - color: Colors.cyanAccent, - width: 150, - height: 150 * 0.618, - transform: Matrix4.skew(-pi / 10, 0), - child: const Text( - "Container", - style: TextStyle(fontSize: 20), - ), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/Container/node6_constraints.dart b/packages/widgets/lib/StatelessWidget/Container/node6_constraints.dart deleted file mode 100644 index 3371138f6..000000000 --- a/packages/widgets/lib/StatelessWidget/Container/node6_constraints.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 1, -// "name": 'Container的约束性', -// "priority": 6, -// "subtitle": -// "【constraints】 : 约束 【BoxConstraints】\n " -// "会约束该区域的尺寸,不会小于指定的最小宽高,也不会大于指定的最大宽高。", -// } -class ContainerConstraints extends StatelessWidget { - const ContainerConstraints({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - color: Colors.blue, - width: 200, - height: 200 * 0.618, - constraints: const BoxConstraints( - minWidth: 100, - maxWidth: 150, - minHeight: 20, - maxHeight: 100, - ), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/CupertinoActionSheet/node1_base.dart b/packages/widgets/lib/StatelessWidget/CupertinoActionSheet/node1_base.dart deleted file mode 100644 index 89f278349..000000000 --- a/packages/widgets/lib/StatelessWidget/CupertinoActionSheet/node1_base.dart +++ /dev/null @@ -1,67 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 131, -// "name": 'CupertinoActionSheet基本使用', -// "priority": 1, -// "subtitle": "【title】 : 第一行组件 【Widget】\n" -// "【message】 : 第二行组件 【Widget】\n" -// "【cancelButton】 : 取消按钮处组件 【Widget】\n" -// "【actions】 : 中间组件列表 【List】", -// } -class CustomCupertinoActionSheet extends StatelessWidget { - const CustomCupertinoActionSheet({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - _buildRaisedButton(context), - _buildCupertinoActionSheet(context), - ], - ); - } - - Widget _buildCupertinoActionSheet(BuildContext context) => - Container( - alignment: Alignment.bottomCenter, - child: CupertinoActionSheet( - title: const Text("Please chose a language"), - message: const Text('the language you use in this application.'), - cancelButton: CupertinoActionSheetAction( - onPressed: () => Navigator.pop(context), child: const Text("Cancel")), - actions: [ - CupertinoActionSheetAction( - onPressed: () => Navigator.pop(context), - child: const Text('Dart')), - CupertinoActionSheetAction( - onPressed: () => Navigator.pop(context), - child: const Text('Java')), - CupertinoActionSheetAction( - onPressed: () => Navigator.pop(context), - child: const Text('Kotlin')), - ], - ), - ); - - Widget _buildRaisedButton(BuildContext context) => ElevatedButton( - style: TextButton.styleFrom( - backgroundColor: Colors.blue, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10)), - ), - ), - onPressed: () => showDialog( - context: context, - builder: (ctx) => _buildCupertinoActionSheet(context)), - child: const Text( - 'Just Show It !', - style: TextStyle(color: Colors.white), - ), - ); -} diff --git a/packages/widgets/lib/StatelessWidget/CupertinoActionSheetAction/node1_base.dart b/packages/widgets/lib/StatelessWidget/CupertinoActionSheetAction/node1_base.dart deleted file mode 100644 index 99003ba9e..000000000 --- a/packages/widgets/lib/StatelessWidget/CupertinoActionSheetAction/node1_base.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:widgets/utils/dialog_about.dart'; - - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 132, -// "name": 'CupertinoActionSheetAction基本使用', -// "priority": 1, -// "subtitle": "【child】 : 子组件 【Widget】\n" -// "【isDefaultAction】 : 是否默认选中 【bool】\n" -// "【onPressed】 : 点击事件 【Function()】", -// } -class CustomCupertinoActionSheetAction extends StatelessWidget { - const CustomCupertinoActionSheetAction({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Container( - margin: const EdgeInsets.all(5), - color: Colors.grey.withAlpha(33), - child: CupertinoActionSheetAction( - isDefaultAction: true, - onPressed: () => DialogAbout.show(context), - child: const Text('张风捷特烈')), - ), - Container( - color: Colors.grey.withAlpha(33), - margin: const EdgeInsets.all(5), - child: CupertinoActionSheetAction( - isDefaultAction: false, - onPressed: () => DialogAbout.show(context), - child: const Text('百里·巫缨')), - ), - ], - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/CupertinoAlertDialog/node1_base.dart b/packages/widgets/lib/StatelessWidget/CupertinoAlertDialog/node1_base.dart deleted file mode 100644 index fbcc4be49..000000000 --- a/packages/widgets/lib/StatelessWidget/CupertinoAlertDialog/node1_base.dart +++ /dev/null @@ -1,102 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 129, -// "name": 'CupertinoActionSheetAction基本使用', -// "priority": 1, -// "subtitle": "【child】 : 子组件 【Widget】\n" -// "【isDefaultAction】 : 是否默认选中 【bool】\n" -// "【onPressed】 : 点击事件 【Function()】", -// } - -class CustomCupertinoAlertDialog extends StatelessWidget { - const CustomCupertinoAlertDialog({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Column( - children: [ - _buildRaisedButton(context), - _buildCupertinoAlertDialog(context), - ], - ); - } - - Widget _buildRaisedButton(BuildContext context) => - ElevatedButton( - style: TextButton.styleFrom( - backgroundColor: Colors.blue, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10)), - ), - ), - onPressed: () { - showDialog( - context: context, - builder: (ctx) => _buildCupertinoAlertDialog(context)); - }, - child: const Text( - 'Just Show It !', - style: TextStyle(color: Colors.white), - ), - ); - - Widget _buildCupertinoAlertDialog(BuildContext context) { - return Material( - color: Colors.transparent, - child: CupertinoAlertDialog( - title: _buildTitle(context), - content: _buildContent(), - actions: [ - CupertinoButton( - child: const Text("Yes, Delete"), - onPressed: () => Navigator.pop(context), - ), - CupertinoButton( - child: const Text("Cancle"), - onPressed: () => Navigator.pop(context), - ), - ]), - ); - } - - Widget _buildTitle(context) { - return Row( - //标题 - children: [ - const Icon( - CupertinoIcons.delete_solid, - color: Colors.red, - ), - const Expanded( - child: Text( - 'Delete File', - style: TextStyle(color: Colors.red, fontSize: 20), - )), - InkWell( - child: const Icon(CupertinoIcons.clear_thick), - onTap: () => Navigator.pop(context), - ) - ]); - } - - Widget _buildContent() { - return Padding( - padding: const EdgeInsets.only(top: 18.0), - child: Column( - children: const[ - Text( - ' Hi toly! If you push the conform buttom ,' - ' You will lose this file. Are you sure wand to do that?', - style: TextStyle(color: Color(0xff999999), fontSize: 16), - textAlign: TextAlign.justify, - ), - ], - ), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/CupertinoDialogAction/no_node1_base.dart b/packages/widgets/lib/StatelessWidget/CupertinoDialogAction/no_node1_base.dart deleted file mode 100644 index 8e0f866e3..000000000 --- a/packages/widgets/lib/StatelessWidget/CupertinoDialogAction/no_node1_base.dart +++ /dev/null @@ -1,130 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: - -class CustomDialog extends StatelessWidget { - const CustomDialog({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Column( - children: [ - _buildRaisedButton(context), - _buildDialog(), - ], - ); - } - - Widget _buildDialog() => CupertinoDialogAction( - onPressed: (){ - - }, - child: const SizedBox( - width: 50, - child: DeleteDialog(), - ), - ); - - Widget _buildRaisedButton(BuildContext context) => ElevatedButton( - style: TextButton.styleFrom( - backgroundColor: Colors.blue, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10)), - ), - ), - onPressed: () { - showDialog(context: context, builder: (ctx) => _buildDialog()); - }, - child: const Text( - 'Just Show It !', - style: TextStyle(color: Colors.white), - ), - - ); -} - -class DeleteDialog extends StatelessWidget { - const DeleteDialog({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - _buildBar(context), - _buildTitle(), - _buildContent(), - _buildFooter(context), - ], - ); - } - - Widget _buildTitle() { - return const Text( - 'Delete Doucument', - style: TextStyle(color: Color(0xff5CC5E9), fontSize: 24), - ); - } - - Widget _buildContent() { - return const Padding( - padding: EdgeInsets.all(10.0), - child: Text( - ' Hi toly! If you push the conform buttom ,' - ' You will lose this file. Are you sure wand to do that?', - style: TextStyle(color: Color(0xffCFCFCF), fontSize: 16), - textAlign: TextAlign.justify, - ), - ); - } - - Widget _buildFooter(context) { - return Padding( - padding: const EdgeInsets.only(bottom: 15.0, top: 10), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Container( - alignment: Alignment.center, - height: 40, - width: 120, - decoration: const BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(30)), - color: Color(0xff73D1EE)), - child: const Text('Yes, Delete', - style: TextStyle(color: Colors.white, fontSize: 16)), - ), - InkWell( - onTap: ()=>Navigator.of(context).pop(), - child: Container( - alignment: Alignment.center, - height: 40, - width: 120, - decoration: const BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(30)), - color: Colors.orangeAccent), - child: const Text('Cancle', - style: TextStyle(color: Colors.white, fontSize: 16)), - ), - ) - ], - ), - ); - } - - _buildBar(context) => Container( - height: 30, - alignment: Alignment.centerRight, - margin: const EdgeInsets.only(right: 10, top: 5), - child: InkWell( - onTap: ()=>Navigator.of(context).pop(), - child: const Icon( - Icons.close, - color: Color(0xff82CAE3), - ), - ), - ); -} diff --git a/packages/widgets/lib/StatelessWidget/CupertinoDialogAction/node1_base.dart b/packages/widgets/lib/StatelessWidget/CupertinoDialogAction/node1_base.dart deleted file mode 100644 index 679eeeca8..000000000 --- a/packages/widgets/lib/StatelessWidget/CupertinoDialogAction/node1_base.dart +++ /dev/null @@ -1,47 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/7/22 -/// contact me by email 1981462002@qq.com -/// 说明: 352 CupertinoDialogAction 0 一个简单的按钮,通常用于CupertinoAlertDialog中,一般不单独使用。 -// { -// "widgetId": 352, -// "name": "CupertinoDialogAction基本使用", -// "priority": 1, -// "subtitle": "【isDefaultAction】 : 是否是默认性操作 【bool】\n" -// "【isDestructiveAction】 : 是否是毁灭性操作 【bool】\n" -// "【textStyle】: 文字样式 【TextStyle】\n" -// "【onPressed】: 点击事件 【VoidCallback】\n" -// "【child】: 子组件 【Widget】", -// } -class CupertinoDialogActionDemo extends StatelessWidget { - const CupertinoDialogActionDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Column( - children: [ - CupertinoDialogAction( - isDestructiveAction: false, - onPressed: () => _toast(context), - child: const Text('CupertinoDialogAction'), - ), - CupertinoDialogAction( - isDestructiveAction: true, - onPressed: () => _toast(context), - child: const Text('CupertinoDialogAction'), - ), - ], - ); - } - - void _toast(BuildContext context) { - SnackBar snackBar = SnackBar( - backgroundColor: Theme.of(context).primaryColor, - content: const Text('CupertinoDialogAction'), - ); - - ScaffoldMessenger.of(context).showSnackBar(snackBar); - - } -} diff --git a/packages/widgets/lib/StatelessWidget/CupertinoFullscreenDialogTransition/node1_base.dart b/packages/widgets/lib/StatelessWidget/CupertinoFullscreenDialogTransition/node1_base.dart deleted file mode 100644 index 8ff03dbac..000000000 --- a/packages/widgets/lib/StatelessWidget/CupertinoFullscreenDialogTransition/node1_base.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020/4/11 -/// contact me by email 1981462002@qq.com -/// -/// 说明: 219 CupertinoFullscreenDialogTransition 0 全页面过渡变换 创建一个 iOS 风格的转换,用于唤出全屏对话框。link 216 -// { -// "widgetId": 219, -// "name": '组件介绍', -// "priority": 1, -// "subtitle": "【child】 : 子组件 【Widget】\n" -// "【linearTransition】 : 是否线性转换 【bool】\n" -// "【primaryRouteAnimation】 : 初始路由动画 【Animation】\n" -// "【secondaryRouteAnimation】 : 第二路由动画 【Animation】", -// } - -class CupertinoFullscreenDialogTransitionDemo extends StatelessWidget { - const CupertinoFullscreenDialogTransitionDemo({Key? key}) : super(key: key); - - final String info = - '和 CupertinoPageTransition 一样,该组件底层基于 SlideTransition 组件实现,' - '主要用途是模仿 iOS 风格,用于唤出全屏对话框动画过渡效果。' - '源码中唯一的使用处是 CupertinoPageRoute 处理路由跳转动画时,一般不会单独使用。' - '当【route.fullscreenDialog】为 true 时,会使用 CupertinoFullscreenDialogTransition 组件,否则使用 CupertinoPageTransition 组件。' - '其中个属性信息和 CupertinoPageTransition 组件一致,详见之。'; - - @override - Widget build(BuildContext context) { - return Container( - color: Colors.blue.withOpacity(0.1), - padding: const EdgeInsets.all(10), - margin: const EdgeInsets.all(10), - child: Text(info), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/CupertinoNavigationBarBackButton/node1_base.dart b/packages/widgets/lib/StatelessWidget/CupertinoNavigationBarBackButton/node1_base.dart deleted file mode 100644 index f87463ad3..000000000 --- a/packages/widgets/lib/StatelessWidget/CupertinoNavigationBarBackButton/node1_base.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/7/22 -/// contact me by email 1981462002@qq.com -/// 说明: 218 CupertinoNavigationBarBackButton Cupertino风格的导航栏返回按钮,可指定颜色和点击事件,一般不单独使用。 -// { -// "widgetId": 218, -// "name": "返回按钮基本使用", -// "priority": 1, -// "subtitle": "【onPressed】 : 点击事件 【VoidCallback】\n" -// "【color】: 颜色 【Color】", -// } - -class CupertinoNavigationBarBackButtonDemo extends StatelessWidget { - const CupertinoNavigationBarBackButtonDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return CupertinoNavigationBarBackButton( - color: Colors.deepPurpleAccent, - onPressed: () => Navigator.of(context).pop(), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/CupertinoPageTransition/node1_base.dart b/packages/widgets/lib/StatelessWidget/CupertinoPageTransition/node1_base.dart deleted file mode 100644 index 69a5524ff..000000000 --- a/packages/widgets/lib/StatelessWidget/CupertinoPageTransition/node1_base.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/11 -/// contact me by email 1981462002@qq.com -/// -/// 说明: 216 CupertinoPageTransition 0 风格的页面过渡动画变换 提供一个 iOS 风格的页面过渡动画。 link 219 -// { -// "widgetId": 216, -// "name": 'CupertinoPageTransition 介绍', -// "priority": 1, -// "subtitle": "【child】 : 子组件 【Widget】\n" -// "【linearTransition】 : 是否线性转换 【bool】\n" -// "【primaryRouteAnimation】 : 初始路由动画 【Animation】\n" -// "【secondaryRouteAnimation】 : 第二路由动画 【Animation】", -// } -class CupertinoPageTransitionDemo extends StatelessWidget { - const CupertinoPageTransitionDemo({Key? key}) : super(key: key); - - final String info = - '该组件底层基于 SlideTransition 组件实现,主要用途是模仿 iOS 风格,处理页面间跳转的过渡动画。' - '源码中唯一的使用处是 CupertinoPageRoute 处理路由跳转动画时,一般不会单独使用。' - '如 A 跳转到 B, primaryRouteAnimation 和 secondaryRouteAnimation 都是一个 0.0->1.0 的动画,' - '前者用于处理 B 界面进入过渡动画;后者用于处理 A 界面被覆盖的过渡动画。'; - - @override - Widget build(BuildContext context) { - return Container( - color: Colors.blue.withOpacity(0.1), - padding: const EdgeInsets.all(10), - margin: const EdgeInsets.all(10), - child: Text(info ), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/CupertinoPopupSurface/node1_base.dart b/packages/widgets/lib/StatelessWidget/CupertinoPopupSurface/node1_base.dart deleted file mode 100644 index 0b4b281af..000000000 --- a/packages/widgets/lib/StatelessWidget/CupertinoPopupSurface/node1_base.dart +++ /dev/null @@ -1,64 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 217 CupertinoPopupSurface 模糊弹出层 -/// ios 弹出框的圆角矩形模糊背景,源码中应用于 Cupertino 风格的对话框中。 -/// -// { -// "widgetId": 217, -// "name": 'CupertinoPopupSurface 使用', -// "priority": 1, -// "subtitle": "【isSurfacePainted】 : 是否绘白 【bool】\n" -// "【child】 : 子组件 【Widget】\n" -// "测试效果左侧 isSurfacePainted = false,右侧 isSurfacePainted = true", -// } - -class CupertinoPopupSurfaceDemo extends StatelessWidget { - CupertinoPopupSurfaceDemo({Key? key}) : super(key: key); - - final List rainbow = [ - 0xffff0000, - 0xffFF7F00, - 0xffFFFF00, - 0xff00FF00, - 0xff00FFFF, - 0xff0000FF, - 0xff8B00FF - ]; - - final List stops = [0.0, 1 / 6, 2 / 6, 3 / 6, 4 / 6, 5 / 6, 1.0]; - - - @override - Widget build(BuildContext context) { - return Container( - decoration: BoxDecoration( - gradient: RadialGradient( - radius: 1.8, - stops: stops, - colors: rainbow.map((e) => Color(e)).toList())), - padding: const EdgeInsets.all(10), - child: Wrap( - spacing: 10, - children: [ - buildCupertinoPopupSurface(false), - buildCupertinoPopupSurface(true), - ], - ), - ); - } - - Widget buildCupertinoPopupSurface(bool isSurfacePainted) { - return CupertinoPopupSurface( - isSurfacePainted: isSurfacePainted, - child: Container( - width: 150, - height: 100, - color: Colors.white.withOpacity(0.3), - alignment: Alignment.center, - ), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/CupertinoTheme/node1_base.dart b/packages/widgets/lib/StatelessWidget/CupertinoTheme/node1_base.dart deleted file mode 100644 index b13349816..000000000 --- a/packages/widgets/lib/StatelessWidget/CupertinoTheme/node1_base.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 169, -// "name": '文字样式-TextTheme', -// "priority": 1, -// "subtitle": "后代组件可以通过CupertinoTheme.of获取主题的数据进行使用。", -// } -class TextCupertinoTheme extends StatelessWidget { - const TextCupertinoTheme({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - CupertinoTextThemeData queryData = CupertinoTheme.of(context).textTheme; - Map styles = { - "tabLabelTextStyle: ": queryData.tabLabelTextStyle, - "actionTextStyle: ": queryData.actionTextStyle, - "navActionTextStyle: ": queryData.navActionTextStyle, - "textStyle: ": queryData.textStyle, - "navTitleTextStyle: ": queryData.navTitleTextStyle, - "pickerTextStyle: ": queryData.pickerTextStyle, - "dateTimePickerTextStyle: ": queryData.dateTimePickerTextStyle, - "navLargeTitleTextStyle: ": queryData.navLargeTitleTextStyle, - }; - TextStyle style = - const TextStyle(fontSize: 16, fontWeight: FontWeight.bold); - return Column( - children: - styles.keys.map((e) => buildItem(e, style, styles[e]!)).toList(), - ); - } - - Widget buildItem(String label, TextStyle labelStyle, TextStyle style) => - Column( - children: [ - Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(label, style: labelStyle), - Text("@toly", style: style) - ], - ), - ), - const Divider(height: 1) - ], - ); -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/CupertinoTheme/node2_use.dart b/packages/widgets/lib/StatelessWidget/CupertinoTheme/node2_use.dart deleted file mode 100644 index 2a336d2a4..000000000 --- a/packages/widgets/lib/StatelessWidget/CupertinoTheme/node2_use.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 169, -// "name": 'CupertinoThemeData的使用', -// "priority": 2, -// "subtitle": -// "和Theme一样可以通过指定的属性,让它们在后代中共享,不过属性较少。注意如果需要使用主题,不能在当前的context中获取。", -// } - -class CustomCupertinoTheme extends StatelessWidget { - const CustomCupertinoTheme({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return const CupertinoTheme( - data: CupertinoThemeData( - primaryColor: Colors.blue, primaryContrastingColor: Colors.green), - child: _ChildUseTheme()); - } -} - -class _ChildUseTheme extends StatelessWidget { - const _ChildUseTheme({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Wrap( - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Container( - width: 50, - height: 50, - color: CupertinoTheme.of(context).primaryContrastingColor, - ), - SizedBox(width: 150, child: Slider(value: 0.8, onChanged: (v) => {})), - SizedBox( - width: 150, - child: Divider( - color: CupertinoTheme.of(context).primaryContrastingColor, - thickness: 1, - )) - ]); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/DataTable/node1_base.dart b/packages/widgets/lib/StatelessWidget/DataTable/node1_base.dart deleted file mode 100644 index 24f11f7a5..000000000 --- a/packages/widgets/lib/StatelessWidget/DataTable/node1_base.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-21 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 102, -// "name": 'DataTable基本使用', -// "priority": 1, -// "subtitle": -// "【columns】 : 列 【List】\n" -// "【rows】 : 行 【List】", -// } - -class _Bean { - final int id; - final String name; - final String type; - - _Bean(this.id, this.name, this.type); -} - -class CustomDataTable extends StatelessWidget { - CustomDataTable({Key? key}) : super(key: key); - - final List<_Bean> data = [ - _Bean(101, 'DataTable', 'StatelessWidget'), - _Bean(44, 'RangeSlider', 'StatefulWidget'), - _Bean(2, 'Text', 'StatelessWidget'), - _Bean(1, 'Image', 'StatefulWidget'), - ]; - - final List columns = ['id', '名称', '类型']; - - @override - Widget build(BuildContext context) { - return DataTable( - columns: columns - .map((String title) => DataColumn(label: Text(title))) - .toList(), - rows: data - .map((_Bean bean) => DataRow(cells: [ - DataCell(Text('${bean.id}')), - DataCell(Text(bean.name)), - DataCell(Text(bean.type)), - ])) - .toList()); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/DataTable/node2_operation.dart b/packages/widgets/lib/StatelessWidget/DataTable/node2_operation.dart deleted file mode 100644 index b9fa49448..000000000 --- a/packages/widgets/lib/StatelessWidget/DataTable/node2_operation.dart +++ /dev/null @@ -1,118 +0,0 @@ - - -/// create by 张风捷特烈 on 2020/4/25 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 102, -// "name": 'DataTable的sort', -// "priority": 2, -// "subtitle": -// "【sortColumnIndex】 : 列号 【int】\n" -// "【columnSpacing】 : 列间距 【double】\n" -// "【sortAscending】 : 是否顺序 【bool】", -// } - -import 'package:flutter/material.dart'; - -class _BeanOp { - final int id; - final String name; - final String type; - bool select; - - _BeanOp(this.id, this.name, this.type, this.select); - - @override - String toString() { - return '_BeanOp{id: $id, name: $name, type: $type, select: $select}'; - } -} - -class SortDataTable extends StatefulWidget { - const SortDataTable({Key? key}) : super(key: key); - - @override - _SortDataTableState createState() => _SortDataTableState(); -} - -class _SortDataTableState extends State { - List<_BeanOp> data = [ - _BeanOp(101, 'DataTable', 'StatelessWidget', false), - _BeanOp(44, 'RangeSlider', 'StatefulWidget', false), - _BeanOp(2, 'Text', 'StatelessWidget', false), - _BeanOp(1, 'Image', 'StatefulWidget', false), - ]; - - bool _sortAscending = false; - List<_BeanOp> selectData = <_BeanOp>[]; - - @override - Widget build(BuildContext context) { - return DataTable( - columnSpacing: 20, - sortColumnIndex: 1, - sortAscending: _sortAscending, - columns: [ - DataColumn( - label: Checkbox( - value: selectData.length == data.length, - onChanged: _onSelectAll, - ), - ), - DataColumn(label: const Text('id'), numeric: false, onSort: _onSortId), - const DataColumn(label: Text('名称')), - const DataColumn(label: Text('类型')), - ], - rows: data - .map((e) => DataRow(selected: false, cells: [ - DataCell(Checkbox( - value: e.select, - onChanged: (v) => _onSelectOne(v, e), - )), - DataCell(Text('${e.id}')), - DataCell(Text(e.name), - showEditIcon: true, onTap: () {}), - DataCell(Text(e.type)), - ])) - .toList()); - } - - _onSortId(int index, bool ascending) { - setState(() { - _sortAscending = ascending; - data.sort( - (a, b) => ascending ? a.id.compareTo(b.id) : b.id.compareTo(a.id)); - }); - } - - void _onSelectOne(bool? selected, _BeanOp e) { - if(selected==null) return; - setState(() { - if (selected) { - selectData.add(e); - } else { - selectData.remove(e); - } - e.select = selected; - }); - } - - void _onSelectAll(bool? select) { - if(select==null) return; - setState(() { - if (select) { - for (_BeanOp e in data) { - e.select = true; - } - selectData = data.map((e) => e).toList(); - } else { - for (_BeanOp e in data) { - e.select = false; - } - selectData = []; - } - }); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/DayPicker/node1_base.dart b/packages/widgets/lib/StatelessWidget/DayPicker/node1_base.dart deleted file mode 100644 index 5fc129372..000000000 --- a/packages/widgets/lib/StatelessWidget/DayPicker/node1_base.dart +++ /dev/null @@ -1,53 +0,0 @@ - - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 134, -// "name": 'DayPicker基本使用', -// "priority": 1, -// "subtitle": -// "【selectedDate】 : 选中日期 【DateTime】\n" -// "【currentDate】 : 当前日期 【DateTime】\n" -// "【firstDate】 : 最前日期限制 【DateTime】\n" -// "【lastDate】 : 最后日期限制 【DateTime】\n" -// "【displayedMonth】 : 当前展示的月份 【DateTime】\n" -// "【onChanged】 : 点击回调 【Function(DateTime)】", -// } -import 'package:flutter/material.dart'; - -class CustomDayPicker extends StatelessWidget{ - - const CustomDayPicker({Key? key}) : super(key: key); - - final String info = - 'DayPicker 日期选择器于 Flutter3.0 退出历史舞台。取代者为 CalendarDatePicker 日历选择器。'; - - @override - Widget build(BuildContext context) { - return Container( - color: Colors.blue.withOpacity(0.1), - padding: const EdgeInsets.all(10), - margin: const EdgeInsets.all(10), - child: Text(info), - ); - } - - // final DateTime _date = DateTime.now(); - // - // @override - // Widget build(BuildContext context) { - // return SizedBox( - // height: 350, - // child: DayPicker( - // selectedDate: _date, - // currentDate: DateTime.now(), - // onChanged: (date)=> setState(() => _date = date), - // firstDate: DateTime(2018), - // lastDate: DateTime(2030), - // displayedMonth: DateTime.now() - // ), - // ); - // } -} diff --git a/packages/widgets/lib/StatelessWidget/Dialog/node1_base.dart b/packages/widgets/lib/StatelessWidget/Dialog/node1_base.dart deleted file mode 100644 index e5af212c0..000000000 --- a/packages/widgets/lib/StatelessWidget/Dialog/node1_base.dart +++ /dev/null @@ -1,139 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-03-24 -/// contact me by email 1981462002@qq.com -/// 说明: -/// -// { -// "widgetId": 126, -// "name": 'Dialog基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 动画图标数据 【Widget】\n" -// "【elevation】 : 影深 【double】\n" -// "【backgroundColor】 : 背景色 【Color】\n" -// "【shape】 : 形状 【ShapeBorder】", -// } -class CustomDialog extends StatelessWidget { - const CustomDialog({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Column( - children: [ - _buildRaisedButton(context), - _buildDialog(), - ], - ); - } - - Widget _buildDialog() => const Dialog( - backgroundColor: Colors.white, - elevation: 5, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10))), - child: SizedBox( - width: 50, - child: DeleteDialog(), - ), - ); - - Widget _buildRaisedButton(BuildContext context) => ElevatedButton( - style: TextButton.styleFrom( - backgroundColor: Colors.blue, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10)), - ), - ), - onPressed: () { - showDialog(context: context, builder: (ctx) => _buildDialog()); - }, - child: const Text( - 'Just Show It !', - style: TextStyle(color: Colors.white), - ), - ); - -} - -class DeleteDialog extends StatelessWidget { - const DeleteDialog({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - _buildBar(context), - _buildTitle(), - _buildContent(), - _buildFooter(context), - ], - ); - } - - Widget _buildTitle() { - return const Text( - 'Delete Doucument', - style: TextStyle(color: Color(0xff5CC5E9), fontSize: 24), - ); - } - - Widget _buildContent() { - return const Padding( - padding: EdgeInsets.all(15.0), - child: Text( - ' Hi toly! If you push the conform buttom ,' - ' You will lose this file. Are you sure wand to do that?', - style: TextStyle(color: Color(0xffCFCFCF), fontSize: 16), - textAlign: TextAlign.justify, - ), - ); - } - - Widget _buildFooter(context) { - return Padding( - padding: const EdgeInsets.only(bottom: 15.0, top: 10,left: 10,right: 10), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Container( - alignment: Alignment.center, - height: 40, - width: 100, - decoration: const BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(30)), - color: Color(0xff73D1EE)), - child: const Text('Yes', - style: TextStyle(color: Colors.white, fontSize: 16)), - ), - InkWell( - onTap: ()=>Navigator.of(context).pop(), - child: Container( - alignment: Alignment.center, - height: 40, - width: 100, - decoration: const BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(30)), - color: Colors.orangeAccent), - child: const Text('Cancle', - style: TextStyle(color: Colors.white, fontSize: 16)), - ), - ) - ], - ), - ); - } - - Widget _buildBar(context) => Container( - height: 30, - alignment: Alignment.centerRight, - margin: const EdgeInsets.only(right: 10, top: 5), - child: InkWell( - onTap: ()=>Navigator.of(context).pop(), - child: const Icon( - Icons.close, - color: Color(0xff82CAE3), - ), - ), - ); -} diff --git a/packages/widgets/lib/StatelessWidget/Divider/node1_base.dart b/packages/widgets/lib/StatelessWidget/Divider/node1_base.dart deleted file mode 100644 index 952f021b6..000000000 --- a/packages/widgets/lib/StatelessWidget/Divider/node1_base.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-24 -/// contact me by email 1981462002@qq.com -/// 说明: -/// -// { -// "widgetId": 34, -// "priority": 1, -// "name": "Divider颜色和粗细", -// "subtitle": "【color】: 颜色 【Color】\n" -// "【thickness】: 线粗细 【double】", -// } - -class CustomDivider extends StatelessWidget { - const CustomDivider({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - List dataColor = [ - Colors.red, Colors.yellow, - Colors.blue, Colors.green]; - List dataThickness = [1.0, 2.0, 4.0, 6.0]; - Map data = Map.fromIterables(dataColor, dataThickness); - return Column( - children: dataColor - .map((e) => Divider( - color: e, - thickness: data[e], - )).toList(), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/Divider/node2_height.dart b/packages/widgets/lib/StatelessWidget/Divider/node2_height.dart deleted file mode 100644 index 1064d09b3..000000000 --- a/packages/widgets/lib/StatelessWidget/Divider/node2_height.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-24 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 34, -// "priority": 2, -// "name": "Divider高度和空缺", -// "subtitle": "【indent】: 前面空缺长度 【double】\n" -// "【endIndent】: 后面空缺长度 【double】\n" -// "【height】: 占位高 【double】", -// } - - -class HeightDivider extends StatelessWidget { - const HeightDivider({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - List dataThickness = [10.0, 20.0, 30.0, 40.0]; - List dataColor = [ - Colors.red, Colors.yellow, - Colors.blue, Colors.green]; - Map data = Map.fromIterables(dataColor, dataThickness); - - return Column( - children: dataColor - .map((Color color) => Divider( - color: color, - indent:data[color], - endIndent: data[color]!*2, - height: data[color], - thickness: data[color]!/10, - )) - .toList(), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/DraggableScrollableActuator/node1_base.dart b/packages/widgets/lib/StatelessWidget/DraggableScrollableActuator/node1_base.dart deleted file mode 100644 index abc8a027f..000000000 --- a/packages/widgets/lib/StatelessWidget/DraggableScrollableActuator/node1_base.dart +++ /dev/null @@ -1,117 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 221 DraggableScrollableActuator 拖滑重置器 它可以通知后代的 DraggableScrollableSheet,将其位置重置为初始状态。 -// { -// "widgetId": 221, -// "name": '基本使用方法', -// "priority": 1, -// "subtitle": "【child】 : 子组件 【Widget】\n" -// "使用 DraggableScrollableActuator.reset(context) 重置后代 DraggableScrollableSheet 位初始位置。", -// } - -class DraggableScrollableActuatorDemo extends StatelessWidget { - const DraggableScrollableActuatorDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - padding: const EdgeInsets.all(10), - child: ElevatedButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => DraggableScrollableActuatorPage()), - ); - }, - child: const Text("进入 DraggableScrollableActuator 测试页"), - ), - ); - } -} - -class DraggableScrollableActuatorPage extends StatelessWidget { - DraggableScrollableActuatorPage({Key? key}) : super(key: key); - - final List data = [ - Colors.orange[50]!, - Colors.orange[100]!, - Colors.orange[200]!, - Colors.orange[300]!, - Colors.orange[400]!, - Colors.orange[500]!, - Colors.orange[600]!, - Colors.orange[700]!, - Colors.orange[800]!, - Colors.orange[900]!, - Colors.red[50]!, - Colors.red[100]!, - Colors.red[200]!, - Colors.red[300]!, - Colors.red[400]!, - Colors.red[500]!, - Colors.red[600]!, - Colors.red[700]!, - Colors.red[800]!, - Colors.red[900]!, - ]; - - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text("DraggableScrollableActuator"), - ), - body: DraggableScrollableActuator( - child: Builder( - builder: (ctx) => Column( - children: [ - ElevatedButton( - onPressed: () { - DraggableScrollableActuator.reset(ctx); - }, - child: const Text("重置位置"), - ), - Expanded( - child: buildSheet(), - ), - ], - ), - ), - ), - ); - } - - Widget buildSheet() => DraggableScrollableSheet( - initialChildSize: 0.3, - minChildSize: 0.2, - maxChildSize: 1, - expand: true, - builder: (BuildContext context, ScrollController scrollController) => - ListView.builder( - controller: scrollController, - itemCount: data.length, - itemBuilder: buildColorItem, - ), - ); - - Widget buildColorItem(BuildContext context, int index) { - return Container( - alignment: Alignment.center, - height: 60, - color: data[index], - child: Text( - colorString(data[index]), - style: const TextStyle(color: Colors.white, shadows: [ - Shadow(color: Colors.black, offset: Offset(.5, .5), blurRadius: 2) - ]), - ), - ); - } - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/packages/widgets/lib/StatelessWidget/Drawer/node1_base.dart b/packages/widgets/lib/StatelessWidget/Drawer/node1_base.dart deleted file mode 100644 index b57bc0de4..000000000 --- a/packages/widgets/lib/StatelessWidget/Drawer/node1_base.dart +++ /dev/null @@ -1,73 +0,0 @@ - -/// create by 张风捷特烈 on 2020-03-26 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 154, -// "name": 'Drawer基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 子组件 【Widget】\n" -// "【elevation】 : 影深 【double】", -// } -import 'package:flutter/material.dart'; - -class CustomDrawer extends StatelessWidget { - const CustomDrawer({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 400, - child: Scaffold( - appBar: AppBar( - title: const Text('Flutter Unit'), - ), - drawer: Drawer( - elevation: 3, - child: _buildChild(), - ), - ), - ); - } - - Widget _buildChild() => ListView( - padding: EdgeInsets.zero, - children: const [ - DrawerHeader( - decoration: BoxDecoration( - image: DecorationImage( - image: AssetImage('assets/images/caver.webp'), - fit: BoxFit.cover), - ), - child: Text( - '张风捷特烈', - style: TextStyle(fontSize: 24, color: Colors.white, shadows: [ - Shadow(color: Colors.black, offset: Offset(1, 1), blurRadius: 3) - ]), - ), - ), - ListTile( - leading: Icon( - Icons.star, - color: Colors.blue, - ), - title: Text('我的收藏'), - ), - ListTile( - leading: Icon( - Icons.palette, - color: Colors.orangeAccent, - ), - title: Text('我的绘画'), - ), - ListTile( - leading: Icon( - Icons.insert_drive_file, - color: Colors.green, - ), - title: Text('我的文件'), - ), - ], - ); -} diff --git a/packages/widgets/lib/StatelessWidget/DrawerButton/node1_base.dart b/packages/widgets/lib/StatelessWidget/DrawerButton/node1_base.dart deleted file mode 100644 index 45699d5b4..000000000 --- a/packages/widgets/lib/StatelessWidget/DrawerButton/node1_base.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2023/11/28 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 276, -// "priority": 1, -// "name": "DrawerButton 基本使用", -// "subtitle": "【onPressed】 : 点击事件 【VoidCallback?】\n" -// "【style】: 按钮样式 【ButtonStyle?】\n" -// "onPressed 为空时,点击时会打开左抽屉。", -// } - -class DrawerButtonDemo extends StatelessWidget { - const DrawerButtonDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return DrawerButton( - style: TextButton.styleFrom( - backgroundColor: Colors.blue, - iconColor: Colors.white, - ), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/DrawerButtonIcon/node1_base.dart b/packages/widgets/lib/StatelessWidget/DrawerButtonIcon/node1_base.dart deleted file mode 100644 index 3ad0c9da0..000000000 --- a/packages/widgets/lib/StatelessWidget/DrawerButtonIcon/node1_base.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2023/11/28 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 273, -// "priority": 1, -// "name": "DrawerButtonIcon 效果", -// "subtitle": "通过 _ActionIcon 组件根据 ActionIconTheme 主题,来适配不同平台的抽屉按钮图标。", -// } -class DrawerButtonIconDemo extends StatelessWidget { - const DrawerButtonIconDemo({super.key}); - - @override - Widget build(BuildContext context) { - return const DrawerButtonIcon(); - } -} diff --git a/packages/widgets/lib/StatelessWidget/DrawerHeader/node1_base.dart b/packages/widgets/lib/StatelessWidget/DrawerHeader/node1_base.dart deleted file mode 100644 index 0b976fdbf..000000000 --- a/packages/widgets/lib/StatelessWidget/DrawerHeader/node1_base.dart +++ /dev/null @@ -1,84 +0,0 @@ - -/// create by 张风捷特烈 on 2020-03-26 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 155, -// "name": 'DrawerHeader基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 子组件 【Widget】\n" -// "【decoration】 : 装饰 【Decoration】\n" -// "【margin】 : 外边距 【EdgeInsetsGeometry】\n" -// "【padding】 : 内边距 【EdgeInsetsGeometry】", -// } - -import 'package:flutter/material.dart'; -class CustomDrawerHeader extends StatelessWidget { - const CustomDrawerHeader({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 400, - child: Scaffold( - appBar: AppBar( - title: const Text('Flutter Unit'), - ), - drawer: Drawer( - elevation: 3, - child: _buildChild(), - ), - ), - ); - } - - Widget _buildChild() => ListView( - padding: EdgeInsets.zero, - children: [ - _buildHeader(), - const ListTile( - leading: Icon( - Icons.star, - color: Colors.blue, - ), - title: Text('我的收藏'), - ), - const ListTile( - leading: Icon( - Icons.palette, - color: Colors.orangeAccent, - ), - title: Text('我的绘画'), - ), - const ListTile( - leading: Icon( - Icons.insert_drive_file, - color: Colors.green, - ), - title: Text('我的文件'), - ), - ], - ); - - Widget _buildHeader() => const DrawerHeader( - margin: EdgeInsets.all(10), - padding: EdgeInsets.only(left: 20,top: 15), - decoration: BoxDecoration( - borderRadius: BorderRadius.only( - topLeft:Radius.circular(40), - topRight:Radius.circular(40) - ), - image: DecorationImage( - image: AssetImage('assets/images/caver.webp'), - fit: BoxFit.cover), - ), - child: Text( - '张风捷特烈', - style: TextStyle(fontSize: 24, color: Colors.white, shadows: [ - Shadow(color: Colors.black, offset: Offset(1, 1), blurRadius: 3) - ]), - ), - ); -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/EndDrawerButton/node1_base.dart b/packages/widgets/lib/StatelessWidget/EndDrawerButton/node1_base.dart deleted file mode 100644 index e11381e20..000000000 --- a/packages/widgets/lib/StatelessWidget/EndDrawerButton/node1_base.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2023/11/28 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 361, -// "priority": 1, -// "name": "EndDrawerButton 基本使用", -// "subtitle": "【onPressed】 : 点击事件 【VoidCallback?】\n" -// "【style】: 按钮样式 【ButtonStyle?】\n" -// "onPressed 为空时,点击时会打开右抽屉。", -// } - -class EndDrawerButtonDemo extends StatelessWidget { - const EndDrawerButtonDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return EndDrawerButton( - style: TextButton.styleFrom( - backgroundColor: Colors.blue, - iconColor: Colors.white, - ), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/EndDrawerButtonIcon/node1_base.dart b/packages/widgets/lib/StatelessWidget/EndDrawerButtonIcon/node1_base.dart deleted file mode 100644 index 75c9500f8..000000000 --- a/packages/widgets/lib/StatelessWidget/EndDrawerButtonIcon/node1_base.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2023/11/28 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 275, -// "priority": 1, -// "name": "EndDrawerButtonIcon 效果", -// "subtitle": "通过 _ActionIcon 组件根据 ActionIconTheme 主题,来适配不同平台的右抽屉按钮图标。", -// } -class EndDrawerButtonIconDemo extends StatelessWidget { - const EndDrawerButtonIconDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return const EndDrawerButtonIcon(); - } -} diff --git a/packages/widgets/lib/StatelessWidget/FadeInImage/node1_base.dart b/packages/widgets/lib/StatelessWidget/FadeInImage/node1_base.dart deleted file mode 100644 index bd759cf28..000000000 --- a/packages/widgets/lib/StatelessWidget/FadeInImage/node1_base.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-26 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 8, -// "priority": 1, -// "name": "FadeInImage.assetNetwork加载网络图片", -// "subtitle": "【placeholder】 : 展位图地址 【String】\n" -// "【image】 : 显示图地址 【String】\n" -// "【width】: 宽 【double】\n" -// "【height】: 高 【double】\n" -// "【fadeInDuration】: 淡入时长 【Duration】\n" -// "【fadeOutDuration】: 淡出时长 【Duration】\n" -// "【fadeInCurve】: 淡入曲线 【Cubic】\n" -// "【fadeOutCurve】: 淡出曲线 【Cubic】\n" -// "【fit】: 适应模式 【BoxFit】\n" -// "【alignment】: 对齐模式 【Alignment】\n" -// "【repeat】: 重复模式 【ImageRepeat】\n", -// } - -class CustomFadeInImage extends StatelessWidget { - const CustomFadeInImage({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - String placeholder = "assets/images/icon_head.webp"; - String img = - "https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/" - "15739960c2da4de3a263eeabcb60057f~tplv-k3u1fbpfcp-zoom-crop-mark" - ":1304:1304:1304:734.awebp"; - return FadeInImage.assetNetwork( - placeholder: placeholder, - image: img, - width: 100, - height: 100, - fit: BoxFit.cover, - repeat:ImageRepeat.noRepeat, - alignment: Alignment.center, - fadeInDuration:const Duration(seconds: 5), - fadeInCurve: Curves.easeInCubic, - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/FilterChip/node1_base.dart b/packages/widgets/lib/StatelessWidget/FilterChip/node1_base.dart deleted file mode 100644 index f4db385a4..000000000 --- a/packages/widgets/lib/StatelessWidget/FilterChip/node1_base.dart +++ /dev/null @@ -1,70 +0,0 @@ -/// create by 张风捷特烈 on 2020-03-26 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 15, -// "priority": 1, -// "name": "FilterChip可接受选择事件", -// "subtitle": "【selected】: 是否选择 【bool】\n" -// "【onSelected】: 选择事件 【Function(bool)】\n" -// "【selectedColor】: 选择后的颜色 【Color】\n" -// "【selectedShadowColor】: 选择后的阴影颜色 【Color】\n", -// } -import 'package:flutter/material.dart'; - -class CustomFilterChip extends StatefulWidget { - const CustomFilterChip({Key? key}) : super(key: key); - - @override - _CustomFilterChipState createState() => _CustomFilterChipState(); -} - -class _CustomFilterChipState extends State { - final Map map = { - 'A': 'Ant', - 'B': 'Bug', - 'C': 'Cat', - 'D': 'Dog', - }; - final List _selected = []; - - @override - Widget build(BuildContext context) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Wrap( - children: map.keys.map((key) => _buildChild(key)).toList(), - ), - Container( - padding: const EdgeInsets.all(10), - child: Text('您已选择: ${_selected.join(', ')}')), - ], - ); - } - - Padding _buildChild(String key) => Padding( - padding: const EdgeInsets.all(4.0), - child: FilterChip( - selectedColor: Colors.orange.withAlpha(55), - selectedShadowColor: Colors.blue, - shadowColor: Colors.orangeAccent, - pressElevation: 5, - elevation: 3, - avatar: CircleAvatar(child: Text(key)), - label: Text(map[key]!), - selected: _selected.contains(map[key]), - onSelected: (bool value) => _onSelected(value, key), - ), - ); - - void _onSelected(bool value, String key) { - setState(() { - if (value) { - _selected.add(map[key]!); - } else { - _selected.removeWhere((name) => name == map[key]); - } - }); - } -} diff --git a/packages/widgets/lib/StatelessWidget/FlatButton/node1_base.dart b/packages/widgets/lib/StatelessWidget/FlatButton/node1_base.dart deleted file mode 100644 index 856b6cf18..000000000 --- a/packages/widgets/lib/StatelessWidget/FlatButton/node1_base.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-03-26 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 25, -// "priority": 1, -// "name": "FlatButton点击事件", -// "subtitle": "【color】: 颜色 【Color】\n" -// "【splashColor】: 水波纹颜色 【Color】\n" -// "【child】: 子组件 【Widget】\n" -// "【textColor】: 子组件文字颜色 【Color】\n" -// "【highlightColor】: 长按高亮色 【Color】\n" -// "【padding】: 内边距 【EdgeInsetsGeometry】\n" -// "【onPressed】: 点击事件 【Function】", -// } - -class CustomFlatButton extends StatelessWidget { - const CustomFlatButton({Key? key}) : super(key: key); - - final String info = - 'FlatButton 按钮于 Flutter3.3 退出历史舞台。取代者为 ElevatedButton 按钮。'; - - @override - Widget build(BuildContext context) { - return Container( - color: Colors.blue.withOpacity(0.1), - padding: const EdgeInsets.all(10), - margin: const EdgeInsets.all(10), - child: Text(info), - ); - } - -// @override -// Widget build(BuildContext context) { -// return FlatButton( -// onPressed: ()=>{}, -// padding: const EdgeInsets.all(8), -// splashColor: Colors.green, -// child: const Text("FlatButton"), -// textColor: const Color(0xffFfffff), -// color: Colors.blue, -// highlightColor: const Color(0xffF88B0A), -// ); -// } -} diff --git a/packages/widgets/lib/StatelessWidget/FloatingActionButton/node1_base.dart b/packages/widgets/lib/StatelessWidget/FloatingActionButton/node1_base.dart deleted file mode 100644 index 9a5e7b34a..000000000 --- a/packages/widgets/lib/StatelessWidget/FloatingActionButton/node1_base.dart +++ /dev/null @@ -1,40 +0,0 @@ -/// create by 张风捷特烈 on 2020-03-26 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 28, -// "priority": 1, -// "name": "FloatingActionButton点击事件", -// "subtitle": "【child】: 子组件 【Widget】\n" -// "【tooltip】: 长按时提示文字 【String】\n" -// "【backgroundColor】: 背景色 【Color】\n" -// "【foregroundColor】: 前景色 【Color】\n" -// "【elevation】: 影深 【double】\n" -// "【onPressed】: 点击事件 【Function】", -// } -import 'package:flutter/material.dart'; - -class CustomFAB extends StatelessWidget { - const CustomFAB({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - Map data = { - Colors.red: Icons.add, - Colors.blue: Icons.bluetooth, - Colors.green: Icons.android, - }; - return Wrap( - spacing: 20, - children: data.keys - .map((e) => FloatingActionButton( - heroTag: e.toString()+"a", - onPressed: () {}, - backgroundColor: e, - foregroundColor: Colors.white, - child: Icon(data[e]), - tooltip: "android", - elevation: 5, //z-阴影盖度 - )).toList()); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/FloatingActionButton/node2_mini.dart b/packages/widgets/lib/StatelessWidget/FloatingActionButton/node2_mini.dart deleted file mode 100644 index e67509983..000000000 --- a/packages/widgets/lib/StatelessWidget/FloatingActionButton/node2_mini.dart +++ /dev/null @@ -1,37 +0,0 @@ -/// create by 张风捷特烈 on 2020-03-26 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 28, -// "priority": 2, -// "name": "mini属性", -// "subtitle": "【mini】: 是否是迷你 【bool】", -// } - -import 'package:flutter/material.dart'; - -class MiniFAB extends StatelessWidget { - const MiniFAB({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - Map data = { - Colors.red: Icons.add, - Colors.blue: Icons.bluetooth, - Colors.green: Icons.android, - }; - return Wrap( - spacing: 20, - children: data.keys - .map((e) => FloatingActionButton( - heroTag: e.toString()+"b", - onPressed: () {}, - backgroundColor: e, - mini: true, - foregroundColor: Colors.white, - child: Icon(data[e]), - tooltip: "android", - elevation: 5, //z-阴影盖度 - )).toList()); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/FloatingActionButton/node3_shape.dart b/packages/widgets/lib/StatelessWidget/FloatingActionButton/node3_shape.dart deleted file mode 100644 index 276d60435..000000000 --- a/packages/widgets/lib/StatelessWidget/FloatingActionButton/node3_shape.dart +++ /dev/null @@ -1,63 +0,0 @@ - - -/// create by 张风捷特烈 on 2020-03-26 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 28, -// "priority": 3, -// "name": "shape属性", -// "subtitle": "【shape】: 形状 【ShapeBorder】", -// } - -import 'dart:math'; - -import 'package:flutter/material.dart'; -import 'package:widgets/utils/pather.dart'; - -class ShapeFAB extends StatelessWidget { - const ShapeFAB({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - Map data = { - Colors.red: Icons.add, - Colors.blue: Icons.bluetooth, - Colors.green: Icons.android, - }; - return Wrap( - spacing: 20, - children: data.keys - .map((e) => FloatingActionButton( - heroTag: e.toString()+"c", - onPressed: () {}, - backgroundColor: e, - shape: StarBorder(), - foregroundColor: Colors.white, - child: Icon(data[e]), - tooltip: "android", - elevation: 5, - )).toList()); - } -} - -/// 边线形状类 -class StarBorder extends ShapeBorder { - @override - EdgeInsetsGeometry get dimensions => EdgeInsets.zero; - - @override - Path getInnerPath(Rect rect, {TextDirection? textDirection}) => Path(); - - @override - Path getOuterPath(Rect rect, {TextDirection? textDirection}) { - return Pather.create.nStarPath(20, 25, 25 * cos((360 / 9 / 2) * pi / 180), - dx: rect.height / 2, dy: rect.width / 2); - } - - @override - void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) {} - - @override - ShapeBorder scale(double t) => this; -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/FlutterLogo/node1_base.dart b/packages/widgets/lib/StatelessWidget/FlutterLogo/node1_base.dart deleted file mode 100644 index d45dad479..000000000 --- a/packages/widgets/lib/StatelessWidget/FlutterLogo/node1_base.dart +++ /dev/null @@ -1,35 +0,0 @@ - - -/// create by 张风捷特烈 on 2020-03-26 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 4, -// "priority": 1, -// "name": "用于显示一个FlutterLogo", -// "subtitle": "【size】 : 大小 【double】\n" -// "【colors】: 颜色 【MaterialColor】", -// } -import 'package:flutter/material.dart'; - -class CustomFlutterLogo extends StatelessWidget { - const CustomFlutterLogo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - - Map data = { - Colors.blue:50.0, - Colors.red:60.0, - Colors.green:70.0, - Colors.yellow:80.0, - }; - return Wrap( - children: data.keys - .map((e) => FlutterLogo( - size: data[e], - textColor: e, - )).toList(), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/FlutterLogo/node2_style.dart b/packages/widgets/lib/StatelessWidget/FlutterLogo/node2_style.dart deleted file mode 100644 index 3e9f4197f..000000000 --- a/packages/widgets/lib/StatelessWidget/FlutterLogo/node2_style.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-26 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 4, -// "priority": 2, -// "name": "样式用于显示文字", -// "subtitle": "【style】 : 样式-3种枚举 【FlutterLogoStyle】\n" -// "【textColor】: 文字颜色 【Color】", -// } -class FlutterLogoWithText extends StatelessWidget { - const FlutterLogoWithText({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - final Map data = { - FlutterLogoStyle.horizontal: Colors.blue, - FlutterLogoStyle.markOnly: Colors.red, - FlutterLogoStyle.stacked: Colors.green, - }; - - return Wrap( - spacing: 20, - children: data.keys - .map((FlutterLogoStyle style) => FlutterLogo( - size: 80, - style: style, - textColor: data[style]!, - )) - .toList(), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/GestureDetector/node1_base.dart b/packages/widgets/lib/StatelessWidget/GestureDetector/node1_base.dart deleted file mode 100644 index 697fdca87..000000000 --- a/packages/widgets/lib/StatelessWidget/GestureDetector/node1_base.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/27 -/// contact me by email 1981462002@qq.com -/// 说明: - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 146, -// "name": 'GestureDetector基本事件', -// "priority": 1, -// "subtitle": -// "【child】 : 子组件 【Widget】\n" -// "【onTap】 : 点击事件 【Function()】\n" -// "【onDoubleTap】 : 双击事件 【GestureTapCallback】\n" -// "【onLongPress】 : 长按事件 【GestureLongPressCallback】", -// } - -class CustomGestureDetector extends StatefulWidget { - const CustomGestureDetector({Key? key}) : super(key: key); - - @override - _CustomGestureDetectorState createState() => _CustomGestureDetectorState(); -} - -class _CustomGestureDetectorState extends State { - String _info = ''; - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: () => setState(() => _info = 'onTap'), - onDoubleTap: () => setState(() => _info = 'onDoubleTap'), - onLongPress: () => setState(() => _info = 'onLongPress'), - child: Container( - alignment: Alignment.center, - width: 300, - height: 300 * 0.4, - color: Colors.grey.withAlpha(33), - child: Text( - _info, - style: const TextStyle(fontSize: 18, color: Colors.blue), - ), - ), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/GestureDetector/node2_tap.dart b/packages/widgets/lib/StatelessWidget/GestureDetector/node2_tap.dart deleted file mode 100644 index 63522f2df..000000000 --- a/packages/widgets/lib/StatelessWidget/GestureDetector/node2_tap.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/27 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 146, -// "name": 'GestureDetector详情信息', -// "priority": 2, -// "subtitle": -// "【onTapDown】 : 按下回调 【GestureTapDownCallback】\n" -// "【onTapUp】 : 点击抬起回调 【GestureTapUpCallback】\n" -// "【onTapCancel】 : 点击取消 【GestureTapCancelCallback】", -// } -class TapGestureDetector extends StatefulWidget { - const TapGestureDetector({Key? key}) : super(key: key); - - @override - _TapGestureDetectorState createState() => _TapGestureDetectorState(); -} - -class _TapGestureDetectorState extends State { - String _info = ''; - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTapDown: (detail) => setState(() => _info = - 'onTapDown:\n相对落点:${detail.localPosition}\n绝对落点:${detail.globalPosition}'), - onTapUp: (detail) => setState(() => _info = - 'onTapUp:\n相对落点:${detail.localPosition}\n绝对落点:${detail.globalPosition}'), - onTapCancel: () => setState(() => _info = 'onTapCancel'), - child: Container( - alignment: Alignment.center, - width: 300, - height: 300 * 0.618, - color: Colors.grey.withAlpha(33), - child: Text( - _info, - style: const TextStyle(fontSize: 18, color: Colors.blue), - ), - ), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/GestureDetector/node3_pan.dart b/packages/widgets/lib/StatelessWidget/GestureDetector/node3_pan.dart deleted file mode 100644 index 18a059004..000000000 --- a/packages/widgets/lib/StatelessWidget/GestureDetector/node3_pan.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 146, -// "name": 'GestureDetector的Pan事件', -// "priority": 3, -// "subtitle": -// "【onPanDown】 : 按下回调 【GestureDragDownCallback】\n" -// "【onPanEnd】 : 拖动结束 【GestureDragEndCallback】\n" -// "【onPanStart】 : 开始拖动 【GestureDragStartCallback】\n" -// "【onPanUpdate】 : 拖动更新 【GestureDragUpdateCallback】\n" -// "【onPanCancel】 : 拖动取消 【GestureDragCancelCallback】", -// } -class PanGestureDetector extends StatefulWidget { - const PanGestureDetector({Key? key}) : super(key: key); - - @override - _PanGestureDetectorState createState() => _PanGestureDetectorState(); -} - -class _PanGestureDetectorState extends State { - String _info = ''; - - @override - Widget build(BuildContext context) { - return GestureDetector( - onPanDown: (detail) => setState(() => _info = - 'onPanDown:\n相对落点:${detail.localPosition}\n绝对落点:${detail.globalPosition}'), - onPanEnd: (detail) => setState(() => _info = - 'onPanEnd:\n初速度:${detail.primaryVelocity}\n最终速度:${detail.velocity}'), - onPanUpdate: (detail) => setState(() => _info = - 'onPanUpdate:\n相对落点:${detail.localPosition}\n绝对落点:${detail.globalPosition}'), - onPanStart: (detail) => setState(() => _info = - 'onPanStart:\n相对落点:${detail.localPosition}\n绝对落点:${detail.globalPosition}'), - onPanCancel: () => setState(() => _info = 'onTapCancel'), - child: SingleChildScrollView( - child: Container( - alignment: Alignment.center, - width: 300, - height: 300 * 0.618, - color: Colors.grey.withAlpha(33), - child: Text( - _info, - style: const TextStyle(fontSize: 18, color: Colors.blue), - ), - ), - ), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/GirdView/node1_base.dart b/packages/widgets/lib/StatelessWidget/GirdView/node1_base.dart deleted file mode 100644 index 49b729d46..000000000 --- a/packages/widgets/lib/StatelessWidget/GirdView/node1_base.dart +++ /dev/null @@ -1,56 +0,0 @@ - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-27 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 163, -// "name": 'GridView.count构造', -// "priority": 1, -// "subtitle": -// "【children】 : 子组件列表 【List】\n" -// "【crossAxisCount】 : 主轴一行box数量 【int】\n" -// "【mainAxisSpacing】 : 主轴每行间距 【double】\n" -// "【crossAxisSpacing】 : 交叉轴每行间距 【double】\n" -// "【childAspectRatio】 : box主长/交叉轴长 【double】\n" -// "【crossAxisCount】 : 主轴一行数量 【int】", -// } -class CustomGridView extends StatelessWidget { - CustomGridView({Key? key}) : super(key: key); - - final List data = List.generate(128, (i) => Color(0xFFFF00FF - 2 * i)); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 200, - child: GridView.count( - crossAxisCount: 4, - mainAxisSpacing: 2, - crossAxisSpacing: 2, - childAspectRatio: 1 / 0.618, - children: data.map((color) => _buildItem(color)).toList(), - ), - ); - } - - Container _buildItem(Color color) => Container( - alignment: Alignment.center, - width: 100, - height: 30, - color: color, - child: Text( - colorString(color), - style: const TextStyle( - color: Colors.white, - shadows: [ - Shadow(color: Colors.black, offset: Offset(.5, .5), blurRadius: 2) - ], - ), - ), - ); - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/GirdView/node2_direction.dart b/packages/widgets/lib/StatelessWidget/GirdView/node2_direction.dart deleted file mode 100644 index 51f723380..000000000 --- a/packages/widgets/lib/StatelessWidget/GirdView/node2_direction.dart +++ /dev/null @@ -1,55 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-27 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 163, -// "name": 'GridView滑动方向', -// "priority": 2, -// "subtitle": -// "【scrollDirection】 : 滑动方向 【Axis】\n" -// "【reverse】 : 是否反向滑动 【bool】\n" -// "【shrinkWrap】 : 无边界时是否包裹 【bool】", -// } -class HorizontalGridView extends StatelessWidget { - HorizontalGridView({Key? key}) : super(key: key); - - final List data = List.generate(128, (i) => Color(0xFF00FFFF - 2 * i)); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 200, - child: GridView.count( - scrollDirection: Axis.horizontal, - reverse: true, - crossAxisCount: 4, - mainAxisSpacing: 2, - crossAxisSpacing: 2, - childAspectRatio: 0.618, - children: data.map((color) => _buildItem(color)).toList(), - ), - ); - } - - Container _buildItem(Color color) => Container( - alignment: Alignment.center, - width: 100, - height: 30, - color: color, - child: Text( - colorString(color), - style: const TextStyle( - color: Colors.white, - shadows: [ - Shadow(color: Colors.black, offset: Offset(.5, .5), blurRadius: 2) - ], - ), - ), - ); - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/GirdView/node3_extend.dart b/packages/widgets/lib/StatelessWidget/GirdView/node3_extend.dart deleted file mode 100644 index a7da1abce..000000000 --- a/packages/widgets/lib/StatelessWidget/GirdView/node3_extend.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-27 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 163, -// "name": 'GridView.extent构造', -// "priority": 3, -// "subtitle": -// "【maxCrossAxisExtent】 : box轴向长度 【double】\n" -// "【reverse】 : 是否反向滑动 【bool】\n" -// "【shrinkWrap】 : 无边界时是否包裹 【bool】", -// } -class ExtentGridView extends StatelessWidget { - ExtentGridView({Key? key}) : super(key: key); - - final List data = List.generate(128, (i) => Color(0xFF00FFFF - 2 * i)); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 200, - child: GridView.extent( - scrollDirection: Axis.horizontal, - maxCrossAxisExtent: 80.0, - mainAxisSpacing: 2, - crossAxisSpacing: 2, - childAspectRatio: 0.618, - children: data.map((color) => _buildItem(color)).toList(), - ), - ); - } - - Container _buildItem(Color color) => Container( - alignment: Alignment.center, - width: 100, - height: 30, - color: color, - child: Text( - colorString(color), - style: const TextStyle( - color: Colors.white, - shadows: [ - Shadow(color: Colors.black, offset: Offset(.5, .5), blurRadius: 2) - ], - ), - ), - ); - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/GirdView/node4_builder.dart b/packages/widgets/lib/StatelessWidget/GirdView/node4_builder.dart deleted file mode 100644 index a60215cf5..000000000 --- a/packages/widgets/lib/StatelessWidget/GirdView/node4_builder.dart +++ /dev/null @@ -1,56 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-27 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 163, -// "name": 'GridView.builder构造', -// "priority": 4, -// "subtitle": -// "【itemCount】 : 条目数量 【int】\n" -// "【gridDelegate】 : 网格代理 【SliverGridDelegate】\n" -// "【itemBuilder】 : 条目构造器 【IndexedWidgetBuilder】", -// } -class BuilderGridView extends StatelessWidget { - BuilderGridView({Key? key}) : super(key: key); - - final List data = List.generate(128, (i) => Color(0xFF33FFF - 2 * i)); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 200, - child: GridView.builder( - itemCount: data.length, - scrollDirection: Axis.vertical, - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - //网格代理:定交叉轴数目 - crossAxisCount: 4, //条目个数 - mainAxisSpacing: 5, //主轴间距 - crossAxisSpacing: 5, //交叉轴间距 - childAspectRatio: 1 / 0.618), - itemBuilder: (_, int position) => _buildItem(data[position])), - ); - } - - Widget _buildItem(Color color) => Container( - alignment: Alignment.center, - width: 100, - height: 30, - color: color, - child: Text( - colorString(color), - style: const TextStyle( - color: Colors.white, - shadows: [ - Shadow(color: Colors.black, offset: Offset(.5, .5), blurRadius: 2) - ], - ), - ), - ); - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/GridPager/node1_base.dart b/packages/widgets/lib/StatelessWidget/GridPager/node1_base.dart deleted file mode 100644 index 129551977..000000000 --- a/packages/widgets/lib/StatelessWidget/GridPager/node1_base.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/27 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 37, -// "priority": 1, -// "name": "GridPager 基础属性", -// "subtitle": "【child】: 子组件 【Widget】\n" -// "【color】: 颜色 【Color】\n" -// "【interval】: 小块边长 【double】", -// } -class CustomGridPage extends StatelessWidget { - const CustomGridPage({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return SizedBox( - width: 200, - height: 100, - child: GridPaper( - color: Colors.red, - interval: 50, - child: Image.asset( - "assets/images/wy_300x200.webp", - fit: BoxFit.cover, - ))); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/GridPager/node2_divisions.dart b/packages/widgets/lib/StatelessWidget/GridPager/node2_divisions.dart deleted file mode 100644 index 82d4eadac..000000000 --- a/packages/widgets/lib/StatelessWidget/GridPager/node2_divisions.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/27 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 37, -// "priority": 2, -// "name": "GridPager 再分割", -// "subtitle": "【child】: 子组件 【Widget】\n" -// "【color】: 颜色 【Color】\n" -// "【subdivisions】: 小块中子块个数 【int】\n" -// "【divisions】: 小块中子块的分割数 【int】", -// } -class DivisionsGridPage extends StatelessWidget { - const DivisionsGridPage({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return SizedBox( - width: 200, - height: 100, - child: GridPaper( - color: Colors.red, - interval: 50, - divisions: 4, - subdivisions: 4, - child: Image.asset( - "assets/images/wy_300x200.webp", - fit: BoxFit.cover, - ))); - } -} diff --git a/packages/widgets/lib/StatelessWidget/GridTile/node1_base.dart b/packages/widgets/lib/StatelessWidget/GridTile/node1_base.dart deleted file mode 100644 index 80d58f8ad..000000000 --- a/packages/widgets/lib/StatelessWidget/GridTile/node1_base.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020/4/27 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 21, -// "priority": 1, -// "name": "GridTile的基本表现如下", -// "subtitle": "【header】: 头组件 【Widget】\n" -// "【child】: 子组件 【Widget】\n" -// "【footer】: 脚组件 【Widget】", -// } - -class CustomGridTile extends StatelessWidget { - const CustomGridTile({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return SizedBox( - width: 200, - height: 200, - child: GridTile( - header: GridTileBar( - backgroundColor: Colors.blue.withAlpha(120), - trailing: const Icon(Icons.star, color: Colors.red), - leading: const CircleAvatar( - backgroundImage: AssetImage("assets/images/wy_200x300.webp"), - ), - title: const Text("百里·巫缨"), - subtitle: const Text("倾国必倾城"), - ), - child: Opacity( - opacity: 0.5, - child: Image.asset("assets/images/sabar.webp", fit: BoxFit.cover), - ), - footer: const Padding( - padding: EdgeInsets.all(8.0), - child: Text( - "ID:z\$ySX32&29", - style: TextStyle( - shadows: [ - Shadow( - color: Colors.blue, offset: Offset(.1, .1), blurRadius: 2), - ], - ), - ), - ), - ), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/GridTileBar/node1_base.dart b/packages/widgets/lib/StatelessWidget/GridTileBar/node1_base.dart deleted file mode 100644 index 62c394dd2..000000000 --- a/packages/widgets/lib/StatelessWidget/GridTileBar/node1_base.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/27 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 20, -// "priority": 1, -// "name": "GridTileBar的基本表现如下", -// "subtitle": "【leading】: 左侧组件 【Widget】\n" -// "【trailing】: 尾组件 【Widget】\n" -// "【title】: 中间上组件 【Widget】\n" -// "【subtitle】: 中间下组件 【Widget】\n" -// "【backgroundColor】: 背景色 【Color】", -// } - - -class CustomGridTileBar extends StatelessWidget { - const CustomGridTileBar({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return GridTileBar( - backgroundColor: Colors.blue.withAlpha(120), - trailing: const Icon(Icons.star, color: Colors.red), - leading: const CircleAvatar( - backgroundImage: AssetImage("assets/images/wy_200x300.webp"), - ), - title: const Text("百里·巫缨"), - subtitle: const Text("倾国必倾城"), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/HtmlElementView/node1_base.dart b/packages/widgets/lib/StatelessWidget/HtmlElementView/node1_base.dart deleted file mode 100644 index a3960a629..000000000 --- a/packages/widgets/lib/StatelessWidget/HtmlElementView/node1_base.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/11 -/// contact me by email 1981462002@qq.com -/// -/// 说明: 213 HtmlElementView 0 在 Flutter Web 的 Widget 层次结构中嵌入一个 HTML 元素。 -// { -// "widgetId": 213, -// "name": 'HtmlElementView 介绍', -// "priority": 1, -// "subtitle": -// "【child】 : 子组件 【child】\n" -// "【viewType】 : html元素唯一表识 【String】", -// } -class HtmlElementViewDemo extends StatelessWidget { - const HtmlElementViewDemo({Key? key}) : super(key: key); - - final String info = - '该组件只能用于 Flutter Web 中,嵌入 Html 元素的较为昂贵。' - '内部基于 PlatformViewLink 和 PlatformViewSurface 组件实现。'; - - - @override - Widget build(BuildContext context) { - return Container( - color: Colors.blue.withOpacity(0.1), - padding: const EdgeInsets.all(10), - margin: const EdgeInsets.all(10), - child: Text(info), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/Icon/node1_base.dart b/packages/widgets/lib/StatelessWidget/Icon/node1_base.dart deleted file mode 100644 index 522ecbf77..000000000 --- a/packages/widgets/lib/StatelessWidget/Icon/node1_base.dart +++ /dev/null @@ -1,32 +0,0 @@ - - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/27 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 6, -// "priority": 1, -// "name": "用于显示一个图标", -// "subtitle": "【入参】 :图标数据 【IconData】\n" -// "【size】 : 大小 【double】\n" -// "【color】: 颜色 【Color】", -// } - - -class CustomIcon extends StatelessWidget { - const CustomIcon({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Wrap( - crossAxisAlignment: WrapCrossAlignment.center, - children: const [ - Icon(Icons.send, color: Colors.orange, size: 60), - Icon(Icons.android, color: Colors.green, size: 100), - ], - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/Icon/node2_diy.dart b/packages/widgets/lib/StatelessWidget/Icon/node2_diy.dart deleted file mode 100644 index 30d3bf963..000000000 --- a/packages/widgets/lib/StatelessWidget/Icon/node2_diy.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:app/app.dart'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/27 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 6, -// "priority": 2, -// "name": "使用自定义图标", -// "subtitle": "可在iconfont网站中下载图标字体进行使用", -// } - - -class MyIcon extends StatelessWidget { - const MyIcon({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 20, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - TolyIcon.icon_search, - TolyIcon.icon_star, - TolyIcon.icon_layout, - TolyIcon.icon_star_ok - ].map((e) => Icon( - e, - color: Colors.green, - size: 60, - ),).toList(), - ); - } -} - diff --git a/packages/widgets/lib/StatelessWidget/IconButton/node1_base.dart b/packages/widgets/lib/StatelessWidget/IconButton/node1_base.dart deleted file mode 100644 index 12bdf2e55..000000000 --- a/packages/widgets/lib/StatelessWidget/IconButton/node1_base.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020/4/27 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 30, -// "priority": 1, -// "name": "IconButton属性", -// "subtitle": "【icon】: 图标组件 【Widget】\n" -// "【tooltip】: 长按提示文字 【String】\n" -// "【highlightColor】: 长按高亮色 【Color】\n" -// "【splashColor】: 水波纹色 【Color】\n" -// "【onPressed】: 点击事件 【Function】", -// } - -class CustomIconButton extends StatelessWidget { - const CustomIconButton({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(8.0), - child: IconButton( - padding: const EdgeInsets.only(), - onPressed: () {}, - icon: const Icon(Icons.android, size: 40, color: Colors.green), - tooltip: "android", - highlightColor: Colors.orangeAccent, - splashColor: Colors.blue, - ), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/ImageIcon/node1_base.dart b/packages/widgets/lib/StatelessWidget/ImageIcon/node1_base.dart deleted file mode 100644 index 36f24f6af..000000000 --- a/packages/widgets/lib/StatelessWidget/ImageIcon/node1_base.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/27 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 7, -// "priority": 1, -// "name": "用于显示一个纯色图片", -// "subtitle": "【入参】 : 图片资源 【ImageProvider】\n" -// "【size】 : 大小 【double】\n" -// "【color】: 角标颜色 【Color】", -// } - -class CustomImageIcon extends StatelessWidget { - const CustomImageIcon({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - Map data = { - Colors.blue: 50.0, - Colors.red: 60.0, - Colors.green: 70.0, - Colors.yellow: 80.0, - }; - return Wrap( - spacing: 10, - children: data.keys - .map((e) => ImageIcon( - const AssetImage("assets/images/leaf.webp"), - color: e, - size: data[e], - )) - .toList(), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/InputChip/node1_base.dart b/packages/widgets/lib/StatelessWidget/InputChip/node1_base.dart deleted file mode 100644 index 441d9bc3e..000000000 --- a/packages/widgets/lib/StatelessWidget/InputChip/node1_base.dart +++ /dev/null @@ -1,42 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/27 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 14, -// "priority": 1, -// "name": "可以接受点击、删除事件", -// "subtitle": "【onPressed】: 点击事件 【Function()】\n" -// "【onDeleted】: 删除事件 【Function()】", -// } - -class PressInputChip extends StatefulWidget { - const PressInputChip({Key? key}) : super(key: key); - - @override - _PressInputChipState createState() => _PressInputChipState(); -} - -class _PressInputChipState extends State { - bool _delete = false; - - @override - Widget build(BuildContext context) { - return InputChip( - padding: const EdgeInsets.all(5), - labelPadding: const EdgeInsets.all(3), - label: Text( - !_delete ? - "This is a InputChip." : - "You are clicked delete icon."), - backgroundColor: Colors.grey.withAlpha(66), - avatar: Image.asset("assets/images/icon_head.webp"), - selectedColor: Colors.orangeAccent.withAlpha(88), - selectedShadowColor: Colors.blue, - shadowColor: Colors.orangeAccent, - elevation: 3, - onPressed: () => Navigator.of(context).pushNamed('AboutMePage'), - onDeleted: () => setState(() => _delete = !_delete)); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/InputChip/node2_select.dart b/packages/widgets/lib/StatelessWidget/InputChip/node2_select.dart deleted file mode 100644 index 4b659cf1c..000000000 --- a/packages/widgets/lib/StatelessWidget/InputChip/node2_select.dart +++ /dev/null @@ -1,48 +0,0 @@ -import 'package:flutter/material.dart'; - - - -/// create by 张风捷特烈 on 2020/4/27 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 14, -// "priority": 2, -// "name": "可以接受选中事件", -// "subtitle": "【selected】: 是否选中 【bool】\n" -// "【onSelected】: 选中事件 【Function(bool)】", -// } - -class SelectInputChip extends StatefulWidget { - const SelectInputChip({Key? key}) : super(key: key); - - @override - _SelectInputChipState createState() => _SelectInputChipState(); -} - -class _SelectInputChipState extends State { - bool _select = false; - - @override - Widget build(BuildContext context) { - return InputChip( - selected: _select, - padding: const EdgeInsets.all(5), - labelPadding: const EdgeInsets.all(3), - label: const Text("This is a InputChip."), - backgroundColor: Colors.grey.withAlpha(66), - avatar: Image.asset("assets/images/icon_head.webp"), - selectedColor: Colors.orangeAccent.withAlpha(88), - selectedShadowColor: Colors.blue, - shadowColor: Colors.orangeAccent, - elevation: 3, - onDeleted: () => Navigator.of(context).pushNamed('AboutMePage'), - onSelected: (bool value) { - setState(() { - _select = value; - }); - print("onSelected"); - }, - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/ListTile/node1_base.dart b/packages/widgets/lib/StatelessWidget/ListTile/node1_base.dart deleted file mode 100644 index 4ee09902a..000000000 --- a/packages/widgets/lib/StatelessWidget/ListTile/node1_base.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 16, -// "priority": 1, -// "name": "ListTile的基本表现如下", -// "subtitle": "【leading】: 左侧组件 【Widget】\n" -// "【title】: 中间上组件 【Widget】\n" -// "【subtitle】: 中间下组件 【Widget】\n" -// "【trailing】: 尾组件 【Widget】\n" -// "【contentPadding】: 内边距 【EdgeInsetsGeometry】\n" -// "【onLongPress】: 点击事件 【Function()】", -// } -class CustomListTile extends StatelessWidget { - const CustomListTile({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - margin: const EdgeInsets.all(10), - color: Colors.grey.withAlpha(22), - child: ListTile( - leading: Image.asset("assets/images/icon_head.webp"), - title: const Text("以梦为马"), - subtitle: const Text("海子"), - contentPadding: const EdgeInsets.all(5), - trailing: const Icon(Icons.more_vert), - onLongPress: () => Navigator.of(context).pushNamed('AboutMePage'), - ), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/ListTile/node2_select.dart b/packages/widgets/lib/StatelessWidget/ListTile/node2_select.dart deleted file mode 100644 index b45d37c29..000000000 --- a/packages/widgets/lib/StatelessWidget/ListTile/node2_select.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 16, -// "priority": 2, -// "name": "ListTile选中效果和长按事件", -// "subtitle": "【selected】: 是否选中 【bool】\n" -// "【onTap】: 点击事件 【Function()】", -// } -class SelectListTile extends StatefulWidget { - const SelectListTile({Key? key}) : super(key: key); - - @override - _SelectListTileState createState() => _SelectListTileState(); -} - -class _SelectListTileState extends State { - bool _selected = false; - - @override - Widget build(BuildContext context) { - return Container( - margin: const EdgeInsets.all(10), - color: Colors.grey.withAlpha(22), - child: ListTile( - leading: Image.asset("assets/images/icon_head.webp"), - selected: _selected, - title: const Text("以梦为马"), - subtitle: const Text("海子"), - contentPadding: const EdgeInsets.all(5), - trailing: const Icon(Icons.more_vert), - onTap: () => setState(() => _selected = !_selected), - ), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/ListTile/node3_dense.dart b/packages/widgets/lib/StatelessWidget/ListTile/node3_dense.dart deleted file mode 100644 index a176f586d..000000000 --- a/packages/widgets/lib/StatelessWidget/ListTile/node3_dense.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 16, -// "priority": 3, -// "name": "ListTile的密排属性", -// "subtitle": "【dense】: 是否密排 【bool】", -// } -class DenseListTile extends StatefulWidget { - const DenseListTile({Key? key}) : super(key: key); - - @override - _DenseListTileState createState() => _DenseListTileState(); -} - -class _DenseListTileState extends State { - bool _dense = false; - - @override - Widget build(BuildContext context) { - return Container( - margin: const EdgeInsets.all(10), - color: Colors.grey.withAlpha(22), - child: ListTile( - leading: Image.asset("assets/images/icon_head.webp"), - title: const Text("以梦为马"), - subtitle: const Text("海子"), - selected: false, - contentPadding: const EdgeInsets.all(5), - trailing: const Icon(Icons.more_vert), - dense: _dense, - onTap: () => setState(() => _dense = !_dense), - ), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/ListView/node1_base.dart b/packages/widgets/lib/StatelessWidget/ListView/node1_base.dart deleted file mode 100644 index 45afd5a62..000000000 --- a/packages/widgets/lib/StatelessWidget/ListView/node1_base.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-27 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 162, -// "name": 'ListView基本使用', -// "priority": 1, -// "subtitle": -// "【children】 : 子组件列表 【List】\n" -// "【padding】 : 内边距 【EdgeInsetsGeometry】", -// } -class CustomListView extends StatelessWidget { - CustomListView({Key? key}) : super(key: key); - - final List data = [ - Colors.purple[50]!, - Colors.purple[100]!, - Colors.purple[200]!, - Colors.purple[300]!, - Colors.purple[400]!, - Colors.purple[500]!, - Colors.purple[600]!, - Colors.purple[700]!, - Colors.purple[800]!, - Colors.purple[900]!, - ]; - - TextStyle get textStyle => const TextStyle( - color: Colors.white, - shadows: [ - Shadow(color: Colors.black, offset: Offset(.5, .5), blurRadius: 2) - ], - ); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 200, - child: ListView( - padding: const EdgeInsets.symmetric(horizontal: 5), - children: data - .map((color) => Container( - alignment: Alignment.center, - width: 100, - height: 50, - color: color, - child: Text( - colorString(color), - style: textStyle, - ), - )) - .toList(), - ), - ); - } - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/ListView/node2_direction.dart b/packages/widgets/lib/StatelessWidget/ListView/node2_direction.dart deleted file mode 100644 index 081ded31c..000000000 --- a/packages/widgets/lib/StatelessWidget/ListView/node2_direction.dart +++ /dev/null @@ -1,65 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/27 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 162, -// "name": 'ListView横向滑动', -// "priority": 2, -// "subtitle": -// "【scrollDirection】 : 滑动方向 【Axis】\n" -// "【reverse】 : 是否反向滑动 【bool】\n" -// "【shrinkWrap】 : 无边界时是否包裹 【bool】", -// } -class HorizontalListView extends StatelessWidget { - HorizontalListView({Key? key}) : super(key: key); - - final List data = [ - Colors.purple[50]!, - Colors.purple[100]!, - Colors.purple[200]!, - Colors.purple[300]!, - Colors.purple[400]!, - Colors.purple[500]!, - Colors.purple[600]!, - Colors.purple[700]!, - Colors.purple[800]!, - Colors.purple[900]!, - ]; - - TextStyle get textStyle => const TextStyle( - color: Colors.white, - shadows: [ - Shadow(color: Colors.black, offset: Offset(.5, .5), blurRadius: 2) - ], - ); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 200, - child: ListView( - reverse: true, - shrinkWrap: true, - scrollDirection: Axis.horizontal, - children: data - .map((color) => Container( - alignment: Alignment.center, - width: 100, - height: 50, - color: color, - child: Text( - colorString(color), - style: textStyle, - ), - )) - .toList(), - ), - ); - } - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/ListView/node3_builder.dart b/packages/widgets/lib/StatelessWidget/ListView/node3_builder.dart deleted file mode 100644 index d951c4d8a..000000000 --- a/packages/widgets/lib/StatelessWidget/ListView/node3_builder.dart +++ /dev/null @@ -1,62 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/27 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 162, -// "name": 'ListView.builder构造', -// "priority": 3, -// "subtitle": -// "【itemCount】 : 条目个数 【int】\n" -// "【itemBuilder】 : 条目构造器 【IndexedWidgetBuilder】", -// } -class BuilderListView extends StatelessWidget { - BuilderListView({Key? key}) : super(key: key); - - final List data = [ - Colors.purple[50]!, - Colors.purple[100]!, - Colors.purple[200]!, - Colors.purple[300]!, - Colors.purple[400]!, - Colors.purple[500]!, - Colors.purple[600]!, - Colors.purple[700]!, - Colors.purple[800]!, - Colors.purple[900]!, - ]; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 200, - child: ListView.builder( - itemCount: data.length, - itemBuilder: (context, index) => _buildItem(data[index]), - ), - ); - } - - TextStyle get textStyle => const TextStyle( - color: Colors.white, - shadows: [ - Shadow(color: Colors.black, offset: Offset(.5, .5), blurRadius: 2) - ], - ); - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; - - Widget _buildItem(Color color) => Container( - alignment: Alignment.center, - width: 100, - height: 50, - color: color, - child: Text( - colorString(color), - style: textStyle, - ), - ); -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/ListView/node4_separated.dart b/packages/widgets/lib/StatelessWidget/ListView/node4_separated.dart deleted file mode 100644 index 4105d1082..000000000 --- a/packages/widgets/lib/StatelessWidget/ListView/node4_separated.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/27 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 162, -// "name": 'ListView.separated构造', -// "priority": 3, -// "subtitle": -// "【separatorBuilder】 : 条目构造器 【IndexedWidgetBuilder】", -// } -class SeparatedListView extends StatelessWidget { - SeparatedListView({Key? key}) : super(key: key); - - final List data = [ - Colors.purple[50]!, - Colors.purple[100]!, - Colors.purple[200]!, - Colors.purple[300]!, - Colors.purple[400]!, - Colors.purple[500]!, - Colors.purple[600]!, - Colors.purple[700]!, - Colors.purple[800]!, - Colors.purple[900]!, - ]; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 200, - child: ListView.separated( - separatorBuilder: (context, index) => const Divider( - thickness: 1, - height: 1, - color: Colors.orange, - ), - itemCount: data.length, - itemBuilder: (context, index) => _buildItem(data[index]), - ), - ); - } - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; - - TextStyle get textStyle => const TextStyle( - color: Colors.white, - shadows: [ - Shadow(color: Colors.black, offset: Offset(.5, .5), blurRadius: 2) - ], - ); - - Widget _buildItem(Color color) => Container( - alignment: Alignment.center, - width: 100, - height: 50, - color: color, - child: Text( - colorString(color), - style: textStyle, - ), - ); -} diff --git a/packages/widgets/lib/StatelessWidget/Listener/node1_base.dart b/packages/widgets/lib/StatelessWidget/Listener/node1_base.dart deleted file mode 100644 index 894cc42ec..000000000 --- a/packages/widgets/lib/StatelessWidget/Listener/node1_base.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 147, -// "name": 'Listener基本事件', -// "priority": 1, -// "subtitle": -// "【child】 : 子组件 【Widget】\n" -// "【onPointerDown】 : 按下事件 【Function(PointerDownEvent)】\n" -// "【onPointerMove】 : 移动事件 【Function(PointerMoveEvent)】\n" -// "【onPointerMove】 : 抬起事件 【Function(PointerUpEvent)】\n" -// "【onPointerCancel】 : 取消事件 【Function(PointerUpEvent)】", -// } - -class CustomListener extends StatefulWidget { - const CustomListener({Key? key}) : super(key: key); - - @override - _CustomListenerState createState() => _CustomListenerState(); -} - -class _CustomListenerState extends State { - String _info = ''; - - @override - Widget build(BuildContext context) { - return Listener( - onPointerDown: (detail) => setState(() => _info = detail.toString()), - onPointerMove: (detail) => setState(() => _info = detail.toString()), - onPointerUp: (detail) => setState(() => _info = detail.toString()), - onPointerCancel: (detail) => setState(() => _info = detail.toString()), - - child: Container( - alignment: Alignment.center, - width: 300, - height: 300 * 0.618, - color: Colors.grey.withAlpha(33), - child: Text( - _info, - style: const TextStyle(fontSize: 16, color: Colors.blue), - ), - ), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/MaterialBanner/node1_one_btn.dart b/packages/widgets/lib/StatelessWidget/MaterialBanner/node1_one_btn.dart deleted file mode 100644 index 236ac64fe..000000000 --- a/packages/widgets/lib/StatelessWidget/MaterialBanner/node1_one_btn.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/7/21 -/// contact me by email 1981462002@qq.com -/// 说明: 211 MaterialBanner Material风格的横幅组件,支持左中右或左中下结构,可指定边距背景色等。 - -// { -// "widgetId": 211, -// "name": 'MaterialBanner一行的使用', -// "priority": 1, -// "subtitle": "【content】 : 中间组件 【Widget】\n" -// "【leading】: 左侧组件 【Widget】\n" -// "【actions】: 右侧组件列表 【List】\n" -// "【padding】: 内边距 【EdgeInsetsGeometry】\n" -// "【forceActionsBelow】: 是否按钮在下方 【bool】\n" -// "【backgroundColor】: 背景色 【Color】", -// } -class MaterialBannerDemo extends StatelessWidget { - const MaterialBannerDemo({Key? key}) : super(key: key); - - final String info = 'Welcome to Flutter Unit!'; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - MaterialBanner( - content: Text( - info, - style: const TextStyle(color: Colors.white), - ), - backgroundColor: Colors.purple, - leading: const Icon(Icons.info, color: Colors.lightBlueAccent), - padding: const EdgeInsetsDirectional.only(start: 16.0, top: 2.0), - forceActionsBelow: false, - // 默认false - actions: const [ - Text( - 'I KNOW', - style: TextStyle( - color: Colors.orange, - fontWeight: FontWeight.bold, - fontSize: 14), - ) - ], - )], - ); - } - -} diff --git a/packages/widgets/lib/StatelessWidget/MaterialBanner/node2_two_btn.dart b/packages/widgets/lib/StatelessWidget/MaterialBanner/node2_two_btn.dart deleted file mode 100644 index 7f3c18e73..000000000 --- a/packages/widgets/lib/StatelessWidget/MaterialBanner/node2_two_btn.dart +++ /dev/null @@ -1,67 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/7/21 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 211, -// "name": 'MaterialBanner两行的使用', -// "priority": 2, -// "subtitle": "【contentTextStyle】: 中间位置样式 【TextStyle】\n" -// "【leadingPadding】: 左侧组件边距 【EdgeInsetsGeometry】\n" -// "当尾部组件数量大于1,该组件结构为左中下。", -// } -class MaterialBannerDemoTwo extends StatelessWidget { - const MaterialBannerDemoTwo({Key? key}) : super(key: key); - - final String info = - 'A banner displays an important, succinct message, and provides actions for users to address. ' - 'A user action is required for itto be dismissed.'; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - MaterialBanner( - content: Text( - info, - style: const TextStyle(color: Colors.white), - ), - backgroundColor: Colors.purple, - leading: const Icon(Icons.warning, color: Colors.yellow), - padding: - const EdgeInsetsDirectional.only(start: 16.0, top: 2.0, end: 2), - leadingPadding: const EdgeInsetsDirectional.only(end: 16.0), - actions: [ - ElevatedButton( - style: TextButton.styleFrom( - backgroundColor: Colors.white, - ), - onPressed: () {}, - child: const Text( - 'I KNOW', - style: TextStyle( - color: Colors.purple, - fontWeight: FontWeight.bold, - fontSize: 14), - ), - ), - ElevatedButton( - style: TextButton.styleFrom( - backgroundColor: Colors.white, - ), - onPressed: () {}, - child: const Text( - 'I IGNORE', - style: TextStyle( - color: Colors.purple, - fontWeight: FontWeight.bold, - fontSize: 14), - ), - ), - ], - )], - ); - } - -} diff --git a/packages/widgets/lib/StatelessWidget/MaterialButton/node1_base.dart b/packages/widgets/lib/StatelessWidget/MaterialButton/node1_base.dart deleted file mode 100644 index ad46dc973..000000000 --- a/packages/widgets/lib/StatelessWidget/MaterialButton/node1_base.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 23, -// "priority": 1, -// "name": "MaterialButton点击事件", -// "subtitle": "【color】: 颜色 【Color】\n" -// "【splashColor】: 水波纹颜色 【Color】\n" -// "【height】: 高 【double】\n" -// "【elevation】: 影深 【double】\n" -// "【child】: 子组件 【Widget】\n" -// "【textColor】: 子组件文字颜色 【Color】\n" -// "【highlightColor】: 长按高亮色 【Color】\n" -// "【padding】: 内边距 【EdgeInsetsGeometry】\n" -// "【onPressed】: 点击事件 【Function】", -// } - -class CustomMaterialButton extends StatelessWidget { - const CustomMaterialButton({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return MaterialButton( - height: 40, - elevation: 5, - color: Colors.orangeAccent, - textColor: Colors.white, - splashColor: Colors.blue, - padding: const EdgeInsets.all(8), - child: const Text("MaterialButton"), - onPressed: () => Navigator.of(context).pushNamed('AboutMePage')); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/MaterialButton/node2_onLongPress.dart b/packages/widgets/lib/StatelessWidget/MaterialButton/node2_onLongPress.dart deleted file mode 100644 index fba95a72d..000000000 --- a/packages/widgets/lib/StatelessWidget/MaterialButton/node2_onLongPress.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 23, -// "priority": 2, -// "name": "MaterialButton长按事件", -// "subtitle": "【highlightColor】: 长按高亮色 【Color】\n" -// "【onLongPress】: 长按事件 【Function】", -// } - -class LongPressMaterialButton extends StatelessWidget { - const LongPressMaterialButton({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return MaterialButton( - height: 40, - elevation: 5, - color: Colors.blue, - highlightColor: Colors.green, - textColor: Colors.white, - padding: const EdgeInsets.all(8), - child: const Text("MaterialButton"), - onLongPress: () => Navigator.of(context).pushNamed('AboutMePage'), - onPressed: () => Navigator.of(context).pushNamed('AboutMePage')); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/MaterialButton/node3_shape.dart b/packages/widgets/lib/StatelessWidget/MaterialButton/node3_shape.dart deleted file mode 100644 index 9ab3246ce..000000000 --- a/packages/widgets/lib/StatelessWidget/MaterialButton/node3_shape.dart +++ /dev/null @@ -1,64 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 23, -// "priority": 3, -// "name": "MaterialButton的自定义形状", -// "subtitle": "【shape】: 形状 【ShapeBorder】", -// } - -class ShapeMaterialButton extends StatelessWidget { - const ShapeMaterialButton({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 20, - children: [ - SizedBox( - width: 40, - height: 40, - child: MaterialButton( - padding: const EdgeInsets.all(0), - textColor: const Color(0xffFfffff), - elevation: 3, - color: Colors.blue, - highlightColor: const Color(0xffF88B0A), - splashColor: Colors.red, - child: const Icon( - Icons.add, - color: Colors.white, - ), - shape: const CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFDFDFDF)), - ), - onLongPress: () => Navigator.of(context).pushNamed('AboutMePage'), - onPressed: () => Navigator.of(context).pushNamed('AboutMePage')), - ), - SizedBox( - width: 100, - height: 40, - child: MaterialButton( - padding: const EdgeInsets.all(0), - textColor: const Color(0xffFfffff), - elevation: 3, - color: Colors.blue, - highlightColor: const Color(0xffF88B0A), - splashColor: Colors.red, - child: const Icon( - Icons.remove, - color: Colors.white, - ), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(15))), - onLongPress: () => Navigator.of(context).pushNamed('AboutMePage'), - onPressed: () => Navigator.of(context).pushNamed('AboutMePage')), - ), - ], - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/ModalBarrier/node1_base.dart b/packages/widgets/lib/StatelessWidget/ModalBarrier/node1_base.dart deleted file mode 100644 index bdabc296b..000000000 --- a/packages/widgets/lib/StatelessWidget/ModalBarrier/node1_base.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-04-01 -/// contact me by email 1981462002@qq.com -/// 说明: 212 ModalBarrier 屏障模 -/// 相当于一块幕布,防止用户与其背后的 Widget 交互,可以通过 dismissible 决定点击时,是否触发返回栈。源码中用于 Dialog 相关组件。 -/// link: 227,126,127,128 -// { -// "widgetId": 212, -// "name": 'ModalBarrier 介绍', -// "priority": 1, -// "subtitle": -// "【dismissible】 : 点击是否返回 【bool】\n" -// "【color】 : 颜色 【Color】", -// } -class ModalBarrierDemo extends StatelessWidget { - const ModalBarrierDemo({Key? key}) : super(key: key); - - - @override - Widget build(BuildContext context) { - return SizedBox( - width: 200, - height: 100, - child: Stack(alignment: Alignment.center, children: [ - ModalBarrier( - dismissible: true, - color: Colors.grey.withOpacity(0.3), - ), - const Text('点击背景返回') - ]), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/NavigationToolbar/node1_base.dart b/packages/widgets/lib/StatelessWidget/NavigationToolbar/node1_base.dart deleted file mode 100644 index 7789c819d..000000000 --- a/packages/widgets/lib/StatelessWidget/NavigationToolbar/node1_base.dart +++ /dev/null @@ -1,47 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/7/20 -/// contact me by email 1981462002@qq.com -/// 说明: NavigationToolbar 214 左中右模式的通用结构组件,可指定中间组件距左侧边距及是否居中。源码在AppBar等导航条结构中有使用它。 -// { -// "widgetId": 214, -// "name": 'NavigationToolbar基本使用', -// "priority": 1, -// "subtitle": "【leading】 : 左侧组件 【Widget】\n" -// "【middle】: 中间组件 【Widget】\n" -// "【trailing】: 右侧组件组件 【Widget】\n" -// "【centerMiddle】: 中间组件是否居中 【bool】\n" -// "【middleSpacing】: 中间组件距左距离 【double】", -// } -class NavigationToolbarDemo extends StatelessWidget { - const NavigationToolbarDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - children: const [ - SizedBox( - height: 60, - child: NavigationToolbar( - leading: Icon(Icons.ac_unit), - middle: Text('middleSpacing#true'), - middleSpacing: 20, - centerMiddle: true, - trailing: Icon(Icons.more_vert), - ), - ), - SizedBox( - height: 60, - child: NavigationToolbar( - leading: Icon(Icons.ac_unit), - middle: Text('middleSpacing#false'), - middleSpacing: 20, - centerMiddle: false, - trailing: Icon(Icons.more_vert), - ), - ), - ], - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/NotificationListener/node1_base.dart b/packages/widgets/lib/StatelessWidget/NotificationListener/node1_base.dart deleted file mode 100644 index 4231b9961..000000000 --- a/packages/widgets/lib/StatelessWidget/NotificationListener/node1_base.dart +++ /dev/null @@ -1,67 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/8/14 -/// contact me by email 1981462002@qq.com -/// 说明: NotificationListener 220 0 通知监听器 可指定Notification子泛型T,监听该类型的变化。Flutter内置很多滑动的Notification,当然你也可以自定义Notification进行监听。 -// { -// "widgetId": 220, -// "name": "监听OverscrollIndicatorNotification", -// "priority": 1, -// "subtitle": "该通知之后在滑动到最顶和最底是回调,通过leading属性判断是顶部还是底部。另外通过notification#disallowGlow(),可以去除顶底滑动蓝色阴影", -// } - -class NotificationListenerDemo extends StatefulWidget { - const NotificationListenerDemo({Key? key}) : super(key: key); - - @override - _NotificationListenerDemoState createState() => _NotificationListenerDemoState(); -} - -class _NotificationListenerDemoState extends State { - final List data = List.generate(30, (i) => '第${i + 1}条'); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 250, - child: NotificationListener( - onNotification: _onNotification, - child: CupertinoScrollbar( - child: ListView.separated( - itemBuilder: _buildItem, - itemCount: data.length, - separatorBuilder: (_,__)=>const Divider(height: 5,), - ), - )), - ); - } - - bool _onNotification(OverscrollIndicatorNotification notification) { - if (notification.leading) { - notification.disallowIndicator(); - ScaffoldMessenger.of(context).showSnackBar(const SnackBar( - content: Text('已滑到顶部'), - backgroundColor: Colors.blue, - duration: Duration(milliseconds: 200), - )); - } else { - notification.disallowIndicator(); - ScaffoldMessenger.of(context).showSnackBar(const SnackBar( - content: Text('已滑到底部'), - duration: Duration(milliseconds: 200), - backgroundColor: Colors.blue, - )); - } - - return true; - } - - Widget _buildItem(BuildContext context, int index) { - return Container( - height: 50, - alignment: Alignment.center, - child: Text(data[index],style: TextStyle(color: Theme.of(context).primaryColor,fontSize: 18),), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/NotificationListener/node2_update.dart b/packages/widgets/lib/StatelessWidget/NotificationListener/node2_update.dart deleted file mode 100644 index 990f803e7..000000000 --- a/packages/widgets/lib/StatelessWidget/NotificationListener/node2_update.dart +++ /dev/null @@ -1,85 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/8/14 -/// contact me by email 1981462002@qq.com -/// 说明: NotificationListener 210 0 通知监听器 可指定Notification子泛型T,监听该类型的变化。Flutter内置很多滑动的Notification,当然你也可以自定义Notification进行监听。 -// { -// "widgetId": 220, -// "name": "监听ScrollUpdateNotification", -// "priority": 2, -// "subtitle": "在滑动过程中对滑动数据进行回调,你可以获取大量数据来进行操作。", -// } - -class NotificationListenerUpdate extends StatefulWidget { - const NotificationListenerUpdate({Key? key}) : super(key: key); - - @override - _NotificationListenerUpdateState createState() => - _NotificationListenerUpdateState(); -} - -class _NotificationListenerUpdateState - extends State { - final List data = List.generate(30, (i) => '第${i + 1}条'); - - String _info = ''; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 300, - child: Stack( - children: [ - Positioned( - child: Padding( - padding: const EdgeInsets.only(left: 8.0), - child: Text(_info), - )), - NotificationListener( - onNotification: _onNotification, - child: CupertinoScrollbar( - child: ListView.separated( - itemBuilder: _buildItem, - itemCount: data.length, - separatorBuilder: (_, __) => const Divider(height: 5), - ), - )), - - ], - ), - ); - } - - bool _onNotification(ScrollUpdateNotification notification) { - setState(() { - _info = 'axis------【${notification.metrics.axis}】------\n' - 'pixels------【${notification.metrics.pixels}】------\n' - 'atEdge------【${notification.metrics.atEdge}】------\n' - 'axisDirection------【${notification.metrics.axisDirection}】------\n' - 'extentInside------【${notification.metrics.extentInside}】------\n' - 'outOfRange------【${notification.metrics.outOfRange}】------\n' - 'minScrollExtent------【${notification.metrics.minScrollExtent}】------\n' - 'maxScrollExtent------【${notification.metrics.maxScrollExtent}】------\n' - 'viewportDimension------【${notification.metrics.viewportDimension}】------\n' - 'delta------【${notification.dragDetails?.delta}】------\n' - 'globalPosition------【${notification.dragDetails?.globalPosition}】------\n' - 'localPosition------【${notification.dragDetails?.localPosition}】------\n' - 'scrollDelta------【${notification.scrollDelta}】------\n' - 'depth------【${notification.depth}】------'; - }); - return true; - } - - Widget _buildItem(BuildContext context, int index) { - return Container( - height: 50, - alignment: Alignment.centerRight, - padding: const EdgeInsets.only(right: 8), - child: Text( - data[index], - style: TextStyle(color: Theme.of(context).primaryColor, fontSize: 18), - ), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/OrientationBuilder/node1_base.dart b/packages/widgets/lib/StatelessWidget/OrientationBuilder/node1_base.dart deleted file mode 100644 index 22968e882..000000000 --- a/packages/widgets/lib/StatelessWidget/OrientationBuilder/node1_base.dart +++ /dev/null @@ -1,57 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/7/21 -/// contact me by email 1981462002@qq.com -/// 说明: 203 OrientationBuilder 能够回调父组件是横向还是纵向,可以据此来构建不同的子组件。 -// { -// "widgetId": 203, -// "name": 'OrientationBuilder基本使用', -// "priority": 1, -// "subtitle": "【builder】 : 方向组件构造器 【OrientationWidgetBuilder】", -// } -class OrientationBuilderDemo extends StatefulWidget { - const OrientationBuilderDemo({Key? key}) : super(key: key); - - @override - _OrientationBuilderDemoState createState() => _OrientationBuilderDemoState(); -} - -class _OrientationBuilderDemoState extends State { - double _width = 200; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Container( - width: _width, - height: 200, - child: OrientationBuilder(builder: _builder), - color: Colors.orange, - ), - _buildSlider() - ], - ); - } - - //根据回调的orientation返回组件 - Widget _builder(BuildContext context, Orientation orientation) { - switch(orientation){ - case Orientation.portrait: - return const Icon(Icons.phone_android,size: 60,); - case Orientation.landscape: - return const RotatedBox( - quarterTurns: 1, - child: Icon(Icons.phone_android,size: 60,)); - default: return Container(); - } - } - - Widget _buildSlider() =>Slider( - value: _width, - max: 350.0, - min: 80.0, - divisions: 17, - onChanged: (v)=> setState(() => _width= v), - ); -} diff --git a/packages/widgets/lib/StatelessWidget/OutlineButton/node1_base.dart b/packages/widgets/lib/StatelessWidget/OutlineButton/node1_base.dart deleted file mode 100644 index bba68e82a..000000000 --- a/packages/widgets/lib/StatelessWidget/OutlineButton/node1_base.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 27, -// "priority": 1, -// "name": "OutlineButton点击事件", -// "subtitle": "【textColor】: 子组件文字颜色 【Color】\n" -// "【splashColor】: 水波纹颜色 【Color】\n" -// "【highlightColor】: 长按高亮色 【Color】\n" -// "【highlightedBorderColor】: 高亮时框色 【Color】\n" -// "【child】: 子组件 【Widget】\n" -// "【padding】: 内边距 【EdgeInsetsGeometry】\n" -// "【borderSide】: 边线 【BorderSide】\n" -// "【onPressed】: 点击事件 【Function】", -// } - -class CustomOutlineButton extends StatelessWidget { - const CustomOutlineButton({Key? key}) : super(key: key); - - final String info = - 'OutlineButton 按钮于 Flutter3.0 退出历史舞台。取代者为 OutlinedButton 按钮。'; - - @override - Widget build(BuildContext context) { - return Container( - color: Colors.blue.withOpacity(0.1), - padding: const EdgeInsets.all(10), - margin: const EdgeInsets.all(10), - child: Text(info), - ); - } - - // @override - // Widget build(BuildContext context) { - // return OutlineButton(//边线按钮 - // onPressed: () {}, - // child: const Text("OutlineButton"), - // padding: const EdgeInsets.all(8), - // splashColor: Colors.green, - // highlightColor: Colors.orangeAccent, - // highlightedBorderColor: Colors.grey, - // textColor: const Color(0xff000000), - // borderSide: const BorderSide(color: Color(0xff0A66F8), width: 2), - // ); - // } -} diff --git a/packages/widgets/lib/StatelessWidget/PageStorage/node1_base.dart b/packages/widgets/lib/StatelessWidget/PageStorage/node1_base.dart deleted file mode 100644 index 2f62074c2..000000000 --- a/packages/widgets/lib/StatelessWidget/PageStorage/node1_base.dart +++ /dev/null @@ -1,117 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/8/14 -/// contact me by email 1981462002@qq.com -/// 说明: PageStorage 210 0 页面存储器 可以将页面状态进行存储,在切页时可以保持状态。源码中在ScrollView、PageView、ExpansionTile等皆有应用。 -// { -// "widgetId": 210, -// "name": "PageStorage基本使用", -// "priority": 1, -// "subtitle": "【bucket】 : 存储区 【PageStorageBucket】\n" -// "【child】: 子组件 【Widget】\n" -// "上面切换界面初始化组件时并不会将状态重置。如上CountWidget,子组件需要在初始化时从存储器中读取状态,在改变状态时将状态量写入存储器。另外,如果使用MaterialApp已经内置了PageStorage,不过你也可以创建PageStorage。", -// } - -class PageStorageDemo extends StatefulWidget { - const PageStorageDemo({Key? key}) : super(key: key); - - @override - _PageStorageDemoState createState() => _PageStorageDemoState(); -} - -class _PageStorageDemoState extends State { - int _pageIndex = 0; - final PageStorageBucket _bucket = PageStorageBucket(); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 200, - child: Scaffold( - body: PageStorage( - child: _buildContentByIndex(), - bucket: _bucket, - ), - bottomNavigationBar: BottomNavigationBar( - elevation: 0, - backgroundColor: Colors.blueAccent.withAlpha(55), - currentIndex: _pageIndex, - onTap: (int index) { - setState(() { - _pageIndex = index; - }); - }, - items: const [ - BottomNavigationBarItem( - icon: Icon(Icons.home), - label: 'Home', - ), - BottomNavigationBarItem( - icon: Icon(Icons.settings), - label: 'Setting', - ), - ], - ), - ), - ); - } - - Widget _buildContentByIndex() { - if (_pageIndex == 0) { - return const CountWidget(key: PageStorageKey('CountWidget1')); - } - - if (_pageIndex == 1) { - return const CountWidget(key: PageStorageKey('CountWidget2')); - } - - return ListView(); - } -} - -class CountWidget extends StatefulWidget { - const CountWidget({Key? key}) : super(key: key); - - @override - _CountWidgetState createState() => _CountWidgetState(); -} - -class _CountWidgetState extends State { - int _count = 0; - - @override - void initState() { - super.initState(); - _count = PageStorage.of(context)?.readState(context); - } - - @override - Widget build(BuildContext context) { - return Container( - alignment: Alignment.center, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text('点击了$_count次'), - MaterialButton( - onPressed: _addCount, - child: const Icon( - Icons.add, - color: Colors.white, - ), - color: Colors.green, - shape: const CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFDFDFDF)), - )) - ], - ), - ); - } - - void _addCount() { - setState(() { - _count++; - PageStorage.of(context)?.writeState(context, _count); - }); - } -} diff --git a/packages/widgets/lib/StatelessWidget/Placeholder/node1_base.dart b/packages/widgets/lib/StatelessWidget/Placeholder/node1_base.dart deleted file mode 100644 index c7f8f6808..000000000 --- a/packages/widgets/lib/StatelessWidget/Placeholder/node1_base.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 36, -// "priority": 1, -// "name": "Placeholder基础属性", -// "subtitle": "【color】: 颜色 【Color】\n" -// "【strokeWidth】: 线粗 【double】", -// } -class CustomPlaceholder extends StatelessWidget { - const CustomPlaceholder({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return const SizedBox( - width: 100, - height: 100*0.618, - child: Placeholder( - color: Colors.orangeAccent, - strokeWidth: 2, - ), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/Placeholder/node2_fallback.dart b/packages/widgets/lib/StatelessWidget/Placeholder/node2_fallback.dart deleted file mode 100644 index 63030673f..000000000 --- a/packages/widgets/lib/StatelessWidget/Placeholder/node2_fallback.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 36, -// "priority": 2, -// "name": "Placeholder的fallback属性", -// "subtitle": " 当所在区域无宽高约束时,占位组件的宽高。" -// "【fallbackHeight】: 高 【double】\n" -// "【fallbackWidth】: 宽 【double】", -// } -class FallbackPlaceholder extends StatelessWidget { - const FallbackPlaceholder({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return const UnconstrainedBox( - child: Placeholder( - color: Colors.blue, - strokeWidth: 2, - fallbackHeight: 100, - fallbackWidth: 150, - ), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/PositionedDirectional/node1_base.dart b/packages/widgets/lib/StatelessWidget/PositionedDirectional/node1_base.dart deleted file mode 100644 index 73ac81bd2..000000000 --- a/packages/widgets/lib/StatelessWidget/PositionedDirectional/node1_base.dart +++ /dev/null @@ -1,63 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-22 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 159, -// "name": 'PositionedDirectional基本使用', -// "priority": 1, -// "subtitle": "【child】 : 组件 【Widget】\n" -// "【top】 : 到父顶距离 【double】\n" -// "【end】 : 到父右距离 【double】\n" -// "【start】 : 到父左距离 【double】\n" -// "【bottom】 : 到父底距离 【double】", -// } -class CustomPositionedDirectional extends StatelessWidget { - const CustomPositionedDirectional({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - Widget yellowBox = Container( - color: Colors.yellow, - height: 100, - width: 100, - ); - - Widget redBox = Container( - color: Colors.red, - height: 90, - width: 90, - ); - - Widget greenBox = Container( - color: Colors.green, - height: 80, - width: 80, - ); - - Widget cyanBox = Container( - color: Colors.cyanAccent, - height: 70, - width: 70, - ); - - return Container( - width: 200, - height: 120, - color: Colors.grey.withAlpha(33), - child: Stack( - children: [ - yellowBox, - redBox, - PositionedDirectional(top: 20, start: 20, child: greenBox), - PositionedDirectional( - child: cyanBox, - bottom: 10, - end: 10, - ) - ], - )); - } -} diff --git a/packages/widgets/lib/StatelessWidget/PreferredSize/node1_base.dart b/packages/widgets/lib/StatelessWidget/PreferredSize/node1_base.dart deleted file mode 100644 index bad7a8660..000000000 --- a/packages/widgets/lib/StatelessWidget/PreferredSize/node1_base.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/5/3 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 204, -// "name": 'PreferredSize调整AppBar高度', -// "priority": 1, -// "subtitle": "【preferredSize】 : 尺寸 【Size】", -// } -class CustomPreferredSize extends StatelessWidget { - const CustomPreferredSize({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 200, - child: Scaffold( - appBar: PreferredSize( - preferredSize: const Size.fromHeight(150), - child: AppBar( - title: const Text('PreferredSize'), - ), - ), - ), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/PreferredSize/node2_adapter.dart b/packages/widgets/lib/StatelessWidget/PreferredSize/node2_adapter.dart deleted file mode 100644 index 1202bbf9b..000000000 --- a/packages/widgets/lib/StatelessWidget/PreferredSize/node2_adapter.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/5/3 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 204, -// "name": 'PreferredSize的转化使用', -// "priority": 2, -// "subtitle": "【PreferredSize将普通组件转化为PreferredSizeWidget", -// } -class AdapterPreferredSize extends StatelessWidget { - const AdapterPreferredSize({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 200, - child: Scaffold( - appBar: PreferredSize( - preferredSize: const Size.fromHeight(150), - child: AppBar( - title: const Text('PreferredSize'), - bottom: PreferredSize( - preferredSize: const Size.fromHeight(40), - child: Container( - height: 40, - color: Colors.orange, - ), - ), - ), - ), - ), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/RadioListTile/node1_base.dart b/packages/widgets/lib/StatelessWidget/RadioListTile/node1_base.dart deleted file mode 100644 index 1ed6c0a58..000000000 --- a/packages/widgets/lib/StatelessWidget/RadioListTile/node1_base.dart +++ /dev/null @@ -1,73 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-22 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 19, -// "priority": 1, -// "name": "RadioListTile需要一个泛型T", -// "subtitle": "【value】 : 条目对象 【T】\n" -// "【groupValue】 : 选中对象 【T】\n" -// "【selected】: 是否选中 【bool】\n" -// "【secondary】: 右侧组件 【Widget】\n" -// "【title】: 中间上组件 【Widget】\n" -// "【subtitle】: 中间下组件 【Widget】\n" -// "【onChanged】: 切换事件 【Function(T)】", -// } - -enum ItemType { - java, - kotlin, - dart, -} - -class ItemBean { - final String title; - final String subTitle; - final String imgUrl; - - ItemBean(this.title, this.subTitle, this.imgUrl); -} - -class CustomRadioListTile extends StatefulWidget { - const CustomRadioListTile({Key? key}) : super(key: key); - - @override - _CustomRadioListTileState createState() => _CustomRadioListTileState(); -} - -class _CustomRadioListTileState extends State { - final Map languages = { - ItemType.java: - ItemBean("Java", "曾经世界上最流行的语言", "assets/images/java.webp"), - ItemType.kotlin: - ItemBean("Kotlin", "未来世界上最流行的语言", "assets/images/kotlin.webp"), - ItemType.dart: - ItemBean("Dart", "世界上最优雅的语言", "assets/images/dart.webp"), - }; - ItemType _type = ItemType.java; - - @override - Widget build(BuildContext context) { - return Container( - color: Colors.grey.withAlpha(11), - child: Column( - mainAxisSize: MainAxisSize.min, - children: languages.keys - .map((type) => RadioListTile( - value: type, - groupValue: _type, - title: Text(languages[type]!.title), - subtitle: Text(languages[type]!.subTitle), - selected: _type == type, - secondary: CircleAvatar( - backgroundImage: AssetImage(languages[type]!.imgUrl), - ), - onChanged: (ItemType? type) => setState(() => _type = type?? _type), - )) - .toList()), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/RadioListTile/node2_dense.dart b/packages/widgets/lib/StatelessWidget/RadioListTile/node2_dense.dart deleted file mode 100644 index ddaa90f85..000000000 --- a/packages/widgets/lib/StatelessWidget/RadioListTile/node2_dense.dart +++ /dev/null @@ -1,69 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-22 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 19, -// "priority": 2, -// "name": "RadioListTile选中色和密排", -// "subtitle": "【activeColor】 : 选中时颜色 【Color】\n" -// "【dense】: 是否密排 【bool】", -// } - -enum ItemType { - java, - kotlin, - dart, -} - -class ItemBean { - final String title; - final String subTitle; - final String imgUrl; - - ItemBean(this.title, this.subTitle, this.imgUrl); -} - -class DenseRadioListTile extends StatefulWidget { - const DenseRadioListTile({Key? key}) : super(key: key); - - @override - _DenseRadioListTileState createState() => _DenseRadioListTileState(); -} - -class _DenseRadioListTileState extends State { - final Map languages = { - ItemType.java: ItemBean("Java", "曾经世界上最流行的语言", "assets/images/java.webp"), - ItemType.kotlin: - ItemBean("Kotlin", "未来世界上最流行的语言", "assets/images/kotlin.webp"), - ItemType.dart: ItemBean("Dart", "世界上最优雅的语言", "assets/images/dart.webp"), - }; - ItemType _type = ItemType.java; - - @override - Widget build(BuildContext context) { - return Container( - color: Colors.grey.withAlpha(11), - child: Column( - mainAxisSize: MainAxisSize.min, - children: languages.keys - .map((type) => RadioListTile( - value: type, - groupValue: _type, - title: Text(languages[type]!.title), - activeColor: Colors.orangeAccent, - dense: true, - subtitle: Text(languages[type]!.subTitle), - selected: _type == type, - secondary: CircleAvatar( - backgroundImage: AssetImage(languages[type]!.imgUrl), - ), - onChanged: (ItemType? type) => - setState(() => _type = type ?? _type), - )) - .toList()), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/RaisedButton/node1_base.dart b/packages/widgets/lib/StatelessWidget/RaisedButton/node1_base.dart deleted file mode 100644 index e375c2c0c..000000000 --- a/packages/widgets/lib/StatelessWidget/RaisedButton/node1_base.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-03-22 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 26, -// "priority": 1, -// "name": "RaisedButton点击事件", -// "subtitle": "【color】: 颜色 【Color】\n" -// "【splashColor】: 水波纹颜色 【Color】\n" -// "【elevation】: 影深 【double】\n" -// "【child】: 子组件 【Widget】\n" -// "【textColor】: 子组件文字颜色 【Color】\n" -// "【highlightColor】: 长按高亮色 【Color】\n" -// "【padding】: 内边距 【EdgeInsetsGeometry】\n" -// "【onPressed】: 点击事件 【Function】", -// } - -class CustomRaisedButton extends StatelessWidget { - const CustomRaisedButton({Key? key}) : super(key: key); - - final String info = - 'RaisedButton 按钮于 Flutter3.3 退出历史舞台。取代者为 ElevatedButton 按钮。'; - - @override - Widget build(BuildContext context) { - return Container( - color: Colors.blue.withOpacity(0.1), - padding: const EdgeInsets.all(10), - margin: const EdgeInsets.all(10), - child: Text(info), - ); - } - - // @override - // Widget build(BuildContext context) { - // return RaisedButton( - // color: Colors.blue, - // splashColor: Colors.green, - // onPressed: () {}, - // child: const Text("RaisedButton"), - // textColor: const Color(0xffFfffff), - // padding: const EdgeInsets.all(8), - // elevation: 5, - // highlightColor: const Color(0xffF88B0A), - // ); - // } -} diff --git a/packages/widgets/lib/StatelessWidget/RawMagnifier/node1_base.dart b/packages/widgets/lib/StatelessWidget/RawMagnifier/node1_base.dart deleted file mode 100644 index a03e40fbe..000000000 --- a/packages/widgets/lib/StatelessWidget/RawMagnifier/node1_base.dart +++ /dev/null @@ -1,87 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-22 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 360, -// "priority": 1, -// "name": "RawMagnifier 的简单使用", -// "subtitle": "【size】 : 尺寸 【Size】\n" -// "【magnificationScale】 : 放大倍率 【double】\n" -// "【focalPointOffset】: 放大内容偏移量 【Offset】\n" -// "【decoration】: 发现装饰 【MagnifierDecoration】\n" -// "【child】: 子组件 【Widget?】\n", -// } -class MagnifierCircleShape extends StatefulWidget{ - - const MagnifierCircleShape({super.key}); - - @override - State createState() => _MagnifierCircleShapeState(); -} - -class _MagnifierCircleShapeState extends State { - final Size magnifierSize = const Size(150, 150); - - @override - Widget build(BuildContext context) { - return Stack( - alignment: Alignment.center, - children: [ - GestureDetector( - onPanDown: _onPanDown, - onPanEnd: _onPanEnd, - onPanUpdate: _onPanUpdate, - onPanCancel: _onPanCancel, - child: Image.asset('assets/images/sabar_bar.webp')), - Text("张风捷特烈",style: TextStyle(color: Colors.white),), - if(_show) - Positioned( - left: _dragGesturePosition.dx, - top: _dragGesturePosition.dy, - child: _buildMagnifier()), - ], - ); - } - - Widget _buildMagnifier(){ - return RawMagnifier( - decoration: const MagnifierDecoration( - shape: CircleBorder( - side: BorderSide(color: Colors.blue, width: 2), - ), - ), - size: magnifierSize, - // focalPointOffset: Offset(-10, 0), - magnificationScale: 3, - ); - } - - Offset _dragGesturePosition = Offset.zero; - bool _show = false; - - void _onPanDown(DragDownDetails details) { - _dragGesturePosition = details.localPosition-Offset(magnifierSize.width/2,magnifierSize.height/2); - _show = true; - setState(() { - - }); - } - - void _onPanEnd(DragEndDetails details) { - setState(() => _show = false); - } - - void _onPanUpdate(DragUpdateDetails details) { - _dragGesturePosition = details.localPosition-Offset(magnifierSize.width/2,magnifierSize.height/2); - setState(() { - - }); - } - - void _onPanCancel() { - setState(() => _show = false); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/RawMagnifier/node2_shape.dart b/packages/widgets/lib/StatelessWidget/RawMagnifier/node2_shape.dart deleted file mode 100644 index b3ce9b236..000000000 --- a/packages/widgets/lib/StatelessWidget/RawMagnifier/node2_shape.dart +++ /dev/null @@ -1,124 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-22 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 360, -// "priority": 2, -// "name": "RawMagnifier 自定义装饰形状", -// "subtitle": "这里自定义五角星装饰形状", -// } -class MagnifierStarShape extends StatefulWidget{ - - const MagnifierStarShape({super.key}); - - @override - State createState() => _MagnifierStarShapeState(); -} - -class _MagnifierStarShapeState extends State { - final Size magnifierSize = const Size(150, 150); - - @override - Widget build(BuildContext context) { - return Stack( - alignment: Alignment.center, - children: [ - GestureDetector( - onPanDown: _onPanDown, - onPanEnd: _onPanEnd, - onPanUpdate: _onPanUpdate, - onPanCancel: _onPanCancel, - child: Image.asset('assets/images/sabar_bar.webp')), - Text("张风捷特烈",style: TextStyle(color: Colors.white),), - if(_show) - Positioned( - left: _dragGesturePosition.dx, - top: _dragGesturePosition.dy, - child: _buildMagnifier()), - ], - ); - } - - Widget _buildMagnifier(){ - return RawMagnifier( - decoration: MagnifierDecoration( - shape: _StarShapeBorder(), - ), - size: magnifierSize, - // focalPointOffset: Offset(-10, 0), - magnificationScale: 3, - ); - } - - Offset _dragGesturePosition = Offset.zero; - bool _show = false; - - void _onPanDown(DragDownDetails details) { - _dragGesturePosition = details.localPosition-Offset(magnifierSize.width/2,magnifierSize.height/2); - _show = true; - setState(() { - - }); - } - - void _onPanEnd(DragEndDetails details) { - setState(() => _show = false); - } - - void _onPanUpdate(DragUpdateDetails details) { - _dragGesturePosition = details.localPosition-Offset(magnifierSize.width/2,magnifierSize.height/2); - setState(() { - - }); - } - - void _onPanCancel() { - setState(() => _show = false); - } -} - -class _StarShapeBorder extends ShapeBorder { - final Path _path = Path(); - - @override - EdgeInsetsGeometry get dimensions => EdgeInsets.zero; - - @override - Path getInnerPath(Rect rect, {TextDirection? textDirection}) { - return Path(); - } - - @override - Path getOuterPath(Rect rect, {TextDirection? textDirection}) => - nStarPath(5, rect.height / 2, rect.height / 2 * 0.5, - dx: rect.width / 2, dy: rect.height / 2); - - @override - void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) { - Paint paint = Paint()..style=PaintingStyle.stroke..color=Colors.blue..strokeWidth =2; - canvas.drawPath(getOuterPath(rect), paint); - } - - Path nStarPath(int num, double R, double r, {dx = 0, dy = 0}) { - double perRad = 2 * pi / num; - double radA = perRad / 2 / 2; - double radB = 2 * pi / (num - 1) / 2 - radA / 2 + radA; - _path.moveTo(cos(radA) * R + dx, -sin(radA) * R + dy); - for (int i = 0; i < num; i++) { - _path.lineTo( - cos(radA + perRad * i) * R + dx, -sin(radA + perRad * i) * R + dy); - _path.lineTo( - cos(radB + perRad * i) * r + dx, -sin(radB + perRad * i) * r + dy); - } - _path.close(); - return _path; - } - - @override - ShapeBorder scale(double t) => this; -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/SafeArea/node1_base.dart b/packages/widgets/lib/StatelessWidget/SafeArea/node1_base.dart deleted file mode 100644 index bfeb97e82..000000000 --- a/packages/widgets/lib/StatelessWidget/SafeArea/node1_base.dart +++ /dev/null @@ -1,127 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 207 SafeArea 安全区 通过添加内边距,来适配一些手机本身特殊性(圆角、刘海屏等)而所造成的布局问题。 -// { -// "widgetId": 207, -// "name": 'SafeArea 使用测试', -// "priority": 1, -// "subtitle": -// "【left】 : 左侧是否启用 【bool】\n" -// "【top】 : 上方是否启用 【bool】\n" -// "【bottom】 : 下方是否启用 【bool】\n" -// "【right】 : 右侧是否启用 【bool】\n" -// "【child】 : 子组件 【Widget】", -// } - -class SafeAreaDemo extends StatelessWidget { - const SafeAreaDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - padding: const EdgeInsets.all(10), - child: ElevatedButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute(builder: (context) => const SafeAreaPage()), - ); - }, - child: const Text("进入 SafeArea 测试页"), - ), - ); - } -} - -class SafeAreaPage extends StatefulWidget { - const SafeAreaPage({Key? key}) : super(key: key); - - @override - _SafeAreaPageState createState() => _SafeAreaPageState(); -} - -class _SafeAreaPageState extends State { - bool _top = true; - bool _left = true; - bool _right = true; - bool _bottom = true; - - @override - Widget build(BuildContext context) { - return SafeArea( - top: _top, - left: _left, - right: _right, - bottom: _bottom, - child: Scaffold( - appBar: AppBar( - title: const Text( - 'SafeArea 测试', - ), - ), - body: Column( - children: [ - ..._buildSlider(), - Expanded( - child: ListView.separated( - itemCount: 20, - separatorBuilder: (_, __) => const Divider( - height: 1, - ), - itemBuilder: (_, index) => Container( - color: Colors.blue, - // padding: EdgeInsets.only(left: 20), - alignment: Alignment.center, - height: 50, - child: Text( - "第$index个", - style: const TextStyle(fontSize: 24, color: Colors.white), - ), - ), - ), - ), - ], - ), - ), - ); - } - - List _buildSlider()=>[Row( - children: [ - Switch( - value: _top, - onChanged: (v) => setState(() => _top = v), - ), - Text("top: $_top") - ], - ), - Row( - children: [ - Switch( - value: _left, - onChanged: (v) => setState(() => _left = v), - ), - Text("left: $_left") - ], - ), - Row( - children: [ - Switch( - value: _right, - onChanged: (v) => setState(() => _right = v), - ), - Text("right: $_right") - ], - ), - Row( - children: [ - Switch( - value: _bottom, - onChanged: (v) => setState(() => _bottom = v), - ), - Text("bottom: $_bottom") - ], - ),]; -} diff --git a/packages/widgets/lib/StatelessWidget/ScrollView/node1_base.dart b/packages/widgets/lib/StatelessWidget/ScrollView/node1_base.dart deleted file mode 100644 index 5211f615d..000000000 --- a/packages/widgets/lib/StatelessWidget/ScrollView/node1_base.dart +++ /dev/null @@ -1,117 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/9/21 -/// contact me by email 1981462002@qq.com -/// 说明: 349 ScrollView 滑动视图 -/// 该组件用于滑动的支持,该类是一个抽象类,所以无法直接使用,它有很多实现类,如 CustomScrollView、BoxScrollView、ListView、GridView。 -/// link 183,162,163,253,340 -/// -// { -// "widgetId": 349, -// "name": 'ScrollView 介绍', -// "priority": 1, -// "subtitle": "【reverse】 : 是否反向 【bool】\n" -// "【scrollDirection】 : 滑动方向 【Axis】\n" -// "【cacheExtent】 : 缓存长 【double】\n" -// "【dragStartBehavior】 : 拖动行为 【DragStartBehavior】\n" -// "【clipBehavior】 : 裁剪行为 【ClipBehavior】\n" -// "【controller】 : 控制器 【ScrollController】", -// } - -class ScrollViewDemo extends StatelessWidget { - const ScrollViewDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 300, - child: MyScrollView(), - ); - } -} - -class MyScrollView extends ScrollView { - MyScrollView({Key? key}) : super(key: key); - - final String info = - 'ScrollView 其内部依靠 Viewport + Scrollable 实现滑动。它只有一个 buildSlivers 的抽象方法,返回 Sliver 家族 Widget 列表,' - '其子类最简单的是 CustomScrollView,将 slivers 交由用户传递,自身打个酱油。' - 'ListView 和 GridView 在底层源码中也是使用 Sliver 家族相关组件实现的。'; - - final List data = [ - Colors.purple[50]!, - Colors.purple[100]!, - Colors.purple[200]!, - Colors.purple[300]!, - Colors.purple[400]!, - Colors.purple[500]!, - Colors.purple[600]!, - Colors.purple[700]!, - Colors.purple[800]!, - Colors.purple[900]!, - ]; - - @override - List buildSlivers(BuildContext context) { - return [ - _buildSliverAppBar(), - SliverToBoxAdapter( - child: Container( - color: Colors.blue.withOpacity(0.1), - padding: const EdgeInsets.all(10), - margin: const EdgeInsets.all(10), - child: Text(info), - ), - ), - _buildSliverFixedExtentList() - ]; - } - - _buildSliverAppBar() { - return SliverAppBar( - expandedHeight: 190.0, - leading: Container( - margin: const EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')), - flexibleSpace: FlexibleSpaceBar(//伸展处布局 - titlePadding: const EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - title: const Text( - '张风捷特烈', - style: TextStyle(color: Colors.black, //标题 - shadows: [ - Shadow(color: Colors.blue, offset: Offset(1, 1), blurRadius: 2) - ]), - ), - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } - - Widget _buildSliverFixedExtentList() => SliverFixedExtentList( - itemExtent: 60, - delegate: SliverChildBuilderDelegate( - (_, int index) => Container( - alignment: Alignment.center, - width: 100, - height: 50, - color: data[index], - child: Text( - colorString(data[index]), - style: const TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2) - ]), - ), - ), - childCount: data.length), - ); - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/packages/widgets/lib/StatelessWidget/SimpleDialog/node1_base.dart b/packages/widgets/lib/StatelessWidget/SimpleDialog/node1_base.dart deleted file mode 100644 index 672494c6f..000000000 --- a/packages/widgets/lib/StatelessWidget/SimpleDialog/node1_base.dart +++ /dev/null @@ -1,124 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-24 -/// contact me by email 1981462002@qq.com -/// 说明: -/// -// { -// "widgetId": 128, -// "name": 'SimpleDialog基本使用', -// "priority": 1, -// "subtitle": -// "【title】 : 顶部组件 【Widget】\n" -// "【children】 : 子组件列表 【List】\n" -// "【titlePadding】 : 顶部内边距 【EdgeInsetsGeometry】\n" -// "【contentPadding】 : 内容内边距 【EdgeInsetsGeometry】\n" -// "【backgroundColor】 : 背景色 【Color】\n" -// "【elevation】 : 影深 【double】\n" -// "【shape】 : 形状 【ShapeBorder】", -// } -class CustomSimpleDialog extends StatelessWidget { - const CustomSimpleDialog({Key? key}) : super(key: key); - - final List info = const [ - '性别: 男 未婚', - '微信: zdl1994328', - "掘金: 张风捷特烈", - "github: toly1994328", - "邮箱: 1981462008@qq.com", - ]; - - @override - Widget build(BuildContext context) { - return Stack( - children: [ - _buildSimpleDialog(context), - Positioned( - top: 70, - right: 30, - child: _buildRaisedButton(context)), - - ], - ); - } - Widget _buildRaisedButton(BuildContext context) => ElevatedButton( - style: TextButton.styleFrom( - backgroundColor: Colors.blue, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10)), - ), - ), - onPressed: () { - showDialog(context: context, builder: (ctx) => _buildSimpleDialog(ctx)); - }, - child: const Text( - 'Just Show It', - style: TextStyle(color: Colors.white), - ), - ); - - SimpleDialog _buildSimpleDialog(BuildContext context) { - return SimpleDialog( - title: _buildTitle(), - titlePadding: const EdgeInsets.only( - top: 5, - left: 20, - ), - contentPadding: const EdgeInsets.symmetric(horizontal: 5), - children: _buildChild(context), - backgroundColor: Colors.white, - elevation: 4, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10))), - ); - } - - List _buildChild(BuildContext context) { - return info - .map((str) => Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SimpleDialogOption( - onPressed: () => print(str), - child: SizedBox( - width: double.infinity, - child: Text( - str, - style: const TextStyle( - color: Color(0xff999999), fontSize: 16), - ), - ), - ), - Divider( - indent: 20, - height: 12, - color: info.indexOf(str) == info.length - 1 - ? Colors.transparent - : Theme.of(context).dividerColor, - ) - ], - )) - .toList(); - } - - Widget _buildTitle() { - return Row(//标题 - children: [ - Image.asset( - "assets/images/icon_head.webp", - width: 30, - height: 30, - ), - const SizedBox( - width: 10, - ), - const Expanded( - child: Text( - "张风捷特烈", - style: TextStyle(fontSize: 18), - )), - const CloseButton() - ], - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/SimpleDialogOption/node1_base.dart b/packages/widgets/lib/StatelessWidget/SimpleDialogOption/node1_base.dart deleted file mode 100644 index a5ca6e83d..000000000 --- a/packages/widgets/lib/StatelessWidget/SimpleDialogOption/node1_base.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 133, -// "name": 'SimpleDialogOption基本使用', -// "priority": 1, -// "subtitle": "【child】 : 子组件 【Widget】\n" -// "【onPressed】 : 点击事件 【Function()】", -// } -class CustomSimpleDialogOption extends StatelessWidget { - const CustomSimpleDialogOption({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Container( - alignment: Alignment.center, - width: double.infinity, - height: 50, - margin: const EdgeInsets.all(5), - color: Colors.grey.withAlpha(33), - child: SimpleDialogOption( - onPressed: () => Navigator.of(context).pushNamed('AboutMePage'), - child: const Text('张风捷特烈')), - ), - Container( - height: 50, - alignment: Alignment.center, - width: double.infinity, - color: Colors.grey.withAlpha(33), - margin: const EdgeInsets.all(5), - child: SimpleDialogOption( - onPressed: () => Navigator.of(context).pushNamed('AboutMePage'), - child: const Text('百里·巫缨')), - ), - ], - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/SingleChildScrollView/node1_base.dart b/packages/widgets/lib/StatelessWidget/SingleChildScrollView/node1_base.dart deleted file mode 100644 index 8ea25ad08..000000000 --- a/packages/widgets/lib/StatelessWidget/SingleChildScrollView/node1_base.dart +++ /dev/null @@ -1,67 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-28 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 164, -// "name": 'SingleChildScrollView基本使用', -// "priority": 1, -// "subtitle": "【child】 : 子组件 【Widget】\n" -// "【padding】 : 内边距 【EdgeInsetsGeometry】", -// } -class CustomSingleChildScrollView extends StatelessWidget { - CustomSingleChildScrollView({Key? key}) : super(key: key); - - final List data = [ - Colors.blue[50]!, - Colors.blue[100]!, - Colors.blue[200]!, - Colors.blue[300]!, - Colors.blue[400]!, - Colors.blue[500]!, - Colors.blue[600]!, - Colors.blue[700]!, - Colors.blue[800]!, - Colors.blue[900]!, - ]; - - TextStyle get textStyle => const TextStyle( - color: Colors.white, - shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2, - ) - ], - ); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 200, - child: SingleChildScrollView( - padding: const EdgeInsets.symmetric(horizontal: 10), - child: Column( - children: data - .map((color) => Container( - alignment: Alignment.center, - height: 50, - color: color, - child: Text( - colorString(color), - style: textStyle, - ), - )) - .toList(), - ), - - ), - ); - } - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/SingleChildScrollView/node2_direction.dart b/packages/widgets/lib/StatelessWidget/SingleChildScrollView/node2_direction.dart deleted file mode 100644 index 030dfe9ef..000000000 --- a/packages/widgets/lib/StatelessWidget/SingleChildScrollView/node2_direction.dart +++ /dev/null @@ -1,69 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/27 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 164, -// "name": 'SingleChildScrollView滑动方向', -// "priority": 2, -// "subtitle": "【scrollDirection】 : 滑动方向 【Axis】\n" -// "【reverse】 : 是否反向 【Axis】", -// } -class DirectionSingleChildScrollView extends StatelessWidget { - DirectionSingleChildScrollView({Key? key}) : super(key: key); - - final List data = [ - Colors.blue[50]!, - Colors.blue[100]!, - Colors.blue[200]!, - Colors.blue[300]!, - Colors.blue[400]!, - Colors.blue[500]!, - Colors.blue[600]!, - Colors.blue[700]!, - Colors.blue[800]!, - Colors.blue[900]!, - ]; - - TextStyle get textStyle => const TextStyle( - color: Colors.white, - shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2, - ) - ], - ); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 200, - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - reverse: true, - padding: const EdgeInsets.symmetric(horizontal: 10), - child: Row( - children: data - .map((color) => Container( - alignment: Alignment.center, - width: 90, - color: color, - child: Text( - colorString(color), - style: textStyle, - ), - )) - .toList(), - ), - - ), - ); - } - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/SnackBar/node1_base.dart b/packages/widgets/lib/StatelessWidget/SnackBar/node1_base.dart deleted file mode 100644 index 48b2896ef..000000000 --- a/packages/widgets/lib/StatelessWidget/SnackBar/node1_base.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 140, -// "name": 'SnackBar基本使用', -// "priority": 1, -// "subtitle": -// "【content】 : 中间内容组件 【Widget】\n" -// "【action】 : 右侧按钮 【SnackBarAction】\n" -// "【duration】 : 持续时长 【Widget】\n" -// "【backgroundColor】 : 背景色 【Color】\n" -// "【elevation】 : 影深 【double】\n" -// "【shape】 : 形状 【ShapeBorder】\n" -// "【onVisible】 : 显示时回调 【Function()】", -// } -class CustomSnackBar extends StatefulWidget { - const CustomSnackBar({Key? key}) : super(key: key); - - @override - _CustomSnackBarState createState() => _CustomSnackBarState(); -} - -class _CustomSnackBarState extends State { - @override - Widget build(BuildContext context) { - return ElevatedButton( - onPressed: () => - ScaffoldMessenger.of(context).showSnackBar(_buildSnackBar()), - child: const Text( - '点我弹出SnackBar', - style: TextStyle(color: Colors.white), - )); - } - - SnackBar _buildSnackBar() { - return SnackBar( - elevation: 3, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(10), topRight: Radius.circular(10))), - content: const Text('Wellcome to for join Flutter Unit!'), - duration: const Duration(seconds: 3), - //持续时间 - backgroundColor: Colors.red, - onVisible: () => print('onVisible'), - action: SnackBarAction( - textColor: Colors.white, label: '确定', onPressed: () {}), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/SnackBarAction/node1_base.dart b/packages/widgets/lib/StatelessWidget/SnackBarAction/node1_base.dart deleted file mode 100644 index 2e8fa3683..000000000 --- a/packages/widgets/lib/StatelessWidget/SnackBarAction/node1_base.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 141, -// "name": 'SnackBarAction基本使用', -// "priority": 1, -// "subtitle": -// "【label】 : 标签 【String】\n" -// "【textColor】 : 文字颜色 【Color】\n" -// "【disabledTextColor】 : 文字失效色 【Color】\n" -// "【onPressed】 : 点击回调 【Function()】", -// } -class CustomSnackBarAction extends StatelessWidget { - const CustomSnackBarAction({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return SnackBarAction( - disabledTextColor: Colors.red, - textColor: Colors.blue, - label: '确定', - onPressed: () => print('onPressed'), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/Spacer/node1_base.dart b/packages/widgets/lib/StatelessWidget/Spacer/node1_base.dart deleted file mode 100644 index 8423188d2..000000000 --- a/packages/widgets/lib/StatelessWidget/Spacer/node1_base.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-22 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 107, -// "name": 'Spacer基本使用', -// "priority": 1, -// "subtitle": -// "一个Spacer会占据可延伸区域", -// } -class OneSpacer extends StatelessWidget { - const OneSpacer({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return - Container( - color: Colors.grey.withAlpha(33), - child: Row(children: [ - Container( - alignment: Alignment.center, - width: 100, - height: 50, - color: Colors.red, - ), - const Spacer(), - Container( - alignment: Alignment.center, - width: 60, - height: 50, - color: Colors.blue, - ), - ],), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/Spacer/node2_flex.dart b/packages/widgets/lib/StatelessWidget/Spacer/node2_flex.dart deleted file mode 100644 index 8ebdcbfc9..000000000 --- a/packages/widgets/lib/StatelessWidget/Spacer/node2_flex.dart +++ /dev/null @@ -1,75 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-22 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 107, -// "name": '多个Spacer空间分配', -// "priority": 2, -// "subtitle": -// "【flex】 : 剩余空间分配占比 【int】", -// } -class ManySpacer extends StatefulWidget { - const ManySpacer({Key? key}) : super(key: key); - - @override - _ManySpacerState createState() => _ManySpacerState(); -} - -class _ManySpacerState extends State { - int _flexA = 1; - int _flexB = 1; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - _buildSliders(), - Container( - color: Colors.grey.withAlpha(33), - child: Row(children: [ - Spacer(flex: _flexA), - Container( - alignment: Alignment.center, - width: 100, - height: 50, - color: Colors.red, - ), - Spacer(flex: _flexB), - Container( - alignment: Alignment.center, - width: 60, - height: 50, - color: Colors.blue, - ), - ],), - ) - ], - ); - } - - Widget _buildSliders() { - return Column( - children: [ - Slider( - divisions: 20, - min: 1, - max: 10, - label: "左边flex: $_flexA", - value: _flexA.toDouble(), - onChanged: (v) => setState(() => _flexA = v.round()) - ), - Slider( - divisions: 20, - label: "右边flex: $_flexB", - min: 1, - max: 10, - value: _flexB.toDouble(), - onChanged: (v) => setState(() => _flexB = v.round()) - ), - ], - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/SwitchListTile/node1_base.dart b/packages/widgets/lib/StatelessWidget/SwitchListTile/node1_base.dart deleted file mode 100644 index 716cfad9e..000000000 --- a/packages/widgets/lib/StatelessWidget/SwitchListTile/node1_base.dart +++ /dev/null @@ -1,47 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 18, -// "priority": 1, -// "name": "SwitchListTile的基本表现如下", -// "subtitle": "【secondary】: 左侧组件 【Widget】\n" -// "【title】: 中间上组件 【Widget】\n" -// "【subtitle】: 中间下组件 【Widget】\n" -// "【inactiveThumbColor】: 未选中时圆圈颜色 【Color】\n" -// "【inactiveTrackColor】: 未选中滑槽颜色 【Color】\n" -// "【activeColor】: 选中时圆圈颜色 【Color】\n" -// "【activeTrackColor】: 选中滑槽颜色 【Color】\n" -// "【onChanged】: 选中事件 【Function(bool)】", -// } -class CustomSwitchListTile extends StatefulWidget { - const CustomSwitchListTile({Key? key}) : super(key: key); - - @override - _CustomSwitchListTileState createState() => _CustomSwitchListTileState(); -} - -class _CustomSwitchListTileState extends State { - var _value=false; - - @override - Widget build(BuildContext context) { - return Container( - margin: const EdgeInsets.all(10), - color: Colors.grey.withAlpha(22), - child: SwitchListTile( - value: _value, - inactiveThumbColor:Colors.cyanAccent , - inactiveTrackColor: Colors.blue.withAlpha(88), - activeColor: Colors.orangeAccent, - activeTrackColor: Colors.orange, - secondary: Image.asset("assets/images/icon_head.webp"), - title: const Text("张风捷特烈"), - subtitle: const Text("@万花过尽知无物"), - onChanged: (v) => setState(() => _value = !_value), - ), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/SwitchListTile/node2_select.dart b/packages/widgets/lib/StatelessWidget/SwitchListTile/node2_select.dart deleted file mode 100644 index 8b9743e9c..000000000 --- a/packages/widgets/lib/StatelessWidget/SwitchListTile/node2_select.dart +++ /dev/null @@ -1,43 +0,0 @@ - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 18, -// "priority": 2, -// "name": "SwitchListTile的选中效果", -// "subtitle": "【selected】: 是否选中 【bool】\n" -// "【inactiveThumbImage】: 未选中时圆圈图片 【ImageProvider】\n" -// "【activeThumbImage】: 选中时圆圈图片 【ImageProvider】", -// } -class SelectSwitchListTile extends StatefulWidget { - const SelectSwitchListTile({Key? key}) : super(key: key); - - @override - _SelectSwitchListTileState createState() => _SelectSwitchListTileState(); -} - -class _SelectSwitchListTileState extends State { - bool _value=false; - - @override - Widget build(BuildContext context) { - return Container( - margin: const EdgeInsets.all(10), - color: Colors.grey.withAlpha(22), - child: SwitchListTile( - value: _value, - selected: _value, - activeColor: Colors.orangeAccent, - secondary: Image.asset("assets/images/icon_head.webp"), - inactiveThumbImage: const AssetImage("assets/images/head_icon/icon_5.webp"), - activeThumbImage: const AssetImage("assets/images/icon_head.webp"), - title: const Text("张风捷特烈"), - subtitle: const Text("@万花过尽知无物"), - onChanged: (v) => setState(() => _value = !_value), - ), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/SwitchListTile/node3_dense.dart b/packages/widgets/lib/StatelessWidget/SwitchListTile/node3_dense.dart deleted file mode 100644 index a34580af8..000000000 --- a/packages/widgets/lib/StatelessWidget/SwitchListTile/node3_dense.dart +++ /dev/null @@ -1,40 +0,0 @@ - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 18, -// "priority": 3, -// "name": "SwitchListTile的密排属性", -// "subtitle": "【dense】: 是否密排 【bool】", -// } -class DenseSwitchListTile extends StatefulWidget { - const DenseSwitchListTile({Key? key}) : super(key: key); - - @override - _DenseSwitchListTileState createState() => _DenseSwitchListTileState(); -} - -class _DenseSwitchListTileState extends State { - bool _value = false; - - @override - Widget build(BuildContext context) { - return Container( - margin: const EdgeInsets.all(10), - color: Colors.grey.withAlpha(22), - child: SwitchListTile( - value: _value, - dense: true, - selected: _value, - activeColor: Colors.orangeAccent, - secondary: Image.asset("assets/images/icon_head.webp"), - title: const Text("张风捷特烈"), - subtitle: const Text("@万花过尽知无物"), - onChanged: (v) => setState(() => _value = !_value), - ), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/Tab/node1_base.dart b/packages/widgets/lib/StatelessWidget/Tab/node1_base.dart deleted file mode 100644 index 77d476bc4..000000000 --- a/packages/widgets/lib/StatelessWidget/Tab/node1_base.dart +++ /dev/null @@ -1,41 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-25 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 148, -// "name": 'Tab基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 子组件 【Widget】\n" -// "【text】 : 文字 【String】\n" -// "【icon】 : 下方组件 【Widgit】\n" -// " text和child不能同时存在", -// } - -class CustomTab extends StatelessWidget { - const CustomTab({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: const [ - Tab( - icon:Icon( Icons.add,color: Colors.blue,), - child: Text('添加'), - ), - Tab( - icon:Icon( Icons.close,color: Colors.red,), - text: '删除', - ), - Tab( - icon:Icon( Icons.refresh,color: Colors.green), - text: '更新', - ), - ], - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/TabBar/node1_base.dart b/packages/widgets/lib/StatelessWidget/TabBar/node1_base.dart deleted file mode 100644 index 56f089617..000000000 --- a/packages/widgets/lib/StatelessWidget/TabBar/node1_base.dart +++ /dev/null @@ -1,62 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-16 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 58, -// "name": 'TabBar基本使用', -// "priority": 1, -// "subtitle": -// "【controller】 : 控制器 【TabController】\n" -// "【indicatorColor】 : 指示器颜色 【指示器颜色】\n" -// "【indicatorWeight】 : 指示器高 【double】\n" -// "【indicatorPadding】 : 指示器边距 【EdgeInsetsGeometry】\n" -// "【labelStyle】 : 页签文字样式 【TextStyle】\n" -// "【unselectedLabelStyle】 : 未选中文字样式 【TextStyle】\n" -// "【isScrollable】 : 是否可滑动 【bool】\n" -// "【onTap】 : 页签点击回调 【Function(int)】\n" -// "【tabs】 : 标签组件 【List】", -// } -class CustomTabBar extends StatefulWidget { - const CustomTabBar({Key? key}) : super(key: key); - - @override - _CustomTabBarState createState() => _CustomTabBarState(); -} - -class _CustomTabBarState extends State - with SingleTickerProviderStateMixin { - final List tabs = ['风画庭', '雨韵舍', '雷鸣殿', '电疾堂', '霜寒阁', '雪月楼']; - late TabController _tabController; - - @override - void initState() { - super.initState(); - _tabController = TabController(vsync: this, length: tabs.length); - } - - @override - void dispose() { - _tabController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return TabBar( - onTap: (tab) => print(tab), - labelStyle: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), - unselectedLabelStyle: const TextStyle(fontSize: 16), - isScrollable: true, - controller: _tabController, - labelColor: Colors.blue, - indicatorWeight: 3, - indicatorPadding: const EdgeInsets.symmetric(horizontal: 10), - unselectedLabelColor: Colors.grey, - indicatorColor: Colors.orangeAccent, - tabs: tabs.map((e) => Tab(text: e)).toList(), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/TabBar/node2_noShadow.dart b/packages/widgets/lib/StatelessWidget/TabBar/node2_noShadow.dart deleted file mode 100644 index 698975291..000000000 --- a/packages/widgets/lib/StatelessWidget/TabBar/node2_noShadow.dart +++ /dev/null @@ -1,60 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/27 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 58, -// "name": '通过设置Theme可实现无水波纹', -// "priority": 1, -// "subtitle": -// "将Theme关于水波纹的颜色设置为透明即可。", -// } -class NoShadowTabBarDemo extends StatefulWidget { - const NoShadowTabBarDemo({Key? key}) : super(key: key); - - @override - _NSTabBarState createState() => _NSTabBarState(); -} - -class _NSTabBarState extends State - with SingleTickerProviderStateMixin { - final List tabs = ['风画庭', '雨韵舍', '雷鸣殿', '电疾堂', '霜寒阁', '雪月楼']; - late TabController _tabController; - - @override - void initState() { - super.initState(); - _tabController = TabController(vsync: this, length: tabs.length); - } - - @override - void dispose() { - _tabController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Theme( - data: Theme.of(context).copyWith( - splashColor: Colors.transparent, - highlightColor: Colors.transparent, - ), - child: TabBar( - onTap: (tab) => print(tab), - labelStyle: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), - unselectedLabelStyle: const TextStyle(fontSize: 16), - isScrollable: true, - controller: _tabController, - labelColor: Colors.blue, - indicatorWeight: 3, - indicatorPadding: const EdgeInsets.symmetric(horizontal: 10), - unselectedLabelColor: Colors.grey, - indicatorColor: Colors.orangeAccent, - tabs: tabs.map((e) => Tab(text: e)).toList(), - ), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/TabPageSelector/node1_base.dart b/packages/widgets/lib/StatelessWidget/TabPageSelector/node1_base.dart deleted file mode 100644 index ab504e0c1..000000000 --- a/packages/widgets/lib/StatelessWidget/TabPageSelector/node1_base.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/7/22 -/// contact me by email 1981462002@qq.com -/// 说明: 205 TabPageSelector 通常作为指示器与TabBarView联用,共同使用一个TabController。可指定颜色、大小、选中色。 - -// { -// "widgetId": 205, -// "name": "TabPageSelector基本使用", -// "priority": 1, -// "subtitle": "【controller】 : 控制器 【TabController】\n" -// "【indicatorSize】: 指示器大小 【double】\n" -// "【selectedColor】: 选中色 【Color】\n" -// "【color】: 颜色 【Color】", -// } - -class TabPageSelectorDemo extends StatefulWidget { - const TabPageSelectorDemo({Key? key}) : super(key: key); - - @override - _TabPageSelectorDemoState createState() => _TabPageSelectorDemoState(); -} - -class _TabPageSelectorDemoState extends State - with SingleTickerProviderStateMixin { - final List tabs = ['风画庭', '雨韵舍', '雷鸣殿', '电疾堂', '霜寒阁', '雪月楼']; - late TabController _tabController; - - @override - void initState() { - super.initState(); - _tabController = TabController(vsync: this, length: tabs.length); - } - - @override - void dispose() { - _tabController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 200, - child: Stack( - alignment: Alignment.center, - children: [ - Container(color: Colors.purple, child: _buildTableBarView()), - Positioned( - bottom: 10, - child: buildTabPageSelector(), - ), - ], - ), - ); - } - - Widget buildTabPageSelector() => TabPageSelector( - controller: _tabController, - color: Colors.white, - indicatorSize: 10, - selectedColor: Colors.orangeAccent, - ); - - Widget _buildTableBarView() => TabBarView( - controller: _tabController, - children: tabs - .map((e) => Center( - child: Text( - e, - style: const TextStyle(color: Colors.white, fontSize: 20), - ))) - .toList()); -} diff --git a/packages/widgets/lib/StatelessWidget/TabPageSelectorIndicator/node1_base.dart b/packages/widgets/lib/StatelessWidget/TabPageSelectorIndicator/node1_base.dart deleted file mode 100644 index 6b377b3d8..000000000 --- a/packages/widgets/lib/StatelessWidget/TabPageSelectorIndicator/node1_base.dart +++ /dev/null @@ -1,56 +0,0 @@ -import 'package:flutter/material.dart'; - - - -/// create by 张风捷特烈 on 2020/7/22 -/// contact me by email 1981462002@qq.com -/// 说明: 206 TabPageSelectorIndicator 一个有边线的圆形组件,可指定大小、颜色、边线色。是TabPageSelector的部分之一,一般不单独使用。 - -// { -// "widgetId": 206, -// "name": "TabPageSelectorIndicator基本使用", -// "priority": 1, -// "subtitle": "【size】: 大小 【double】\n" -// "【backgroundColor】: 背景色 【Color】\n" -// "【borderColor】: 边线色 【Color】", -// } - -class TabPageSelectorIndicatorDemo extends StatelessWidget { - const TabPageSelectorIndicatorDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children:const [ - TabPageSelectorIndicator( - backgroundColor: Colors.greenAccent, - borderColor: Colors.deepPurpleAccent, - size: 15, - ), - TabPageSelectorIndicator( - backgroundColor: Colors.blue, - borderColor: Colors.grey, - size: 20, - ), - TabPageSelectorIndicator( - backgroundColor: Colors.green, - borderColor: Colors.red, - size: 25, - ), - TabPageSelectorIndicator( - backgroundColor: Colors.yellow, - borderColor: Colors.brown, - size: 30, - ), - TabPageSelectorIndicator( - backgroundColor: Colors.amber, - borderColor: Colors.purpleAccent, - size: 35, - ), - ], - ); - } - -} - diff --git a/packages/widgets/lib/StatelessWidget/Text/node1_base.dart b/packages/widgets/lib/StatelessWidget/Text/node1_base.dart deleted file mode 100644 index fc27085b1..000000000 --- a/packages/widgets/lib/StatelessWidget/Text/node1_base.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: - - -// { -// "widgetId": 2, -// "priority": 1, -// "name": "文字的基本样式", -// "subtitle": "【入参】 : 文字 【String】\n" -// "【style】: 文字样式 【TextStyle】\n" -// "【color】: 文字样式 【Color】\n" -// "【fontSize】: 文字大小 【double】\n" -// "【fontWeight】: 字重 【FontWeight】\n" -// "【fontStyle】: 字体样式 【fontStyle】\n" -// "【letterSpacing】: 字距 【double】", -// } -class CustomText extends StatelessWidget { - const CustomText({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - TextStyle style = const TextStyle( - color: Colors.blue, - fontSize: 20, - fontWeight: FontWeight.bold, - fontStyle: FontStyle.italic, - letterSpacing: 10, - ); - return Container( - width: 200, - color: Colors.cyanAccent.withAlpha(33), - height: 200 * 0.618 * 0.618, - child: Text("toly-张风捷特烈-1994`", style: style), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/Text/node2_shadows.dart b/packages/widgets/lib/StatelessWidget/Text/node2_shadows.dart deleted file mode 100644 index a7bf94140..000000000 --- a/packages/widgets/lib/StatelessWidget/Text/node2_shadows.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: - - -// { -// "widgetId": 2, -// "priority": 2, -// "name": "文字阴影", -// "subtitle": "【shadows】 : 文字 【List】\n" -// "【backgroundColor】: 背景颜色 【Color】", -// } -class ShadowText extends StatelessWidget { - const ShadowText({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return const Text( - "张风捷特烈", - style: TextStyle( - fontSize: 50, - color: Colors.white, - backgroundColor: Colors.black, - shadows: [ - Shadow( - color: Colors.cyanAccent, - offset: Offset(1, 1), - blurRadius: 10), - Shadow( - color: Colors.blue, - offset: Offset(-0.1, 0.1), - blurRadius: 10), - ]), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/Text/node3_decoration.dart b/packages/widgets/lib/StatelessWidget/Text/node3_decoration.dart deleted file mode 100644 index 1aee5c8e5..000000000 --- a/packages/widgets/lib/StatelessWidget/Text/node3_decoration.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: - - -// { -// "widgetId": 2, -// "priority": 3, -// "name": "文字装饰线", -// "subtitle": "【fontFamily】 : 文字字体 【String】\n" -// "【decoration】: 装饰线 【TextDecoration】\n" -// "【decorationColor】: 装饰线颜色 【Color】\n" -// "【decorationThickness】: 装饰线粗 【double】\n" -// "【decorationStyle】: 装饰线样式 【TextDecorationStyle】", -// } -class DecorationText extends StatelessWidget { - const DecorationText({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return const Text( - "19940328", - style: TextStyle( - fontSize: 50, - fontWeight: FontWeight.bold, - decoration: TextDecoration.underline, - decorationThickness: 3, - decorationStyle: TextDecorationStyle.wavy, - decorationColor: Colors.blue, - fontStyle: FontStyle.italic, - fontFamily: "DancingScript", - letterSpacing: 10), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/Text/node4_textAlign.dart b/packages/widgets/lib/StatelessWidget/Text/node4_textAlign.dart deleted file mode 100644 index 11b8b2ce6..000000000 --- a/packages/widgets/lib/StatelessWidget/Text/node4_textAlign.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: - - -// { -// "widgetId": 2, -// "priority": 4, -// "name": "文字对齐方式", -// "subtitle": "【textAlign】: 对齐方式 【TextAlign】\n" -// "下面依次是:left、right、center、justify、start、end ", -// } -class TextAlignText extends StatelessWidget { - const TextAlignText({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 10, - runSpacing: 10, - children: TextAlign.values - .map((TextAlign textAlign) => Container( - width: 120, - color: Colors.cyanAccent.withAlpha(33), - height: 120 * 0.618, - child: Text( - " 张风捷特烈 toly " * 3, - textAlign: textAlign, - ), - )) - .toList(), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/Text/node5_textDirection.dart b/packages/widgets/lib/StatelessWidget/Text/node5_textDirection.dart deleted file mode 100644 index da2ce798a..000000000 --- a/packages/widgets/lib/StatelessWidget/Text/node5_textDirection.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: - - -// { -// "widgetId": 2, -// "priority": 5, -// "name": "文字方向与最大行数", -// "subtitle": "【maxLines】 : 最大行数 【int】\n" -// "【textDirection】 : 文字方向 【TextDirection】\n" -// "下面依次是:rtl、ltr ", -// } -class TextDirectionText extends StatelessWidget { - const TextDirectionText({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 40, - runSpacing: 10, - children: TextDirection.values - .map((TextDirection direction) => Container( - width: 120, - color: Colors.cyanAccent.withAlpha(33), - height: 120 * 0.618, - child: Text( - " 张风捷特烈 toly " * 10, - textDirection: direction, - maxLines: 3, - overflow: TextOverflow.ellipsis, - ), - )) - .toList(), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/Text/node6_softWrap.dart b/packages/widgets/lib/StatelessWidget/Text/node6_softWrap.dart deleted file mode 100644 index cc80eb61b..000000000 --- a/packages/widgets/lib/StatelessWidget/Text/node6_softWrap.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: - - -// { -// "widgetId": 2, -// "priority": 6, -// "name": "是否包裹与越界效果", -// "subtitle": "【softWrap】 : 是否换行 【bool】\n" -// "【overflow】 : 越界效果 【TextOverflow】\n" -// "下面softWrap=false; overflow依次是:clip、fade、ellipsis、visible ", -// } -class SoftWrapText extends StatelessWidget { - const SoftWrapText({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 10, - runSpacing: 10, - children: TextOverflow.values - .map((TextOverflow textOverflow) => Container( - width: 150, - color: Colors.cyanAccent.withAlpha(33), - height: 150 * 0.618 * 0.618, - child: Text( - " 张风捷特烈 toly " * 5, - overflow: textOverflow, - softWrap: false, - ), - )) - .toList(), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/Theme/node1_base.dart b/packages/widgets/lib/StatelessWidget/Theme/node1_base.dart deleted file mode 100644 index d16b467fd..000000000 --- a/packages/widgets/lib/StatelessWidget/Theme/node1_base.dart +++ /dev/null @@ -1,62 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 168, -// "name": '文字样式-ThemeData#TextTheme', -// "priority": 1, -// "subtitle": -// "子组件可以通过ThemeData.of获取主题的数据进行使用。", -// } -class TextThemeDemo extends StatelessWidget { - const TextThemeDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - TextTheme queryData = Theme.of(context).textTheme; - Map styles = { - "headline1: ": queryData.headline1!, - "headline2: ": queryData.headline2!, - "headline3: ": queryData.headline3!, - "headline4: ": queryData.headline4!, - "headline5: ": queryData.headline5!, - "headline6: ": queryData.headline6!, - "button: ": queryData.button!, - "overline: ": queryData.overline!, - "subtitle1: ": queryData.subtitle1!, - "subtitle2: ": queryData.subtitle2!, - "caption: ": queryData.caption!, - "bodyText1: ": queryData.bodyText1!, - "bodyText2: ": queryData.bodyText2!, - }; - - return Column( - children: styles.keys - .map((String styleInfo) => buildItem(styleInfo, styles[styleInfo]!)) - .toList(), - ); - } - - TextStyle get textStyle => const TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - ); - - Widget buildItem(String styleInfo, TextStyle style) => Column( - children: [ - Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(styleInfo, style: textStyle), - Text("@toly", style: style) - ], - ), - ), - const Divider(height: 1) - ], - ); -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/Theme/node2_use.dart b/packages/widgets/lib/StatelessWidget/Theme/node2_use.dart deleted file mode 100644 index 89d1d3ecb..000000000 --- a/packages/widgets/lib/StatelessWidget/Theme/node2_use.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/27 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 168, -// "name": 'Theme的用法', -// "priority": 2, -// "subtitle": -// "使用Theme,可以指定非常多的属性作为主题,这些属性将应用于所有的后代组件,如指定字体、滑块、卡片、文字、分割线、按钮等属性。", -// } -class CustomTheme extends StatelessWidget { - const CustomTheme({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Theme( - data: ThemeData( - cardTheme: const CardTheme( - color: Colors.red, - elevation: 4, - ), - dividerTheme: const DividerThemeData( - color: Colors.blue, - thickness: 2, - ), - sliderTheme: const SliderThemeData( - thumbColor: Colors.red, - activeTrackColor: Colors.green, - inactiveTrackColor: Colors.grey, - )), - child: Wrap( - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Card( - child: Container( - width: 50, - height: 50, - color: Colors.transparent, - ), - ), - const SizedBox( - width: 150, - child: Slider(value: 0.8, onChanged: null), - ), - const SizedBox(width: 150, child: Divider()) - ])); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/Title/node1_base.dart b/packages/widgets/lib/StatelessWidget/Title/node1_base.dart deleted file mode 100644 index 40287758e..000000000 --- a/packages/widgets/lib/StatelessWidget/Title/node1_base.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/7/22 -/// contact me by email 1981462002@qq.com -/// 说明: 208 Title 0 该组件用于描述app在操作系统中的名称,可以在应用栏列表里看到效果。MaterialApp中的title字段效果的根源是该组件。 -// { -// "widgetId": 208, -// "name": "Title基本使用", -// "priority": 1, -// "subtitle": "【title】 : 名称 【int】\n" -// "【color】: 颜色 【Color】\n" -// "【child】: 子组件 【Widget】", -// } -class TitleDemo extends StatelessWidget { - const TitleDemo({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Title( - title: '张风捷特烈', - color: const Color(0xFF9C27B0), - child: const Center( - child: Text('应用菜单栏中该页的名称为: 张风捷特烈'), - ), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/ToggleButtons/node1_single.dart b/packages/widgets/lib/StatelessWidget/ToggleButtons/node1_single.dart deleted file mode 100644 index 8b0c6533d..000000000 --- a/packages/widgets/lib/StatelessWidget/ToggleButtons/node1_single.dart +++ /dev/null @@ -1,44 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/27 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 33, -// "priority": 1, -// "name": "ToggleButtons单选切换", -// "subtitle": "【children】: 子组件集 【List】\n" -// "【borderWidth】: 边线宽 【double】\n" -// "【borderRadius】: 圆角 【BorderRadius】\n" -// "【isSelected】: 是否选中集 【List】\n" -// "【onPressed】: 点击事件 【Function(int)】", -// } -class CustomToggleButtons extends StatefulWidget { - const CustomToggleButtons({Key? key}) : super(key: key); - - @override - _CustomToggleButtonsState createState() => _CustomToggleButtonsState(); -} - -class _CustomToggleButtonsState extends State { - List _isSelected = [true, false, false]; - - @override - Widget build(BuildContext context) { - return ToggleButtons( - children: const [ - Icon(Icons.skip_previous), - Icon(Icons.pause), - Icon(Icons.skip_next), - ], - borderWidth: 1, - borderRadius: BorderRadius.circular(10), - isSelected: _isSelected, - onPressed: (value) => setState(() { - _isSelected = _isSelected.map((e) => false).toList(); - _isSelected[value] = true; - }), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/ToggleButtons/node2_color.dart b/packages/widgets/lib/StatelessWidget/ToggleButtons/node2_color.dart deleted file mode 100644 index 733409e25..000000000 --- a/packages/widgets/lib/StatelessWidget/ToggleButtons/node2_color.dart +++ /dev/null @@ -1,51 +0,0 @@ - - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/27 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 33, -// "priority": 2, -// "name": "ToggleButtons颜色属性", -// "subtitle": "【borderColor】: 边线色 【Color】\n" -// "【selectedBorderColor】: 选中边线色 【Color】\n" -// "【selectedColor】: 选中时组件色 【Color】\n" -// "【fillColor】: 选中时填充色 【Color】\n" -// "【splashColor】: 水波纹色 【Color】", -// } -class ColorToggleButtons extends StatefulWidget { - const ColorToggleButtons({Key? key}) : super(key: key); - - @override - _ColorToggleButtonsState createState() => _ColorToggleButtonsState(); -} - -class _ColorToggleButtonsState extends State { - List _isSelected = [true, false, false]; - - @override - Widget build(BuildContext context) { - return ToggleButtons( - children: const [ - Icon(Icons.skip_previous), - Icon(Icons.pause), - Icon(Icons.skip_next), - ], - borderWidth: 1, - borderColor: Colors.orangeAccent, - selectedBorderColor: Colors.blue, - splashColor: Colors.purple.withAlpha(66), - borderRadius: BorderRadius.circular(10), - selectedColor: Colors.red, - fillColor: Colors.green.withAlpha(11), - isSelected: _isSelected, - onPressed: (value) => setState(() { - _isSelected = _isSelected.map((e) => false).toList(); - _isSelected[value] = true; - }), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/ToggleButtons/node3_multi.dart b/packages/widgets/lib/StatelessWidget/ToggleButtons/node3_multi.dart deleted file mode 100644 index 8c52e113d..000000000 --- a/packages/widgets/lib/StatelessWidget/ToggleButtons/node3_multi.dart +++ /dev/null @@ -1,45 +0,0 @@ - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/27 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 33, -// "priority": 3, -// "name": "ToggleButtons多选切换", -// "subtitle": " 可以控制状态转化的逻辑来形成不同的效果。", -// } -class ProToggleButtons extends StatefulWidget { - const ProToggleButtons({Key? key}) : super(key: key); - - @override - _ProToggleButtonsState createState() => _ProToggleButtonsState(); -} - -class _ProToggleButtonsState extends State { - final List _isSelected = [false, false, false]; - - @override - Widget build(BuildContext context) { - return ToggleButtons( - children: const [ - Icon(Icons.skip_previous), - Icon(Icons.pause), - Icon(Icons.skip_next), - ], - borderWidth: 1, - borderColor: Colors.blue, - selectedBorderColor: Colors.orangeAccent, - splashColor: Colors.purple.withAlpha(66), - borderRadius: BorderRadius.circular(10), - selectedColor: Colors.red, - fillColor: Colors.green.withAlpha(11), - isSelected: _isSelected, - onPressed: (value) => setState(() { - _isSelected[value] = !_isSelected[value]; - }), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/UserAccountsDrawerHeader/node1_base.dart b/packages/widgets/lib/StatelessWidget/UserAccountsDrawerHeader/node1_base.dart deleted file mode 100644 index 64c4c7c7d..000000000 --- a/packages/widgets/lib/StatelessWidget/UserAccountsDrawerHeader/node1_base.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/27 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 22, -// "priority": 1, -// "name": "该组件的基本表现如下", -// "subtitle": "【currentAccountPicture】: 上组件 【Widget】\n" -// "【accountName】: 中组件 【Widget】\n" -// "【accountEmail】: 下组件 【Widget】\n" -// "【decoration】: 装饰 【Decoration】", -// } - -class CustomUAGHP extends StatelessWidget { - const CustomUAGHP({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return SizedBox( - width: MediaQuery.of(context).size.width / 3 * 2, - child: UserAccountsDrawerHeader( - accountName: Container( - padding: const EdgeInsets.all(8.0), - child: const Text( - "张风捷特烈", - style: - TextStyle(color: Colors.orangeAccent, fontSize: 22, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2, - ), - ]), - ), - ), - accountEmail: const Padding( - padding: EdgeInsets.all(8.0), - child: Text("1981462002@qq.com", - style: TextStyle(color: Colors.white, fontSize: 14, shadows: [ - Shadow( - color: Colors.orangeAccent, - offset: Offset(.5, .5), - blurRadius: 2), - ])), - ), - currentAccountPicture: Container( - padding: const EdgeInsets.all(15.0), - child: const CircleAvatar( - backgroundImage: AssetImage("assets/images/icon_head.webp"), - ), - ), - decoration: const BoxDecoration( - image: DecorationImage(image: AssetImage("assets/images/caver.webp")), - ), - ), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/UserAccountsDrawerHeader/node2_pro.dart b/packages/widgets/lib/StatelessWidget/UserAccountsDrawerHeader/node2_pro.dart deleted file mode 100644 index 834e33e3e..000000000 --- a/packages/widgets/lib/StatelessWidget/UserAccountsDrawerHeader/node2_pro.dart +++ /dev/null @@ -1,69 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/27 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 22, -// "priority": 2, -// "name": "右上角和底部", -// "subtitle": "【otherAccountsPictures】: 右上组件 【List】\n" -// "【onDetailsPressed】: 右下角点击事件 【Function()】\n" -// "【arrowColor】: 右下角按钮颜色 【Color】\n" -// "【margin】: 外边距 【EdgeInsetsGeometry】", -// } -class ProUAGHP extends StatelessWidget { - const ProUAGHP({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return SizedBox( - width: MediaQuery.of(context).size.width / 3 * 2, - child: UserAccountsDrawerHeader( - margin: const EdgeInsets.all(10), - accountName: Container( - padding: const EdgeInsets.all(8.0), - child: const Text( - "张风捷特烈", - style: - TextStyle(color: Colors.orangeAccent, fontSize: 22, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2, - ), - ]), - ), - ), - accountEmail: const Padding( - padding: EdgeInsets.all(8.0), - child: Text("1981462002@qq.com", - style: TextStyle(color: Colors.white, fontSize: 14, shadows: [ - Shadow( - color: Colors.orangeAccent, - offset: Offset(.5, .5), - blurRadius: 2, - ), - ])), - ), - currentAccountPicture: Container( - padding: const EdgeInsets.all(15.0), - child: const CircleAvatar( - backgroundImage: AssetImage("assets/images/icon_head.webp"), - ), - ), - otherAccountsPictures: const [ - FlutterLogo(textColor: Colors.green), - ], - onDetailsPressed: () { - - }, - arrowColor: Colors.white, - decoration: const BoxDecoration( - image: DecorationImage(image: AssetImage("assets/images/caver.webp")), - ), - ), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/VerticalDivider/node1_base.dart b/packages/widgets/lib/StatelessWidget/VerticalDivider/node1_base.dart deleted file mode 100644 index 434136f4c..000000000 --- a/packages/widgets/lib/StatelessWidget/VerticalDivider/node1_base.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020/4/27 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 35, -// "priority": 1, -// "name": "VerticalDivider颜色和粗细", -// "subtitle": "【color】: 颜色 【Color】\n" -// "【thickness】: 线粗细 【double】", -// } -class CustomVerticalDivider extends StatelessWidget { - const CustomVerticalDivider({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - List dataColor = [ - Colors.red, Colors.yellow, - Colors.blue, Colors.green]; - List dataThickness = [1.0, 2.0, 4.0, 6.0]; - Map data = Map.fromIterables(dataColor, dataThickness); - return SizedBox( - height: 150, - child: Row( - mainAxisSize: MainAxisSize.min, - children: dataColor - .map((e) => VerticalDivider( - color: e, - thickness: data[e], - )).toList(), - ), - ); - } -} \ No newline at end of file diff --git a/packages/widgets/lib/StatelessWidget/VerticalDivider/node2_height.dart b/packages/widgets/lib/StatelessWidget/VerticalDivider/node2_height.dart deleted file mode 100644 index 9943c9766..000000000 --- a/packages/widgets/lib/StatelessWidget/VerticalDivider/node2_height.dart +++ /dev/null @@ -1,43 +0,0 @@ - - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/27 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 35, -// "priority": 2, -// "name": "VerticalDivider宽度和空缺", -// "subtitle": "【indent】: 前面空缺长度 【double】\n" -// "【endIndent】: 后面空缺长度 【double】\n" -// "【width】: 占位宽 【double】", -// } -class WidthVerticalDivider extends StatelessWidget { - const WidthVerticalDivider({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - List dataColor = [ - Colors.red, Colors.yellow, - Colors.blue, Colors.green]; - List dataThickness = [10.0, 20.0, 30.0, 40.0]; - Map data = Map.fromIterables(dataColor, dataThickness); - return SizedBox( - height: 150, - child: Row( - mainAxisSize: MainAxisSize.min, - children: dataColor - .map((e) => VerticalDivider( - color: e, - indent:data[e], - endIndent: data[e]!*2, - width: data[e], - thickness: data[e]!/10, - )) - .toList(), - ), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/Visibility/node1_base.dart b/packages/widgets/lib/StatelessWidget/Visibility/node1_base.dart deleted file mode 100644 index 5d0bbe32f..000000000 --- a/packages/widgets/lib/StatelessWidget/Visibility/node1_base.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020/4/27 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 10, -// "priority": 1, -// "name": "根据visible控制内部子组件的显隐情况", -// "subtitle": "【visible】 : 是否显示 【bool】\n" -// "【child】: 孩子 【Widget】\n" -// "默认孩子隐藏时会失去原来所在区域。", -// } -class CustomVisibility extends StatelessWidget { - const CustomVisibility({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 10, - children: [ - _buildVisibility(true), - _buildVisibility(false), - ], - ); - } - - Widget _buildVisibility(bool visible) { - Widget box = Container( - height: 30, - width: 30, - color: Colors.blue, - ); - return Container( - width: 150, - height: 150 * 0.618, - color: Colors.cyanAccent.withAlpha(33), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - box, - Visibility( - visible: visible, - child: Container( - alignment: Alignment.center, - height: 80 * 0.618, - width: 80, - color: Colors.red, - child: const Text( - "visible\ntrue", - style: TextStyle(fontSize: 20), - ), - )), - box, - ], - ), - ); - } -} diff --git a/packages/widgets/lib/StatelessWidget/Visibility/node2_replacement.dart b/packages/widgets/lib/StatelessWidget/Visibility/node2_replacement.dart deleted file mode 100644 index d9ed102ba..000000000 --- a/packages/widgets/lib/StatelessWidget/Visibility/node2_replacement.dart +++ /dev/null @@ -1,63 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/27 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 10, -// "priority": 2, -// "name": "replacement可在隐藏时进行占位", -// "subtitle": "【replacement】 : 隐藏时的占位组件 【Widget】", -// } -class ReplacementVisibility extends StatelessWidget { - const ReplacementVisibility({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 10, - children: [ - _buildVisibility(true), - _buildVisibility(false), - ], - ); - } - - Widget _buildVisibility(bool visible) { - Widget box = Container( - height: 30, - width: 30, - color: Colors.blue, - ); - return Container( - width: 150, - height: 150 * 0.618, - color: Colors.cyanAccent.withAlpha(33), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - box, - Visibility( - visible: visible, - replacement: Container( - alignment: Alignment.center, - height: 80 * 0.618, - width: 80, - ), - child: Container( - alignment: Alignment.center, - height: 80 * 0.618, - width: 80, - color: Colors.red, - child: const Text( - "visible\ntrue", - style: TextStyle(fontSize: 20), - ), - )), - box, - ], - ), - ); - } -} diff --git a/packages/widgets/lib/exp/other_unit.dart b/packages/widgets/lib/exp/other_unit.dart deleted file mode 100644 index 8309e77f3..000000000 --- a/packages/widgets/lib/exp/other_unit.dart +++ /dev/null @@ -1,16 +0,0 @@ -/// create by 张风捷特烈 on 2020/4/28 -/// contact me by email 1981462002@qq.com -/// 说明: - -library other_unit.dart; - - -export '../Other/ErrorWidget/node1_base.dart'; -export '../Other/Table/node1_base.dart'; -export '../Other/RawImage/node1_base.dart'; -export '../Other/PerformanceOverlay/node1_base.dart'; -export '../Other/RenderObjectToWidgetAdapter/node1_base.dart'; -export '../Other/ListWheelViewport/node1_base.dart'; -export '../Other/ListWheelViewport/node2_perspective.dart'; -export '../Other/ListWheelViewport/node3_magnifier.dart'; -export '../Other/ListWheelViewport/node4_opacity.dart'; \ No newline at end of file diff --git a/packages/widgets/lib/exp/proxy_unit.dart b/packages/widgets/lib/exp/proxy_unit.dart deleted file mode 100644 index fdc1ec8ac..000000000 --- a/packages/widgets/lib/exp/proxy_unit.dart +++ /dev/null @@ -1,36 +0,0 @@ -/// create by 张风捷特烈 on 2020/4/28 -/// contact me by email 1981462002@qq.com -/// 说明: - -library proxy_widget_unit.dart; - - -export '../ProxyWidget/DropdownButtonHideUnderline/node1_base.dart'; -export '../ProxyWidget/Flexible/node1_base.dart'; -export '../ProxyWidget/MediaQuery/node1_base.dart'; -export '../ProxyWidget/ButtonTheme/node1_base.dart'; -export '../ProxyWidget/DefaultTextStyle/node1_base.dart'; -export '../ProxyWidget/SliderTheme/node1_base.dart'; -export '../ProxyWidget/SliderTheme/node2_diy.dart'; -export '../ProxyWidget/DividerTheme/node1_base.dart'; -export '../ProxyWidget/IconTheme/node1_base.dart'; -export '../ProxyWidget/ScrollConfiguration/node1_base.dart'; -export '../ProxyWidget/Expanded/node1_base.dart'; -export '../ProxyWidget/Positioned/node1_base.dart'; -export '../ProxyWidget/LayoutId/node1_base.dart'; -export '../ProxyWidget/ChipTheme/node1_base.dart' hide CustomFilterChip; -export '../ProxyWidget/ListTileTheme/node1_base.dart'; -export '../ProxyWidget/MaterialBannerTheme/node1_base.dart'; -export '../ProxyWidget/PopupMenuTheme/node1_base.dart'; -export '../ProxyWidget/ToggleButtonsTheme/node1_base.dart'; -export '../ProxyWidget/ButtonBarTheme/node1_base.dart'; -export '../ProxyWidget/TooltipTheme/node1_base.dart'; -export '../ProxyWidget/Directionality/node1_base.dart'; -export '../ProxyWidget/TableCell/node1_base.dart'; -export '../ProxyWidget/KeepAlive/node1_base.dart'; -export '../ProxyWidget/CupertinoUserInterfaceLevel/node1_base.dart'; -export '../ProxyWidget/InheritedTheme/node1_base.dart'; -export '../ProxyWidget/DefaultAssetBundle/node1_base.dart'; -export '../ProxyWidget/InheritedWidget/node1_base.dart'; -export '../ProxyWidget/ParentDataWidget/node1_base.dart'; -export '../ProxyWidget/PrimaryScrollController/node1_base.dart'; diff --git a/packages/widgets/lib/exp/render_object_unit.dart b/packages/widgets/lib/exp/render_object_unit.dart deleted file mode 100644 index 1128f4170..000000000 --- a/packages/widgets/lib/exp/render_object_unit.dart +++ /dev/null @@ -1,100 +0,0 @@ -library render_object_widget; - - -export '../MultiChildRenderObjectWidget/Flex/node1_direction.dart'; -export '../MultiChildRenderObjectWidget/Flex/node2_mainAxisAlignment.dart'; -export '../MultiChildRenderObjectWidget/Flex/node3_crossAxisAlignment.dart'; -export '../MultiChildRenderObjectWidget/Flex/node4_verticalDirection.dart'; -export '../MultiChildRenderObjectWidget/Flex/node5_textDirection.dart'; - -export '../MultiChildRenderObjectWidget/Flow/node1_base.dart'; -export '../MultiChildRenderObjectWidget/Flow/node2_anim.dart'; -export '../MultiChildRenderObjectWidget/RichText/node1_base.dart'; -export '../MultiChildRenderObjectWidget/RichText/node2_widget.dart'; -export '../MultiChildRenderObjectWidget/Stack/node1_base.dart'; -export '../MultiChildRenderObjectWidget/Stack/node2_positioned.dart'; -export '../MultiChildRenderObjectWidget/Wrap/node1_base.dart'; -export '../MultiChildRenderObjectWidget/Wrap/node2_alignment.dart'; -export '../MultiChildRenderObjectWidget/Wrap/node3_crossAxisAlignment.dart'; -export '../MultiChildRenderObjectWidget/Wrap/node4_textDirection.dart'; -export '../MultiChildRenderObjectWidget/Wrap/node5_verticalDirection.dart'; -export '../MultiChildRenderObjectWidget/Column/node1_base.dart'; -export '../MultiChildRenderObjectWidget/IndexedStack/node1_base.dart'; -export '../MultiChildRenderObjectWidget/Row/node1_base.dart'; -export '../MultiChildRenderObjectWidget/CustomMultiChildLayout/node1_base.dart'; -export '../MultiChildRenderObjectWidget/Viewport/node1_base.dart'; -export '../MultiChildRenderObjectWidget/ListBody/node1_base.dart'; -export '../MultiChildRenderObjectWidget/ShrinkWrappingViewport/node1_base.dart'; -export '../MultiChildRenderObjectWidget/NestedScrollViewViewport/node1_base.dart'; - -export '../SingleChildRenderObjectWidget/Align/node1_base.dart'; -export '../SingleChildRenderObjectWidget/Align/node2_other.dart'; -export '../SingleChildRenderObjectWidget/CustomSingleChildLayout/node1_base.dart'; -export '../SingleChildRenderObjectWidget/CustomSingleChildLayout/node2_offset.dart'; -export '../SingleChildRenderObjectWidget/RepaintBoundary/node1_base.dart'; -export '../SingleChildRenderObjectWidget/RepaintBoundary/node2_save.dart'; -export '../SingleChildRenderObjectWidget/CompositedTransformTarget/node1_base.dart'; -export '../SingleChildRenderObjectWidget/CompositedTransformFollower/node1_base.dart'; - -export '../SingleChildRenderObjectWidget/AnnotatedRegion/node1_base.dart'; -export '../SingleChildRenderObjectWidget/CupertinoTextSelectionToolbar/node1_base.dart'; -export '../SingleChildRenderObjectWidget/SizeChangedLayoutNotifier/node1_base.dart'; -export '../SingleChildRenderObjectWidget/ColoredBox/node1_base.dart'; - -export '../SingleChildRenderObjectWidget/ConstrainedBox/node1_base.dart'; -export '../SingleChildRenderObjectWidget/PhysicalModel/node1_base.dart'; -export '../SingleChildRenderObjectWidget/FractionalTranslation/node1_base.dart'; -export '../SingleChildRenderObjectWidget/ColorFiltered/node1_base.dart'; -export '../SingleChildRenderObjectWidget/Baseline/node1_base.dart'; -export '../SingleChildRenderObjectWidget/DecoratedBox/node1_base.dart'; -export '../SingleChildRenderObjectWidget/DecoratedBox/node2_image.dart'; -export '../SingleChildRenderObjectWidget/DecoratedBox/node3_border.dart'; -export '../SingleChildRenderObjectWidget/DecoratedBox/node4_shape.dart'; -export '../SingleChildRenderObjectWidget/DecoratedBox/node5_line.dart'; -export '../SingleChildRenderObjectWidget/DecoratedBox/node6_flutterLogo.dart'; -export '../SingleChildRenderObjectWidget/FadeTransition/node1_base.dart'; -export '../SingleChildRenderObjectWidget/FittedBox/node1_base.dart'; -export '../SingleChildRenderObjectWidget/FractionallySizedBox/node1_base.dart'; -export '../SingleChildRenderObjectWidget/LimiteBox/node1_base.dart'; -export '../SingleChildRenderObjectWidget/OffStage/node1_base.dart'; -export '../SingleChildRenderObjectWidget/Opacity/node1_base.dart'; -export '../SingleChildRenderObjectWidget/Padding/node1_all.dart'; -export '../SingleChildRenderObjectWidget/Padding/node2_only.dart'; -export '../SingleChildRenderObjectWidget/Padding/node3_symmetric.dart'; -export '../SingleChildRenderObjectWidget/RotatedBox/node1_base.dart'; -export '../SingleChildRenderObjectWidget/SizedBox/node1_base.dart'; -export '../SingleChildRenderObjectWidget/SizedOverflowBox/node1_base.dart'; -export '../SingleChildRenderObjectWidget/Transform/node1_skew.dart'; -export '../SingleChildRenderObjectWidget/Transform/node2_translation.dart'; -export '../SingleChildRenderObjectWidget/Transform/node3_scale.dart'; -export '../SingleChildRenderObjectWidget/Transform/node4_rotate.dart'; -export '../SingleChildRenderObjectWidget/Transform/node5_perspective.dart'; -export '../SingleChildRenderObjectWidget/UnConstrainedBox/node1_base.dart'; -export '../StatefulWidget/PositionedTransition/node1_base.dart'; -export '../StatefulWidget/RotationTransition/node1_base.dart'; -export '../StatefulWidget/ScaleTransition/node1_base.dart'; -export '../StatefulWidget/SizeTransition/node1_base.dart'; -export '../SingleChildRenderObjectWidget/ClipOval/node1_base.dart'; -export '../SingleChildRenderObjectWidget/ClipPath/node1_base.dart'; -export '../SingleChildRenderObjectWidget/ClipRRect/node1_base.dart'; -export '../SingleChildRenderObjectWidget/ClipRect/node1_base.dart'; -export '../SingleChildRenderObjectWidget/OverflowBox/node1_base.dart'; -export '../SingleChildRenderObjectWidget/AspectRatio/node1_base.dart'; -export '../SingleChildRenderObjectWidget/AnimatedSize/node1_base.dart'; -export '../SingleChildRenderObjectWidget/Center/node1_base.dart'; -export '../SingleChildRenderObjectWidget/CustomPaint/node2_bezier.dart'; -export '../SingleChildRenderObjectWidget/CustomPaint/node1_clock.dart'; -export '../SingleChildRenderObjectWidget/AbsorbPointer/node1_base.dart'; -export '../SingleChildRenderObjectWidget/BackdropFilter/node1_base.dart'; -export '../SingleChildRenderObjectWidget/IgnorePointer/node1_base.dart'; -export '../SingleChildRenderObjectWidget/LayoutBuilder/node1_base.dart'; -export '../SingleChildRenderObjectWidget/LayoutBuilder/node2_fit.dart'; -export '../SingleChildRenderObjectWidget/LayoutBuilder/node3_expend.dart'; -export '../SingleChildRenderObjectWidget/ShaderMask/node1_radial.dart'; -export '../SingleChildRenderObjectWidget/ShaderMask/node2_linear.dart'; -export '../SingleChildRenderObjectWidget/IntrinsicHeight/node1_base.dart'; -export '../SingleChildRenderObjectWidget/IntrinsicWidth/node1_base.dart'; -export '../SingleChildRenderObjectWidget/PhysicalShape/node1_base.dart'; -export '../SingleChildRenderObjectWidget/ImageFiltered/node1_blur.dart'; -export '../SingleChildRenderObjectWidget/ImageFiltered/node2_color.dart'; -export '../SingleChildRenderObjectWidget/ImageFiltered/node3_matrix.dart'; \ No newline at end of file diff --git a/packages/widgets/lib/exp/sliver_unit.dart b/packages/widgets/lib/exp/sliver_unit.dart deleted file mode 100644 index 5e2359607..000000000 --- a/packages/widgets/lib/exp/sliver_unit.dart +++ /dev/null @@ -1,32 +0,0 @@ -/// create by 张风捷特烈 on 2020/4/28 -/// contact me by email 1981462002@qq.com -/// 说明: - -library sliver_unit.dart; - -export '../Sliver/CustomScrollView/node1_base.dart'; -export '../Sliver/FlexibleSpaceBar/node1_base.dart'; -export '../Sliver/SliverAppBar/node1_base.dart'; -export '../Sliver/SliverFillViewport/node1_base.dart'; -export '../Sliver/SliverFixedExtentList/node1_base.dart'; -export '../Sliver/SliverGrid/node1_base.dart'; -export '../Sliver/SliverList/node1_base.dart'; -export '../Sliver/SliverOpacity/node1_base.dart'; -export '../Sliver/SliverPadding/node1_base.dart'; -export '../Sliver/SliverPersistentHeader/node1_base.dart'; -export '../Sliver/SliverToBoxAdapter/node1_base.dart'; -export '../Sliver/SliverOverlapAbsorber/node1_base.dart'; -export '../Sliver/SliverOverlapInjector/node1_base.dart'; -export '../Sliver/SliverPrototypeExtentList/node1_base.dart'; -export '../Sliver/CupertinoSliverNavigationBar/node1_base.dart'; -export '../Sliver/CupertinoSliverRefreshControl/node1_base.dart'; -export '../Sliver/SliverFillRemaining/node1_base.dart'; -export '../Sliver/SliverIgnorePointer/node1_base.dart'; -export '../Sliver/SliverAnimatedList/node1_base.dart'; -export '../Sliver/SliverLayoutBuilder/node1_base.dart'; -export '../Sliver/SliverWithKeepAliveWidget/node1_base.dart'; -export '../Sliver/DecoratedSliver/node1.dart'; -export '../Sliver/SliverConstrainedCrossAxis/node1.dart'; -export '../Sliver/SliverCrossAxisExpanded/node1.dart'; -export '../Sliver/SliverCrossAxisGroup/node1.dart'; -export '../Sliver/SliverMainAxisGroup/node1.dart'; diff --git a/packages/widgets/lib/exp/stateful_unit.dart b/packages/widgets/lib/exp/stateful_unit.dart deleted file mode 100644 index aa7f85748..000000000 --- a/packages/widgets/lib/exp/stateful_unit.dart +++ /dev/null @@ -1,181 +0,0 @@ -library stateful_unit; - -export '../StatefulWidget/CupertinoButton/node1_base.dart'; -export '../StatefulWidget/CupertinoContextMenu/node1_base.dart'; -export '../StatefulWidget/CupertinoContextMenuAction/node1_base.dart'; -export '../StatefulWidget/DateRangePickerDialog/node1_base.dart'; -export '../StatefulWidget/DateRangePickerDialog/node2_diy.dart'; - -export '../StatefulWidget/CupertinoDatePicker/node1_base.dart'; -export '../StatefulWidget/CupertinoPicker/node1_base.dart'; -export '../StatefulWidget/CupertinoTimerPicker/node1_base.dart'; -export '../StatefulWidget/SlideTransition/node1_base.dart'; -export '../StatefulWidget/MonthPicker/node1_base.dart'; -export '../StatefulWidget/YearPicker/node1_base.dart'; -export '../StatefulWidget/WillPopScope/node1_base.dart'; -export '../StatefulWidget/NestedScrollView/node1_base.dart'; -export '../StatefulWidget/AppBar/node1_base.dart'; -export '../StatefulWidget/AppBar/node2_tab.dart'; -export '../StatefulWidget/BottomAppBar/node1_base.dart'; -export '../StatefulWidget/BottomNavigationBar/node1_base.dart'; -export '../StatefulWidget/BottomNavigationBar/node2_page.dart'; -export '../StatefulWidget/Checkbox/node1_base.dart'; -export '../StatefulWidget/Checkbox/node2_tristate.dart'; -export '../StatefulWidget/ExpandIcon/node1_base.dart'; -export '../StatefulWidget/ExpansionTile/node1_base.dart'; -export '../StatefulWidget/Radio/node1_base.dart'; -export '../StatefulWidget/Tooltip/node1_base.dart'; -export '../StatefulWidget/Tooltip/node2_decoration.dart'; -export '../StatefulWidget/CircularProgressIndicator/node1_base.dart'; -export '../StatefulWidget/CupertinoActivityIndicator/node1_base.dart'; -export '../StatefulWidget/CupertinoSlider/node1_base.dart'; -export '../StatefulWidget/CupertinoSwitch/node1_base.dart'; -export '../StatefulWidget/CupertinoSegmentedControl/node1_base.dart'; -export '../StatefulWidget/CupertinoSegmentedControl/node2_color.dart'; -export '../StatefulWidget/Navigator/node1_base.dart'; -export '../StatefulWidget/InteractiveViewer/node1_base.dart'; -export '../StatefulWidget/InteractiveViewer/node2_constrained.dart'; -export '../StatefulWidget/InteractiveViewer/node3_controller.dart'; - -export '../StatefulWidget/Image/node1_base.dart'; -export '../StatefulWidget/Image/node2_fit.dart'; -export '../StatefulWidget/Image/node3_alignment.dart'; -export '../StatefulWidget/Image/node4_colorBlendMode.dart'; -export '../StatefulWidget/Image/node5_repeat.dart'; -export '../StatefulWidget/Image/node6_centerSlice.dart'; -export '../StatefulWidget/Image/node6_centerSlice.dart'; - -export '../StatefulWidget/RangeSlider/node1_base.dart'; -export '../StatefulWidget/Slider/node1_base.dart'; -export '../StatefulWidget/Slider/node2_lable.dart'; -export '../StatefulWidget/Switch/node1_base.dart'; -export '../StatefulWidget/Switch/node2_image.dart'; -export '../StatefulWidget/StatefulBuilder/node1_base.dart'; -export '../StatefulWidget/TextField/node3_decoration.dart'; -export '../StatefulWidget/RefreshIndicator/node1_base.dart'; -export '../StatefulWidget/SelectableText/node1_base.dart'; -export '../StatefulWidget/SelectableText/node2_align.dart'; -export '../StatefulWidget/CupertinoNavigationBar/node1_base.dart'; -export '../StatefulWidget/CupertinoTabBar/node1_base.dart'; -export '../StatefulWidget/CupertinoTextField/node1_base.dart'; -export '../StatefulWidget/CupertinoTextField/node2_style.dart'; -export '../StatefulWidget/ValueListenableBuilder/node1_base.dart'; -export '../StatefulWidget/MouseRegion/node1_base.dart'; -export '../StatefulWidget/Scrollable/node1_base.dart'; - -export '../StatefulWidget/DropdownButton/node1_base.dart'; -export '../StatefulWidget/DropdownButton/node2_style.dart'; - -export '../StatefulWidget/AnimatedCrossFade/node1_base.dart'; -export '../StatefulWidget/AnimatedCrossFade/node2_curve.dart'; - -export '../StatefulWidget/AnimatedList/node1_base.dart'; -export '../StatefulWidget/AnimatedSwitcher/node1_base.dart'; -export '../StatefulWidget/AlignTransition/node1_base.dart'; -export '../StatefulWidget/DecoratedBoxTransition/node1_base.dart'; -export '../StatefulWidget/DefaultTextStyleTransition/node1_base.dart'; -export '../StatefulWidget/RelativePositionedTransition/node1_base.dart'; -export '../StatefulWidget/CupertinoScrollbar/node1_base.dart'; -export '../StatefulWidget/RawGestureDetector/node1_base.dart'; - -export '../StatefulWidget/Dismissible/node1_base.dart'; -export '../StatefulWidget/AutomaticKeepAlive/node1_base.dart'; -export '../StatefulWidget/AnimatedModalBarrier/node1_base.dart'; -export '../StatefulWidget/FormField/node1_base.dart'; -export '../StatefulWidget/AnimatedBuilder/node1_base.dart'; -export '../StatefulWidget/TweenAnimationBuilder/node1_base.dart'; -export '../StatefulWidget/PaginatedDataTable/node1_base.dart'; -export '../StatefulWidget/RawKeyboardListener/node1_base.dart'; -export '../StatefulWidget/Dismissible/node2_direction.dart'; - -export '../StatefulWidget/DragTarget/node1_base.dart'; -export '../StatefulWidget/Draggable/node1_base.dart'; -export '../StatefulWidget/Draggable/node2_data.dart'; -export '../StatefulWidget/Draggable/node3_use.dart'; - -export '../StatefulWidget/Form/node1_base.dart'; -export '../StatefulWidget/StatusTransitionWidget/node1_base.dart'; -export '../StatefulWidget/UniqueWidget/node1_base.dart'; -export '../StatefulWidget/FutureBuilder/node1_base.dart'; -export '../StatefulWidget/Hero/node1_base.dart'; -export '../StatefulWidget/AnimatedAlign/node1_base.dart'; -export '../StatefulWidget/AnimatedContainer/node1_base.dart'; -export '../StatefulWidget/AnimatedDefaultTextStyle/node1_base.dart'; -export '../StatefulWidget/AnimatedOpacity/node1_base.dart'; -export '../StatefulWidget/AnimatedPadding/node1_base.dart'; -export '../StatefulWidget/AnimatedPositioned/node1_base.dart'; -export '../StatefulWidget/AnimatedPositionedDirectional/node1_base.dart'; - -export '../StatefulWidget/ExpansionPanelList/node1_base.dart'; -export '../StatefulWidget/DropdownButtonFormField/node1_base.dart'; - -export '../StatefulWidget/Ink/node1_base.dart'; -export '../StatefulWidget/Ink/node2_image.dart'; - -export '../StatefulWidget/InkResponse/node1_base.dart'; -export '../StatefulWidget/InkResponse/node2_color.dart'; - -export '../StatefulWidget/InkWell/node1_base.dart'; -export '../StatefulWidget/InkWell/node2_color.dart'; - -export '../StatefulWidget/LicensePage/node1_base.dart'; -export '../StatefulWidget/ListWheelScrollView/node1_base.dart'; -export '../StatefulWidget/LongPressDraggable/node1_base.dart'; -export '../StatefulWidget/Material/node1_base.dart'; -export '../StatefulWidget/Material/node2_shape.dart'; -export '../StatefulWidget/Overlay/node1_base.dart'; -export '../StatefulWidget/PageView/node1_base.dart'; -export '../StatefulWidget/PageView/node2_direction.dart'; -export '../StatefulWidget/PageView/node3_controller.dart'; -export '../StatefulWidget/PopupMenuButton/node1_base.dart'; -export '../StatefulWidget/PopupMenuDivider/node1_base.dart'; -export '../StatefulWidget/RawChip/node1_press.dart'; -export '../StatefulWidget/RawChip/node2_select.dart'; - -export '../StatefulWidget/RawMaterialButton/node1_base.dart'; -export '../StatefulWidget/RawMaterialButton/node2_shape.dart'; -export '../StatefulWidget/ReorderableListView/node1_base.dart'; -export '../StatefulWidget/ReorderableListView/node2_direction.dart'; -export '../StatefulWidget/Scrollbar/node1_base.dart'; -export '../StatefulWidget/Stepper/node1_base.dart'; -export '../StatefulWidget/Stepper/node2_type.dart'; -export '../StatefulWidget/StreamBuilder/node1_base.dart'; -export '../StatefulWidget/TableRowInkWell/node1_base.dart'; -export '../StatefulWidget/TextField/node1_base.dart'; -export '../StatefulWidget/TextField/node2_cursor.dart'; -export '../StatefulWidget/TextField/node3_decoration.dart'; - -export '../StatefulWidget/TextFormField/node1_base.dart'; -export '../StatefulWidget/LinearProgressIndicator/node1_base.dart'; -export '../StatefulWidget/CupertinoApp/node1_base.dart'; -export '../StatefulWidget/CupertinoPageScaffold/node1_base.dart'; -export '../StatefulWidget/CupertinoTabScaffold/node1_base.dart'; -export '../StatefulWidget/MaterialApp/node1_base.dart'; -export '../StatefulWidget/Scaffold/node1_base.dart'; -export '../StatefulWidget/TabBarView/node1_base.dart'; -export '../StatefulWidget/InputDecorator/node1_base.dart'; -export '../StatefulWidget/EditableText/node1_base.dart'; -export '../StatefulWidget/CupertinoSlidingSegmentedControl/node1_base.dart'; -export '../StatefulWidget/WidgetsApp/node1_base.dart' hide HomePage; -export '../StatefulWidget/WidgetInspector/node1_base.dart' hide HomePage; -export '../StatefulWidget/AnimatedTheme/node1_base.dart'; -export '../StatefulWidget/AnimatedPhysicalModel/node1_base.dart'; -export '../StatefulWidget/DefaultTabController/node1_base.dart'; -export '../StatefulWidget/GlowingOverscrollIndicator/node1_base.dart'; -export '../StatefulWidget/DraggableScrollableSheet/node1_base.dart'; -export '../StatefulWidget/DrawerController/node1_base.dart'; -export '../StatefulWidget/MergeableMaterial/node1_base.dart'; -export '../StatefulWidget/CupertinoTabView/node1_base.dart'; - -export '../StatefulWidget/TextButton/node1_base.dart'; -export '../StatefulWidget/TextButton/node2_style.dart'; - -export '../StatefulWidget/ElevatedButton/node1_base.dart'; -export '../StatefulWidget/ElevatedButton/node2_style.dart'; - -export '../StatefulWidget/OutlinedButton//node1_base.dart'; -export '../StatefulWidget/OutlinedButton/node2_style.dart'; - -export '../StatefulWidget/NavigationRail/node1_base.dart'; -export '../StatefulWidget/NavigationRail/node2_extend.dart'; -export '../StatefulWidget/NavigationRail/node3_dark.dart'; \ No newline at end of file diff --git a/packages/widgets/lib/exp/stateless_unit.dart b/packages/widgets/lib/exp/stateless_unit.dart deleted file mode 100644 index bd53f535c..000000000 --- a/packages/widgets/lib/exp/stateless_unit.dart +++ /dev/null @@ -1,213 +0,0 @@ -library stateless_unit; - -export '../StatelessWidget/AboutDialog/node1_base.dart'; -export '../StatelessWidget/AboutListTile/node1_base.dart'; -export '../StatelessWidget/ActionChip/node1_base.dart'; -export '../StatelessWidget/AlertDialog/node1_base.dart'; -export '../StatelessWidget/AnimatedIcon/node1_base.dart'; -export '../StatelessWidget/CheckedModeBanner/node1_base.dart'; -export '../StatelessWidget/Card/node1_base.dart'; -export '../StatelessWidget/Card/node2_shape.dart'; -export '../StatelessWidget/PreferredSize/node1_base.dart'; -export '../StatelessWidget/PreferredSize/node2_adapter.dart'; -export '../StatelessWidget/Builder/node1_base.dart'; -export '../StatelessWidget/NavigationToolbar/node1_base.dart'; -export '../StatelessWidget/CupertinoDialogAction/node1_base.dart'; -export '../StatelessWidget/DraggableScrollableActuator/node1_base.dart'; - -export '../StatelessWidget/CheckboxListTile/node1_base.dart'; -export '../StatelessWidget/CheckboxListTile/node2_select.dart'; -export '../StatelessWidget/CheckboxListTile/node3_dense.dart'; - -export '../StatelessWidget/Chip/node1_base.dart'; -export '../StatelessWidget/Chip/node2_color.dart'; -export '../StatelessWidget/Chip/node3_delete.dart'; - -export '../StatelessWidget/Container/node1_base.dart'; -export '../StatelessWidget/Container/node2_child.dart'; -export '../StatelessWidget/Container/node3_alignment.dart'; -export '../StatelessWidget/Container/node4_decoration.dart'; -export '../StatelessWidget/Container/node5_transform.dart'; -export '../StatelessWidget/Container/node6_constraints.dart'; -export '../StatelessWidget/MaterialBanner/node1_one_btn.dart'; -export '../StatelessWidget/MaterialBanner/node2_two_btn.dart'; -export '../StatelessWidget/SafeArea/node1_base.dart'; - -export '../StatelessWidget/Badge/node_1.dart'; -export '../StatelessWidget/Badge/node_2.dart'; -export '../StatelessWidget/Badge/node_3.dart'; - -export '../StatelessWidget/BackButtonIcon/node1_base.dart'; -export '../StatelessWidget/DrawerButtonIcon/node1_base.dart'; -export '../StatelessWidget/CloseButtonIcon/node1_base.dart'; -export '../StatelessWidget/EndDrawerButtonIcon/node1_base.dart'; - -export '../StatelessWidget/EndDrawerButton/node1_base.dart'; -export '../StatelessWidget/DrawerButton/node1_base.dart'; -export '../StatelessWidget/CloseButton/node1_base.dart'; -export '../StatelessWidget/BackButton/node1_base.dart'; - - -export '../StatelessWidget/CupertinoFullscreenDialogTransition/node1_base.dart'; -export '../StatelessWidget/CupertinoPageTransition/node1_base.dart'; -export '../StatelessWidget/HtmlElementView/node1_base.dart'; - -export '../StatelessWidget/DataTable/node1_base.dart'; -export '../StatelessWidget/DataTable/node2_operation.dart'; -export '../StatelessWidget/OrientationBuilder/node1_base.dart'; -export '../StatelessWidget/Title/node1_base.dart'; -export '../StatelessWidget/TabPageSelector/node1_base.dart'; -export '../StatelessWidget/TabPageSelectorIndicator/node1_base.dart'; -export '../StatelessWidget/CupertinoNavigationBarBackButton/node1_base.dart'; - -export '../StatelessWidget/CupertinoTheme/node1_base.dart'; -export '../StatelessWidget/CupertinoTheme/node2_use.dart'; - -export '../StatelessWidget/CupertinoPopupSurface/node1_base.dart'; -export '../StatelessWidget/Divider/node1_base.dart'; -export '../StatelessWidget/Divider/node2_height.dart'; - -export '../StatelessWidget/ScrollView/node1_base.dart'; -export '../StatelessWidget/ModalBarrier/node1_base.dart'; -export '../StatelessWidget/BoxScrollView/node1_base.dart'; -export '../StatelessWidget/FloatingActionButton/node1_base.dart'; -export '../StatelessWidget/FloatingActionButton/node2_mini.dart'; -export '../StatelessWidget/FloatingActionButton/node3_shape.dart'; -export '../StatelessWidget/GestureDetector/node1_base.dart'; -export '../StatelessWidget/GestureDetector/node2_tap.dart'; -export '../StatelessWidget/GestureDetector/node3_pan.dart'; - -export '../StatelessWidget/Banner/node1_base.dart'; -export '../StatelessWidget/ImageIcon/node1_base.dart'; -export '../StatelessWidget/FadeInImage/node1_base.dart'; -export '../StatelessWidget/CircleAvatar/node1_base.dart'; - - -export '../StatelessWidget/InputChip/node1_base.dart'; -export '../StatelessWidget/InputChip/node2_select.dart'; - - -export '../StatelessWidget/Visibility/node1_base.dart'; -export '../StatelessWidget/Visibility/node2_replacement.dart'; - -export '../StatelessWidget/ChoiceChip/node1_base.dart'; - -export '../StatelessWidget/FilterChip/node1_base.dart'; - - -export '../StatelessWidget/ListTile/node1_base.dart'; -export '../StatelessWidget/ListTile/node2_select.dart'; -export '../StatelessWidget/ListTile/node3_dense.dart'; - - -export '../StatelessWidget/ListView/node1_base.dart'; -export '../StatelessWidget/ListView/node2_direction.dart'; -export '../StatelessWidget/ListView/node3_builder.dart'; -export '../StatelessWidget/ListView/node4_separated.dart'; - -export '../StatelessWidget/MaterialButton/node1_base.dart'; -export '../StatelessWidget/MaterialButton/node2_onLongPress.dart'; -export '../StatelessWidget/MaterialButton/node3_shape.dart'; - -export '../StatelessWidget/GridTileBar/node1_base.dart'; -export '../StatelessWidget/GridTile/node1_base.dart'; - -export '../StatelessWidget/Icon/node1_base.dart'; -export '../StatelessWidget/Icon/node2_diy.dart'; - -export '../StatelessWidget/Placeholder/node1_base.dart'; -export '../StatelessWidget/Placeholder/node2_fallback.dart'; - -export '../StatelessWidget/RadioListTile/node1_base.dart'; -export '../StatelessWidget/RadioListTile/node2_dense.dart' show DenseRadioListTile; - -export '../StatelessWidget/SingleChildScrollView/node1_base.dart'; -export '../StatelessWidget/SingleChildScrollView/node2_direction.dart'; - -export '../StatelessWidget/TabBar/node1_base.dart'; -export '../StatelessWidget/TabBar/node2_noShadow.dart'; - - - -export '../StatelessWidget/ButtonBar/node1_base.dart'; -export '../StatelessWidget/ButtonBar/node2_padding.dart'; - - -export '../StatelessWidget/FlatButton/node1_base.dart'; - -export '../StatelessWidget/FlutterLogo/node1_base.dart'; -export '../StatelessWidget/FlutterLogo/node2_style.dart'; - -export '../StatelessWidget/IconButton/node1_base.dart'; -export '../StatelessWidget/OutlineButton/node1_base.dart'; -export '../StatelessWidget/RaisedButton/node1_base.dart'; - -export '../StatelessWidget/Drawer/node1_base.dart'; -export '../StatelessWidget/DrawerHeader/node1_base.dart'; - -export '../StatelessWidget/UserAccountsDrawerHeader/node1_base.dart'; -export '../StatelessWidget/UserAccountsDrawerHeader/node2_pro.dart'; - - -export '../StatelessWidget/VerticalDivider/node1_base.dart'; -export '../StatelessWidget/VerticalDivider/node2_height.dart'; - - -export '../StatelessWidget/GirdView/node1_base.dart'; -export '../StatelessWidget/GirdView/node2_direction.dart'; -export '../StatelessWidget/GirdView/node3_extend.dart'; -export '../StatelessWidget/GirdView/node4_builder.dart'; - -export '../StatelessWidget/GridPager/node1_base.dart'; -export '../StatelessWidget/GridPager/node2_divisions.dart'; - -export '../StatelessWidget/Spacer/node1_base.dart'; -export '../StatelessWidget/Spacer/node2_flex.dart'; - -export '../StatelessWidget/SwitchListTile/node1_base.dart'; -export '../StatelessWidget/SwitchListTile/node2_select.dart'; -export '../StatelessWidget/SwitchListTile/node3_dense.dart'; - -export '../StatelessWidget/Listener/node1_base.dart'; -export '../StatelessWidget/PositionedDirectional/node1_base.dart'; - -export '../StatelessWidget/Tab/node1_base.dart'; -export '../StatelessWidget/Theme/node1_base.dart'; -export '../StatelessWidget/Theme/node2_use.dart'; - -export '../StatelessWidget/ToggleButtons/node1_single.dart'; -export '../StatelessWidget/ToggleButtons/node2_color.dart'; -export '../StatelessWidget/ToggleButtons/node3_multi.dart'; - - -export '../StatelessWidget/BottomSheet/node1_base.dart'; -export '../StatelessWidget/CupertinoActionSheet/node1_base.dart'; -export '../StatelessWidget/CupertinoActionSheetAction/node1_base.dart'; -export '../StatelessWidget/CupertinoAlertDialog/node1_base.dart'; - -export '../StatelessWidget/Text/node1_base.dart'; -export '../StatelessWidget/Text/node2_shadows.dart'; -export '../StatelessWidget/Text/node3_decoration.dart'; -export '../StatelessWidget/Text/node4_textAlign.dart'; -export '../StatelessWidget/Text/node5_textDirection.dart'; -export '../StatelessWidget/Text/node6_softWrap.dart'; - - -export '../StatelessWidget/DayPicker/node1_base.dart'; -export '../StatelessWidget/Dialog/node1_base.dart'; - -export '../StatelessWidget/SimpleDialog/node1_base.dart'; -export '../StatelessWidget/SimpleDialogOption/node1_base.dart'; -export '../StatelessWidget/SnackBar/node1_base.dart'; -export '../StatelessWidget/SnackBarAction/node1_base.dart'; - -export '../StatelessWidget/PageStorage/node1_base.dart'; -export '../StatelessWidget/NotificationListener/node1_base.dart'; -export '../StatelessWidget/NotificationListener/node2_update.dart'; - -export '../StatelessWidget/Autocomplete/node1_base.dart'; -export '../StatelessWidget/Autocomplete/node2_type.dart'; -export '../StatelessWidget/RawMagnifier/node1_base.dart'; -export '../StatelessWidget/RawMagnifier/node2_shape.dart'; - - diff --git a/packages/widgets/lib/utils/color_utils.dart b/packages/widgets/lib/utils/color_utils.dart deleted file mode 100644 index 98b1151bc..000000000 --- a/packages/widgets/lib/utils/color_utils.dart +++ /dev/null @@ -1,75 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; - -class RandomProvider{ - RandomProvider._();//私有化构造 - static final _random= Random(); - static Random get random =>_random; -} - -class ColorUtils { - static Color randomColor({ - int limitA = 120, - int limitR = 0, - int limitG = 0, - int limitB = 0, - }) { - Random random = RandomProvider.random; - int a = limitA + random.nextInt(256 - limitA); //透明度值 - int r = limitR + random.nextInt(256 - limitR); //红值 - int g = limitG + random.nextInt(256 - limitG); //绿值 - int b = limitB + random.nextInt(256 - limitB); //蓝值 - return Color.fromARGB(a, r, g, b); //生成argb模式的颜色 - } - - - /// 使用方法: - /// var color1=ColorUtils.parse("#33428A43"); - /// var color2=ColorUtils.parse("33428A43"); - /// var color3=ColorUtils.parse("#428A43"); - ///var color4=ColorUtils.parse("428A43"); - /// - static Color parse(String code) { - Color result =Colors.red; - int value = 0 ; - if (code.contains("#")) { - try { - value = int.parse(code.substring(1), radix: 16); - } catch (e) { - print(e); - } - switch (code.length) { - case 1 + 6://6位 - result = Color(value + 0xFF000000); - break; - case 1 + 8://8位 - result = Color(value); - break; - default: - result =Colors.red; - } - }else { - try { - value = int.parse(code, radix: 16); - } catch (e) { - print(e); - } - switch (code.length) { - case 6: - result = Color(value + 0xFF000000); - break; - case 8: - result = Color(value); - break; - default: - result =Colors.red; - } - } - return result; - } - - static String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} - diff --git a/packages/widgets/lib/utils/dialog_about.dart b/packages/widgets/lib/utils/dialog_about.dart deleted file mode 100644 index c20a42a1c..000000000 --- a/packages/widgets/lib/utils/dialog_about.dart +++ /dev/null @@ -1,62 +0,0 @@ -import 'package:app/app.dart'; -import 'package:flutter/material.dart'; - -class DialogAbout extends StatelessWidget { - const DialogAbout({Key? key}) : super(key: key); - - static show(BuildContext context){ - showDialog(//内置方法,创建对话弹框 - context: context, - builder: (_) => const DialogAbout()); - } - @override - Widget build(BuildContext context) { - Widget title = Row( - //标题 - children: [ - Image.asset( - "assets/images/icon_head.webp", - width: 30, - height: 30, - ), - const SizedBox( - width: 10, - ), - const Expanded(child: Text("关于",style: TextStyle(fontSize: 18),)), - InkWell( - child: const Icon(Icons.close), - onTap: ()=>Navigator.of(context).pop(), - ) - ], - ); - Widget content = Column( - //内容 - mainAxisSize: MainAxisSize.min, - children: const [ -// Image.asset( -// "assets/images/icon_flutter.png", -// width: 50, -// ), - FlutterLogo(size: 50,), - SizedBox( - height: 20, - ), - Text( - "Flutter Unit ${StrUnit.version}", - ), - ]); - return AlertDialog(title: title, content: content, actions: [ - //左下角 - Padding( - padding: const EdgeInsets.only(right:15.0,bottom: 10,top: 10), - child: Column( - children: const[ - Text( - "Power By GF·J·Toly\n张风捷特烈", - textAlign: TextAlign.center, - ), - ], - )) - ]); - } -} diff --git a/packages/widgets/lib/utils/pather.dart b/packages/widgets/lib/utils/pather.dart deleted file mode 100644 index 6953fdf34..000000000 --- a/packages/widgets/lib/utils/pather.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/cupertino.dart'; - -class Pather { - Pather._(); - - static Pather create = Pather._(); - - final Path _path = Path(); - - Path nStarPath(int num, double R, double r, {dx = 0, dy = 0}) { - _path.reset();//重置路径 - double perRad = 2 * pi / num;//每份的角度 - double radA = perRad / 2 / 2;//a角 - double radB = 2 * pi / (num - 1) / 2 - radA / 2 + radA;//起始b角 - _path.moveTo(cos(radA) * R + dx, -sin(radA) * R + dy);//移动到起点 - for (int i = 0; i < num; i++) {//循环生成点,路径连至 - _path.lineTo(cos(radA + perRad * i) * R + dx, -sin(radA + perRad * i) * R + dy); - _path.lineTo(cos(radB + perRad * i) * r + dx, -sin(radB + perRad * i) * r + dy); - } - _path.close(); - return _path; - } -} diff --git a/packages/widgets/lib/widgets.dart b/packages/widgets/lib/widgets.dart deleted file mode 100644 index 27ce957fd..000000000 --- a/packages/widgets/lib/widgets.dart +++ /dev/null @@ -1,3 +0,0 @@ -library widgets; - -export 'widgets_map.dart'; \ No newline at end of file diff --git a/packages/widgets/lib/widgets_map.dart b/packages/widgets/lib/widgets_map.dart deleted file mode 100644 index b5c796267..000000000 --- a/packages/widgets/lib/widgets_map.dart +++ /dev/null @@ -1,1469 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'exp/other_unit.dart'; -import 'exp/proxy_unit.dart'; -import 'exp/render_object_unit.dart'; -import 'exp/sliver_unit.dart'; -import 'exp/stateful_unit.dart'; -import 'exp/stateless_unit.dart'; - -/// AUTO CREATED BY [张风捷特烈] , DON'T MODIFY! - -class WidgetsMap { - static List map(String name) { - switch (name) { - case "Container": - return [ - const CustomContainer(), - const ContainerWithChild(), - const ContainerAlignment(), - ContainerDecoration(), - const ContainerTransform(), - const ContainerConstraints() - ]; - case "CupertinoSegmentedControl": - return [ - const CupertinoSegmentedControlDemo(), - const CupertinoSegmentedControlColor() - ]; - case "ImageFiltered": - return [ - const ImageFilteredBlur(), - const ImageFilteredColor(), - const ImageFilteredMatrix() - ]; - case "Autocomplete": - return [const AutocompleteDemo(), const AutocompleteType()]; - case "Badge": - return [ - const BadgeDemo(), - const BadgeLabelDemo(), - const BadgeAlignOffsetDemo(), - ]; - case "DateRangePickerDialog": - return [ - const DateRangePickerDialogDemo(), - const DiyDateRangePickerDialogDemo() - ]; - case "CompositedTransformTarget": - return [ - const CompositedTransformTargetDemo(), - ]; - case "CompositedTransformFollower": - return [ - const CompositedTransformFollowerDemo(), - ]; - case "PrimaryScrollController": - return [ - const PrimaryScrollControllerDemo(), - ]; - case "CupertinoFullscreenDialogTransition": - return [ - const CupertinoFullscreenDialogTransitionDemo(), - ]; - case "CupertinoPageTransition": - return [ - const CupertinoPageTransitionDemo(), - ]; - case "HtmlElementView": - return [ - const HtmlElementViewDemo(), - ]; - case "Text": - return [ - const CustomText(), - const ShadowText(), - const DecorationText(), - const TextAlignText(), - const TextDirectionText(), - const SoftWrapText() - ]; - case "Card": - return [ - const CustomCard(), - const ShapeCard(), - ]; - case "ElevatedButton": - return [ - const ElevatedButtonDemo(), - const ElevatedButtonStyleDemo(), - ]; - case "TextButton": - return [ - const TextButtonDemo(), - const TextButtonStyleDemo(), - ]; - case "OutlinedButton": - return [ - const OutlinedButtonDemo(), - const OutlinedButtonStyleDemo(), - ]; - case "FlutterLogo": - return [ - const CustomFlutterLogo(), - const FlutterLogoWithText(), - ]; - case "Banner": - return [ - CustomBanner(), - ]; - case "UniqueWidget": - return [ - const UniqueWidgetDemo(), - ]; - case "ShrinkWrappingViewport": - return [ - const ShrinkWrappingViewportDemo(), - ]; - case "NestedScrollViewViewport": - return [ - const NestedScrollViewViewportDemo(), - ]; - case "ParentDataWidget": - return [ - const ParentDataWidgetDemo(), - ]; - case "AutomaticKeepAlive": - return [ - AutomaticKeepAliveDemo(), - ]; - case "StatusTransitionWidget": - return [ - const StatusTransitionWidgetDemo(), - ]; - case "KeepAlive": - return [ - KeepAliveDemo(), - ]; - case "ListWheelViewport": - return [ - ListWheelViewportDemo(), - ListWheelViewportDemo2(), - ListWheelViewportDemo3(), - ListWheelViewportDemo4(), - ]; - case "InheritedTheme": - return [ - const InheritedThemeDemo(), - ]; - case "ModalBarrier": - return [ - const ModalBarrierDemo(), - ]; - case "AnimatedModalBarrier": - return [ - const AnimatedModalBarrierDemo(), - ]; - case "DefaultAssetBundle": - return [ - const DefaultAssetBundleDemo(), - ]; - case "DropdownButtonFormField": - return [ - const DropdownButtonFormFieldDemo(), - ]; - case "FormField": - return [ - const FormFieldDemo(), - ]; - case "PaginatedDataTable": - return [ - const PaginatedDataTableDemo(), - ]; - case "InheritedWidget": - return [ - const InheritedWidgetDemo(), - ]; - case "ScrollView": - return [ - const ScrollViewDemo(), - ]; - case "SliverWithKeepAliveWidget": - return [ - const SliverWithKeepAliveWidgetDemo(), - ]; - case "BoxScrollView": - return [ - const BoxScrollViewDemo(), - ]; - case "CupertinoUserInterfaceLevel": - return [ - const CupertinoUserInterfaceLevelDemo(), - ]; - case "CupertinoSliverNavigationBar": - return [ - CupertinoSliverNavigationBarDemo(), - ]; - case "CupertinoSliverRefreshControl": - return [ - const CupertinoSliverRefreshControlDemo(), - ]; - case "SliverAnimatedList": - return [ - const SliverAnimatedListDemo(), - ]; - case "SliverFillRemaining": - return [ - const SliverFillRemainingDemo(), - ]; - case "SliverIgnorePointer": - return [ - const SliverIgnorePointerDemo(), - ]; - case "Viewport": - return [ - ViewportDemo(), - ]; - case "CupertinoPopupSurface": - return [ - CupertinoPopupSurfaceDemo(), - ]; - case "RenderObjectToWidgetAdapter": - return [ - const RenderObjectToWidgetAdapterDemo(), - ]; - case "SliverPrototypeExtentList": - return [ - const SliverPrototypeExtentListDemo(), - ]; - case "InputDecorator": - return [ - const InputDecoratorDemo(), - ]; - case "ColoredBox": - return [ - const ColoredBoxDemo(), - ]; - case "StatefulBuilder": - return [ - const StatefulBuilderDemo(), - ]; - case "RawKeyboardListener": - return [ - const RawKeyboardListenerDemo(), - ]; - case "RawGestureDetector": - return [ - const RawGestureDetectorDemo(), - ]; - case "SafeArea": - return [ - const SafeAreaDemo(), - ]; - case "AnimatedBuilder": - return [ - const AnimatedBuilderDemo(), - ]; - case "TweenAnimationBuilder": - return [ - const TweenAnimationBuilderDemo(), - ]; - case "WidgetInspector": - return [ - const WidgetInspectorDemo(), - ]; - case "PageStorage": - return [ - const PageStorageDemo(), - ]; - case "NotificationListener": - return [ - const NotificationListenerDemo(), - const NotificationListenerUpdate() - ]; - - case "Scrollable": - return [ - ScrollableDemo(), - ]; - case "PhysicalModel": - return [ - const PhysicalModelDemo(), - ]; - case "Directionality": - return [ - const DirectionalityDemo(), - ]; - case "AnimatedTheme": - return [ - const AnimatedThemeDemo(), - ]; - case "CupertinoSlidingSegmentedControl": - return [ - const CupertinoSlidingSegmentedControlDemo(), - ]; - case "WidgetsApp": - return [ - const WidgetsAppDemo(), - ]; - case "TableCell": - return [ - const TableCellDemo(), - ]; - - case "EditableText": - return [ - const EditableTextDemo(), - ]; - case "PhysicalShape": - return [ - const PhysicalShapeDemo(), - ]; - case "ListBody": - return [ - const ListBodyDemo(), - ]; - case "FractionalTranslation": - return [ - const FractionalTranslationDemo(), - ]; - case "MouseRegion": - return [ - const MouseRegionDemo(), - ]; - case "TabPageSelector": - return [ - const TabPageSelectorDemo(), - ]; - case "CupertinoNavigationBarBackButton": - return [ - const CupertinoNavigationBarBackButtonDemo(), - ]; - case "TabPageSelectorIndicator": - return [ - const TabPageSelectorIndicatorDemo(), - ]; - case "Title": - return [ - const TitleDemo(), - ]; - case "ToggleButtonsTheme": - return [ - const ToggleButtonsThemeDemo(), - ]; - case "PerformanceOverlay": - return [ - const PerformanceOverlayDemo(), - ]; - case "NavigationToolbar": - return [ - const NavigationToolbarDemo(), - ]; - case "CupertinoDialogAction": - return [ - const CupertinoDialogActionDemo(), - ]; - case "ButtonBarTheme": - return [ - const ButtonBarThemeDemo(), - ]; - case "RawImage": - return [ - const RawImageDemo(), - ]; - case "TooltipTheme": - return [ - const TooltipThemeDemo(), - ]; - case "CupertinoTextField": - return [ - const CupertinoTextFieldDemo(), - const CupertinoTextFieldStyle(), - ]; - case "MaterialBanner": - return [ - const MaterialBannerDemo(), - const MaterialBannerDemoTwo(), - ]; - case "OrientationBuilder": - return [ - const OrientationBuilderDemo(), - ]; - case "Icon": - return [ - const CustomIcon(), - const MyIcon(), - ]; - case "ImageIcon": - return [ - const CustomImageIcon(), - ]; - case "FadeInImage": - return [ - const CustomFadeInImage(), - ]; - case "InteractiveViewer": - return [ - const InteractiveViewerDemo(), - const InteractiveViewerDemo2(), - const InteractiveViewerDemo3(), - ]; - case "CircleAvatar": - return [ - const CustomCircleAvatar(), - ]; - case "SliverLayoutBuilder": - return [ - const SliverLayoutBuilderDemo(), - ]; - case "Visibility": - return [ - const CustomVisibility(), - const ReplacementVisibility(), - ]; - case "RepaintBoundary": - return [ - const RepaintBoundaryDemo(), - RepaintBoundarySave(), - ]; - case "Chip": - return [ - const CustomChip(), - const ColorOfChip(), - const DeleteOfChip(), - ]; - case "ChoiceChip": - return [const CustomChoiceChip()]; - case "ListTileTheme": - return [const ListTileThemeDemo()]; - case "ValueListenableBuilder": - return [ValueListenableBuilderDemo()]; - case "ActionChip": - return [const CustomActionChip()]; - case "MaterialBannerTheme": - return [const MaterialBannerThemeDemo()]; - case "PopupMenuTheme": - return [const PopupMenuThemeDemo()]; - case "InputChip": - return [const PressInputChip(), const SelectInputChip()]; - case "FilterChip": - return [const CustomFilterChip()]; - case "IntrinsicHeight": - return [const IntrinsicHeightDemo()]; - case "IntrinsicWidth": - return [const IntrinsicWidthDemo()]; - case "ChipTheme": - return [const ChipThemeDemo()]; - case "ListTile": - return [ - const CustomListTile(), - const SelectListTile(), - const DenseListTile() - ]; - case "CheckboxListTile": - return [ - const CustomCheckBoxListTile(), - const SelectCheckBoxListTile(), - const DenseCheckBoxListTile() - ]; - case "SwitchListTile": - return [ - const CustomSwitchListTile(), - const SelectSwitchListTile(), - const DenseSwitchListTile() - ]; - - case "RadioListTile": - return [ - const CustomRadioListTile(), - const DenseRadioListTile(), - ]; - - case "GridTileBar": - return [ - const CustomGridTileBar(), - ]; - - case "GridTile": - return [ - const CustomGridTile(), - ]; - case "UserAccountsDrawerHeader": - return [ - const CustomUAGHP(), - const ProUAGHP(), - ]; - - case "MaterialButton": - return [ - const CustomMaterialButton(), - const LongPressMaterialButton(), - const ShapeMaterialButton(), - ]; - case "CupertinoButton": - return [CustomCupertinoButton()]; - case "FlatButton": - return [const CustomFlatButton()]; - case "RaisedButton": - return [const CustomRaisedButton()]; - case "OutlineButton": - return [const CustomOutlineButton()]; - case "FloatingActionButton": - return [const CustomFAB(), const MiniFAB(), const ShapeFAB()]; - - case "ButtonBar": - return [ - const CustomButtonBar(), - const PaddingButtonBar(), - ]; - - case "IconButton": - return [ - const CustomIconButton(), - ]; - case "BackButton": - return [ - CustomBackButton(), - ]; - - case "CloseButton": - return [ - const CustomCloseButton(), - ]; - case "ToggleButtons": - return [ - const CustomToggleButtons(), - const ColorToggleButtons(), - const ProToggleButtons(), - ]; - case "NestedScrollView": - return [ - const NestedScrollViewDemo(), - ]; - case "SliverOverlapAbsorber": - return [ - const SliverOverlapAbsorberDemo(), - ]; - case "SliverOverlapInjector": - return [ - const SliverOverlapInjectorDemo(), - ]; - case "Divider": - return [ - const CustomDivider(), - const HeightDivider(), - ]; - case "VerticalDivider": - return [ - const CustomVerticalDivider(), - const WidthVerticalDivider(), - ]; - case "Placeholder": - return [ - const CustomPlaceholder(), - const FallbackPlaceholder(), - ]; - case "GridPager": - return [ - const CustomGridPage(), - const DivisionsGridPage(), - ]; - case "Image": - return [ - const LoadImage(), - const FitImage(), - const AlignmentImage(), - const BlendModeImage(), - const RepeatImage(), - const CenterSliceImage(), - ]; - case "Checkbox": - return [ - const CustomCheckbox(), - const TristateCheckBok(), - ]; - case "Switch": - return [ - const CustomSwitch(), - const ImageSwitch(), - ]; - case "CupertinoSwitch": - return [ - const CustomCupertinoSwitch(), - ]; - case "Slider": - return [ - const CustomSlider(), - const DivisionsSlider(), - ]; - case "CupertinoSlider": - return [ - const CustomCupertinoSlider(), - ]; - case "RangeSlider": - return [ - const CustomRangeSlider(), - ]; - case "Radio": - return [ - const CustomRadio(), - ]; - case "CustomMultiChildLayout": - return [ - const CustomMultiChildLayoutDemo(), - ]; - case "LayoutId": - return [ - const LayoutIdDemo(), - ]; - case "CircularProgressIndicator": - return [ - const CustomCircularProgressIndicator(), - ]; - case "LinearProgressIndicator": - return [ - const CustomLinearProgressIndicator(), - ]; - case "CupertinoActivityIndicator": - return [ - const CustomCupertinoActivityIndicator(), - ]; - case "RefreshIndicator": - return [ - const CustomRefreshIndicator(), - ]; - case "Tooltip": - return [ - const CustomTooltip(), - const DecorationTooltip(), - ]; - case "ExpandIcon": - return [ - const CustomExpandIcon(), - ]; - case "ExpansionTile": - return [ - const CustomExpansionTile(), - ]; - - case "AnnotatedRegion": - return [ - const AnnotatedRegionDemo(), - ]; - case "CheckedModeBanner": - return [ - const CheckedModeBannerDemo(), - ]; - case "DefaultTabController": - return [ - const DefaultTabControllerDemo(), - ]; - case "CupertinoTabView": - return [ - const CupertinoTabViewDemo(), - ]; - case "CupertinoTextSelectionToolbar": - return [ - const CupertinoTextSelectionToolbarDemo(), - ]; - case "DraggableScrollableActuator": - return [ - const DraggableScrollableActuatorDemo(), - ]; - case "GlowingOverscrollIndicator": - return [ - GlowingOverscrollIndicatorDemo(), - ]; - case "DraggableScrollableSheet": - return [ - const DraggableScrollableSheetDemo(), - ]; - case "DrawerController": - return [ - const DrawerControllerDemo(), - ]; - case "MergeableMaterial": - return [ - const MergeableMaterialDemo(), - ]; - case "SizeChangedLayoutNotifier": - return [ - const SizeChangedLayoutNotifierDemo(), - ]; - case "SelectableText": - return [ - const CustomSelectableText(), - const AlignSelectableText(), - ]; - case "TextField": - return [ - const CustomTextField(), - const CursorTextField(), - const ComplexTextField(), - ]; - case "DropdownButton": - return [ - const CustomDropDownButton(), - const StyleDropDownButton(), - ]; - case "PopupMenuButton": - return [ - const CustomPopupMenuButton(), - ]; - case "AppBar": - return [ - const CustomAppBar(), - const TabAppBar(), - ]; - case "TabBar": - return [ - const CustomTabBar(), - const NoShadowTabBarDemo(), - ]; - case "TabBarView": - return [ - const CustomTabBarView(), - ]; - case "BottomNavigationBar": - return [ - const CustomBottomNavigationBar(), - const BottomNavigationBarWithPageView() - ]; - case "BottomAppBar": - return [ - const CustomBottomAppBar(), - ]; - case "CupertinoNavigationBar": - return [ - const CustomCupertinoNavigationBar(), - ]; - case "CupertinoTabBar": - return [ - const CustomCupertinoTabBar(), - ]; - case "Scaffold": - return [ - const CustomScaffold(), - ]; - case "MaterialApp": - return [ - const MaterialAppDemo(), - ]; - case "ClipOval": - return [ - const CustomClipOval(), - ]; - case "ClipRect": - return [ - const CustomClipRect(), - ]; - case "ClipRRect": - return [ - const CustomClipRRect(), - ]; - case "ClipPath": - return [ - const CustomClipPath(), - ]; - case "DecoratedBox": - return const [ - BoxDecorationDemo(), - ShapeImageDemo(), - BorderDemo(), - ShapeDecorationDemo(), - UnderlineTabIndicatorDemo(), - FlutterLogoDecorationDemo(), - ]; - case "Offstage": - return [ - const CustomOffstage(), - ]; - case "RotatedBox": - return [ - const CustomRotatedBox(), - ]; - case "Opacity": - return [ - const CustomOpacity(), - ]; - case "Padding": - return [ - const PaddingAll(), - const PaddingOnly(), - const PaddingSymmetric(), - ]; - case "Baseline": - return [ - const CustomBaseline(), - ]; - case "SizedBox": - return [ - const CustomSizedBox(), - ]; - case "AspectRatio": - return [ - const CustomAspectRatio(), - ]; - case "Transform": - return [ - const SkewTransform(), - const TranslationTransform(), - const ScaleTransform(), - const RotateTransform(), - const R3C2(), - ]; - case "LimitedBox": - return [ - const CustomLimitedBox(), - ]; - case "ConstrainedBox": - return [ - const CustomConstrainedBox(), - ]; - case "UnconstrainedBox": - return [ - const CustomUnConstrainedBox(), - ]; - case "FractionallySizedBox": - return [ - const CustomFractionallySizedBox(), - ]; - case "OverflowBox": - return [ - const CustomOverflowBox(), - ]; - case "SizedOverflowBox": - return [ - const CustomSizedOverflowBox(), - ]; - case "Align": - return [const CustomAlign(), const SinLayout()]; - case "Center": - return [ - const CustomCenter(), - ]; - case "FittedBox": - return [ - const CustomFittedBox(), - ]; - case "ColorFiltered": - return [ - const CustomColorFiltered(), - ]; - case "FadeTransition": - return [ - const CustomFadeTransition(), - ]; - case "RotationTransition": - return [ - const CustomRotationTransition(), - ]; - case "ScaleTransition": - return [ - const CustomScaleTransition(), - ]; - case "SizeTransition": - return [ - const CustomSizeTransition(), - ]; - case "PositionedTransition": - return [ - const CustomPositionedTransition(), - ]; - case "Flex": - return [ - DirectionFlex(), - MainAxisAlignmentFlex(), - CrossAxisAlignmentFlex(), - VerticalDirectionFlex(), - TextDirectionFlex(), - ]; - case "Row": - return [ - const CustomRow(), - ]; - case "Column": - return [ - const CustomColumn(), - ]; - case "Stack": - return [const CustomStack(), const PositionedStack()]; - case "Wrap": - return [ - DirectionWrap(), - WrapAlignmentWrap(), - CrossAxisAlignmentWrap(), - TextDirectionWrap(), - VerticalDirectionWrap(), - ]; - case "Flow": - return [ - CircleFlow(), - BurstFlow.show, - ]; - case "AnimatedCrossFade": - return [ - const CustomAnimatedCrossFade(), - const CurveAnimatedCrossFade(), - ]; - case "RichText": - return [ - const CustomRichText(), - const RichTextWithWidget(), - ]; - case "DataTable": - return [ - CustomDataTable(), - const SortDataTable(), - ]; - case "Draggable": - return [ - const CustomDraggable(), - const DraggablePage(), - const DeleteDraggable(), - ]; - case "DragTarget": - return [ - const CustomDragTarget(), - ]; - case "LongPressDraggable": - return [ - const CustomLongPressDraggable(), - ]; - case "Expanded": - return [ - const CustomExpended(), - ]; - case "Spacer": - return [ - const OneSpacer(), - const ManySpacer(), - ]; - case "Positioned": - return [ - const CustomPositioned(), - ]; - case "Flexible": - return [ - const CustomFlexible(), - ]; - case "Table": - return [ - const CustomTable(), - ]; - case "AlignTransition": - return [ - const CustomAlignTransition(), - ]; - case "SlideTransition": - return [ - const CustomSlideTransition(), - ]; - case "DecoratedBoxTransition": - return [ - const CustomDecoratedBoxTransition(), - ]; - case "DefaultTextStyleTransition": - return [ - const CustomDefaultTextStyleTransition(), - ]; - case "RelativePositionedTransition": - return [ - const CustomRelativePositionedTransition(), - ]; - case "AnimatedSwitcher": - return [ - const CustomAnimatedSwitcher(), - ]; - case "AnimatedList": - return [ - const CustomAnimatedList(), - ]; - case "AnimatedOpacity": - return [ - const CustomAnimatedOpacity(), - ]; - case "AnimatedPadding": - return [ - const CustomAnimatedPadding(), - ]; - case "AnimatedAlign": - return [ - const CustomAnimatedAlign(), - ]; - case "AnimatedPositioned": - return [ - const CustomAnimatedPositioned(), - ]; - case "AnimatedPositionedDirectional": - return [ - const CustomAnimatedPositionedDirectional(), - ]; - case "AnimatedContainer": - return [ - const CustomAnimatedContainer(), - ]; - case "AnimatedDefaultTextStyle": - return [ - const CustomAnimatedDefaultTextStyle(), - ]; - case "AnimatedIcon": - return [ - const CustomAnimatedIcon(), - ]; - case "Dialog": - return [ - const CustomDialog(), - ]; - case "AlertDialog": - return [ - const CustomAlertDialog(), - ]; - case "SimpleDialog": - return [ - const CustomSimpleDialog(), - ]; - case "CupertinoAlertDialog": - return [ - const CustomCupertinoAlertDialog(), - ]; - case "AboutDialog": - return [ - const CustomAboutDialog(), - ]; - case "CupertinoActionSheet": - return [ - const CustomCupertinoActionSheet(), - ]; - case "CupertinoActionSheetAction": - return [ - const CustomCupertinoActionSheetAction(), - ]; - case "SimpleDialogOption": - return [ - const CustomSimpleDialogOption(), - ]; - case "DayPicker": - return [ - const CustomDayPicker(), - ]; - case "MonthPicker": - return [ - const CustomMonthPicker(), - ]; - case "YearPicker": - return [ - const CustomYearPicker(), - ]; - case "CupertinoDatePicker": - return [ - const CustomCupertinoDatePicker(), - ]; - case "CupertinoTimerPicker": - return [ - const CustomCupertinoTimerPicker(), - ]; - case "CupertinoPicker": - return [ - const CustomCupertinoPicker(), - ]; - case "SnackBar": - return [ - const CustomSnackBar(), - ]; - case "SnackBarAction": - return [ - const CustomSnackBarAction(), - ]; - case "BottomSheet": - return [ - const CustomBottomSheet(), - ]; - case "CupertinoContextMenu": - return [ - const CustomCupertinoContextMenu(), - ]; - case "CupertinoContextMenuAction": - return [ - const CustomCupertinoContextMenuAction(), - ]; - case "LicensePage": - return [ - const CustomLicensePage(), - ]; - case "Builder": - return [ - const BuilderDemo(), - ]; - case "GestureDetector": - return [ - const CustomGestureDetector(), - const TapGestureDetector(), - const PanGestureDetector(), - ]; - case "Listener": - return [ - const CustomListener(), - ]; - case "Tab": - return [ - const CustomTab(), - ]; - case "PreferredSize": - return [ - const CustomPreferredSize(), - const AdapterPreferredSize(), - ]; - case "InkResponse": - return [ - const CustomInkResponse(), - const ColorInkResponse(), - ]; - case "InkWell": - return [ - const CustomInkWell(), - const ColorInkWell(), - ]; - case "TableRowInkWell": - return [ - const CustomTableRowInkWell(), - ]; - case "Ink": - return [ - const CustomInk(), - const InkImage(), - ]; - case "RawChip": - return [ - const PressRawChip(), - const SelectRawChip(), - ]; - case "Drawer": - return [ - const CustomDrawer(), - ]; - case "DrawerHeader": - return [ - const CustomDrawerHeader(), - ]; - case "CupertinoApp": - return [ - const CustomCupertinoApp(), - ]; - case "CupertinoPageScaffold": - return [ - const CustomCupertinoPageScaffold(), - ]; - case "CupertinoTabScaffold": - return [ - const CustomCupertinoTabScaffold(), - ]; - case "PositionedDirectional": - return [ - const CustomPositionedDirectional(), - ]; - case "Material": - return [ - const CustomMaterial(), - const ShapeMaterial(), - ]; - case "IndexedStack": - return [ - const CustomIndexedStack(), - ]; - case "ListView": - return [ - CustomListView(), - HorizontalListView(), - SeparatedListView(), - BuilderListView(), - ]; - case "GridView": - return [ - CustomGridView(), - HorizontalGridView(), - ExtentGridView(), - BuilderGridView() - ]; - case "SingleChildScrollView": - return [ - CustomSingleChildScrollView(), - DirectionSingleChildScrollView(), - ]; - case "PageView": - return [ - CustomPageView(), - DirectionPageView(), - const CtrlPageView(), - ]; - case "CustomPaint": - return [ - const ClockPage(), - const PlayBezier3Page(), - ]; - case "MediaQuery": - return [ - const CustomMediaQuery(), - ]; - case "Theme": - return [const TextThemeDemo(), const CustomTheme()]; - case "CupertinoTheme": - return [const TextCupertinoTheme(), const CustomCupertinoTheme()]; - case "WillPopScope": - return [ - const CustomWillPopScope(), - ]; - case "Hero": - return [ - const CustomHero(), - ]; - case "FutureBuilder": - return [ - const CustomFutureBuilder(), - ]; - case "StreamBuilder": - return [ - const CustomStreamBuilder(), - ]; - case "PopupMenuDivider": - return [ - const CustomPopupMenuDivider(), - ]; - case "RawMaterialButton": - return [ - const CustomRawMaterialButton(), - const ShapeRawMaterialButton(), - ]; - case "Dismissible": - return [ - const CustomDismissible(), - const DirectionDismissible(), - ]; - case "ReorderableListView": - return [ - const CustomReorderableListView(), - const DirectionReorderableListView(), - ]; - case "ExpansionPanelList": - return [ - const CustomExpansionPanelList(), - ]; - case "ListWheelScrollView": - return [ - const CustomListWheelScrollView(), - ]; - case "ScrollConfiguration": - return [ - CustomScrollConfiguration(), - ]; - case "DropdownButtonHideUnderline": - return [ - const CustomDropDownButtonHideUnderline(), - ]; - case "Overlay": - return [ - const CustomOverlay(), - ]; - case "CustomScrollView": - return [ - CustomScrollViewDemo(), - ]; - case "SliverAppBar": - return [ - const SliverAppBarDemo(), - ]; - case "SliverList": - return [ - SliverListDemo(), - ]; - case "SliverFixedExtentList": - return [ - const SliverFixedExtentListDemo(), - ]; - case "SliverFillViewport": - return [ - const SliverFillViewportDemo(), - ]; - case "SliverGird": - return [ - SliverGirdDemo(), - ]; - case "SliverToBoxAdapter": - return [ - SliverToBoxAdapterDemo(), - ]; - case "SliverPersistentHeader": - return [ - SliverPersistentHeaderDemo(), - ]; - case "SliverPadding": - return [ - SliverPaddingDemo(), - ]; - case "SliverOpacity": - return [ - SliverOpacityDemo(), - ]; - case "AboutListTile": - return [ - const AboutListTileDemo(), - ]; - case "Scrollbar": - return [ - CustomScrollbar(), - ]; - case "CupertinoScrollbar": - return [ - CustomCupertinoScrollbar(), - ]; - case "FlexibleSpaceBar": - return [ - FlexibleSpaceBarDemo(), - ]; - case "ErrorWidget": - return [ - const ErrorWidgetDemo(), - ]; - case "Form": - return [ - const CustomForm(), - ]; - case "TextFormField": - return [ - const CustomTextFormField(), - ]; - case "Stepper": - return [ - const StepperDemo(), - const VerticalStepper(), - ]; - case "AnimatedSize": - return [ - const CustomAnimatedSize(), - ]; - case "ShaderMask": - return [ - const RadialShaderMask(), - const LinearShaderMask(), - ]; - case "DefaultTextStyle": - return [ - const DefaultTextStyleDemo(), - ]; - case "IconTheme": - return [ - const IconThemeDemo(), - ]; - case "ButtonTheme": - return [ - const ButtonThemeDemo(), - ]; - case "DividerTheme": - return [ - const DividerThemeDemo(), - ]; - case "Navigator": - return [ - const NavigatorDemo(), - ]; - case "SliderTheme": - return [ - const SliderThemeDemo(), - const DIYSliderTheme(), - ]; - case "LayoutBuilder": - return [ - const CustomLayoutBuilder(), - const FitByLayoutBuilder(), - const SimpleExpandableText(), - ]; - case "IgnorePointer": - return [ - const CustomIgnorePointer(), - ]; - case "AbsorbPointer": - return [ - const CustomAbsorbPointer(), - ]; - case "BackdropFilter": - return [ - const CustomBackdropFilter(), - ]; - case "AnimatedPhysicalModel": - return [ - const AnimatedPhysicalModelDemo(), - ]; - case "CustomSingleChildLayout": - return [ - const CustomSingleChildLayoutDemo(), - const OffSetWidgetDemo(), - ]; - case "NavigationRail": - return [ - const AnotherPage(child: CustomNavigationRail()), - const AnotherPage(child: ExtendableNavigationRail()), - const AnotherPage(child: DarkNavigationRail()), - ]; - case "RawMagnifier": - return [ - const MagnifierCircleShape(), - const MagnifierStarShape(), - ]; - case "DecoratedSliver": - return [ - const DecorationSliverDemo(), - ]; - case "SliverConstrainedCrossAxis": - return [ - const SliverConstrainedCrossAxisDemo(), - ]; - case "SliverCrossAxisExpanded": - return [ - const SliverCrossAxisExpandedDemo(), - ]; - case "SliverCrossAxisGroup": - return [ - const SliverCrossAxisGroupDemo(), - ]; - case "SliverMainAxisGroup": - return [ - const SliverMainAxisGroupDemo(), - ]; - case "BackButtonIcon": - return [ - const BackButtonIconDemo(), - ]; - case "DrawerButtonIcon": - return [ - const DrawerButtonIconDemo(), - ]; - case "CloseButtonIcon": - return [ - const CloseButtonIconDemo(), - ]; - case "EndDrawerButtonIcon": - return [ - const EndDrawerButtonIconDemo(), - ]; - case "EndDrawerButton": - return [ - const EndDrawerButtonDemo(), - ]; - case "DrawerButton": - return [ - const DrawerButtonDemo(), - ]; - default: - return []; - } - } -} - -class AnotherPage extends StatelessWidget { - final Widget child; - const AnotherPage({Key? key, required this.child}) : super(key: key); - - @override - Widget build(BuildContext context) { - return ElevatedButton( - onPressed: () { - Navigator.of(context).push(MaterialPageRoute( - builder: (_) => Scaffold( - appBar: AppBar( - leading: const BackButton(), - ), - body: child))); - }, - child: Text('跳转到新界面查看效果')); - } -} diff --git a/packages/widgets/pubspec.yaml b/packages/widgets/pubspec.yaml deleted file mode 100644 index 765207f89..000000000 --- a/packages/widgets/pubspec.yaml +++ /dev/null @@ -1,60 +0,0 @@ -name: widgets -description: A new Flutter package project. -version: 0.0.1 -homepage: - -environment: - sdk: '>=2.18.6 <3.0.0' - flutter: ">=1.17.0" - -dependencies: - flutter: - sdk: flutter - path_provider: ^2.0.11 # 路径 - intl: ^0.18.0 - app: - path: ../app - components: - path: ../components - -dev_dependencies: - flutter_test: - sdk: flutter - flutter_lints: ^2.0.0 - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter packages. -flutter: - - # To add assets to your package, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - # - # For details regarding assets in packages, see - # https://flutter.dev/assets-and-images/#from-packages - # - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware - - # To add custom fonts to your package, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts in packages, see - # https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/widgets/test/widgets_test.dart b/packages/widgets/test/widgets_test.dart deleted file mode 100644 index f286e7dcc..000000000 --- a/packages/widgets/test/widgets_test.dart +++ /dev/null @@ -1,12 +0,0 @@ -// import 'package:flutter_test/flutter_test.dart'; -// -// import 'package:widgets/widgets.dart'; -// -// void main() { -// test('adds one to input values', () { -// final calculator = Calculator(); -// expect(calculator.addOne(2), 3); -// expect(calculator.addOne(-7), -6); -// expect(calculator.addOne(0), 1); -// }); -// } diff --git a/pubspec.lock b/pubspec.lock index 7bcc0ef78..e16819a50 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,160 +1,126 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: - algorithm: - dependency: "direct main" - description: - path: "packages/algorithm" - relative: true - source: path - version: "0.0.1" - app: - dependency: "direct main" - description: - path: "packages/app" - relative: true - source: path - version: "0.0.1" - app_update: - dependency: "direct main" - description: - path: "packages/app_update" - relative: true - source: path - version: "0.0.1" archive: dependency: "direct main" description: name: archive - sha256: e0902a06f0e00414e4e3438a084580161279f137aeb862274710f29ec10cf01e + sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" url: "https://pub.flutter-io.cn" source: hosted - version: "3.3.9" + version: "4.0.7" args: dependency: transitive description: name: args - sha256: b003c3098049a51720352d219b0bb5f219b60fbfb68e7a4748139a06a5676515 + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 url: "https://pub.flutter-io.cn" source: hosted - version: "2.3.1" - artifact: - dependency: "direct main" - description: - path: "packages/artifact" - relative: true - source: path - version: "0.0.1" + version: "2.7.0" async: dependency: transitive description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" url: "https://pub.flutter-io.cn" source: hosted - version: "2.11.0" - authentication: - dependency: "direct main" - description: - path: "packages/authentication" - relative: true - source: path - version: "0.0.1" + version: "2.13.0" bloc: dependency: transitive description: name: bloc - sha256: "3820f15f502372d979121de1f6b97bfcf1630ebff8fe1d52fb2b0bfa49be5b49" + sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e" url: "https://pub.flutter-io.cn" source: hosted - version: "8.1.2" + version: "8.1.4" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.1" + version: "2.1.2" characters: dependency: transitive description: name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.flutter-io.cn" source: hosted - version: "1.3.0" - clock: + version: "1.4.0" + charcode: dependency: transitive description: - name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + name: charcode + sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a url: "https://pub.flutter-io.cn" source: hosted - version: "1.1.1" - collection: + version: "1.4.0" + clock: dependency: transitive description: - name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.18.0" - components: - dependency: "direct main" - description: - path: "packages/components" - relative: true - source: path - version: "0.0.1" - connectivity_plus: - dependency: "direct main" - description: - name: connectivity_plus - sha256: "77a180d6938f78ca7d2382d2240eb626c0f6a735d0bfdce227d8ffb80f95c48b" + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.flutter-io.cn" source: hosted - version: "4.0.2" - connectivity_plus_platform_interface: + version: "1.1.2" + collection: dependency: transitive description: - name: connectivity_plus_platform_interface - sha256: cf1d1c28f4416f8c654d7dc3cd638ec586076255d407cef3ddbdaf178272a71a + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.flutter-io.cn" source: hosted - version: "1.2.4" + version: "1.19.1" convert: dependency: transitive description: name: convert - sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 url: "https://pub.flutter-io.cn" source: hosted - version: "3.1.1" + version: "3.1.2" cross_file: dependency: transitive description: name: cross_file - sha256: "0b0036e8cccbfbe0555fd83c1d31a6f30b77a96b598b35a5d36dd41f718695e9" + sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" url: "https://pub.flutter-io.cn" source: hosted - version: "0.3.3+4" + version: "0.3.4+2" crypto: dependency: transitive description: name: crypto - sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67 + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" url: "https://pub.flutter-io.cn" source: hosted - version: "3.0.2" + version: "3.0.6" + csslib: + dependency: transitive + description: + name: csslib + sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.2" cupertino_icons: dependency: "direct main" description: name: cupertino_icons - sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 url: "https://pub.flutter-io.cn" source: hosted - version: "1.0.5" + version: "1.0.8" + dart_quill_delta: + dependency: transitive + description: + name: dart_quill_delta + sha256: bddb0b2948bd5b5a328f1651764486d162c59a8ccffd4c63e8b2c5e44be1dac4 + url: "https://pub.flutter-io.cn" + source: hosted + version: "10.8.3" dash_painter: dependency: "direct main" description: @@ -163,62 +129,110 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.0.2" - dbus: + dev_build: dependency: transitive description: - name: dbus - sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263" + name: dev_build + sha256: fda8a54458b2a873a84e0cd1513f4323a1fb0599ed5455245359bc0398bad9ee url: "https://pub.flutter-io.cn" source: hosted - version: "0.7.8" + version: "1.1.2+11" + diff_match_patch: + dependency: transitive + description: + name: diff_match_patch + sha256: "2efc9e6e8f449d0abe15be240e2c2a3bcd977c8d126cfd70598aee60af35c0a4" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.4.1" dio: dependency: "direct main" description: name: dio - sha256: ce75a1b40947fea0a0e16ce73337122a86762e38b982e1ccb909daa3b9bc4197 + sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9" + url: "https://pub.flutter-io.cn" + source: hosted + version: "5.8.0+1" + dio_web_adapter: + dependency: transitive + description: + name: dio_web_adapter + sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78" url: "https://pub.flutter-io.cn" source: hosted - version: "5.3.2" + version: "2.1.1" equatable: dependency: "direct main" description: name: equatable - sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7" url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.5" + version: "2.0.7" fake_async: dependency: transitive description: name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" url: "https://pub.flutter-io.cn" source: hosted - version: "1.3.1" + version: "1.3.3" ffi: dependency: transitive description: name: ffi - sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978 + sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.1" + version: "2.1.4" file: dependency: transitive description: name: file - sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 url: "https://pub.flutter-io.cn" source: hosted - version: "6.1.4" + version: "7.0.1" file_picker: dependency: "direct main" description: name: file_picker - sha256: be325344c1f3070354a1d84a231a1ba75ea85d413774ec4bdf444c023342e030 + sha256: "77f8e81d22d2a07d0dee2c62e1dda71dc1da73bf43bb2d45af09727406167964" + url: "https://pub.flutter-io.cn" + source: hosted + version: "10.1.9" + file_selector_linux: + dependency: transitive + description: + name: file_selector_linux + sha256: "54cbbd957e1156d29548c7d9b9ec0c0ebb6de0a90452198683a7d23aed617a33" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.9.3+2" + file_selector_platform_interface: + dependency: transitive + description: + name: file_selector_platform_interface + sha256: a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b url: "https://pub.flutter-io.cn" source: hosted - version: "5.5.0" + version: "2.6.2" + file_selector_windows: + dependency: transitive + description: + name: file_selector_windows + sha256: "320fcfb6f33caa90f0b58380489fc5ac05d99ee94b61aa96ec2bff0ba81d3c2b" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.9.3+4" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.1" flutter: dependency: "direct main" description: flutter @@ -228,18 +242,66 @@ packages: dependency: "direct main" description: name: flutter_bloc - sha256: e74efb89ee6945bcbce74a5b3a5a3376b088e5f21f55c263fc38cbdc6237faae + sha256: b594505eac31a0518bdcb4b5b79573b8d9117b193cc80cc12e17d639b10aa27a + url: "https://pub.flutter-io.cn" + source: hosted + version: "8.1.6" + flutter_colorpicker: + dependency: transitive + description: + name: flutter_colorpicker + sha256: "969de5f6f9e2a570ac660fb7b501551451ea2a1ab9e2097e89475f60e07816ea" url: "https://pub.flutter-io.cn" source: hosted - version: "8.1.3" + version: "1.1.0" + flutter_keyboard_visibility_linux: + dependency: transitive + description: + name: flutter_keyboard_visibility_linux + sha256: "6fba7cd9bb033b6ddd8c2beb4c99ad02d728f1e6e6d9b9446667398b2ac39f08" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.0" + flutter_keyboard_visibility_macos: + dependency: transitive + description: + name: flutter_keyboard_visibility_macos + sha256: c5c49b16fff453dfdafdc16f26bdd8fb8d55812a1d50b0ce25fc8d9f2e53d086 + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.0" + flutter_keyboard_visibility_platform_interface: + dependency: transitive + description: + name: flutter_keyboard_visibility_platform_interface + sha256: e43a89845873f7be10cb3884345ceb9aebf00a659f479d1c8f4293fcb37022a4 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.0" + flutter_keyboard_visibility_temp_fork: + dependency: transitive + description: + name: flutter_keyboard_visibility_temp_fork + sha256: e3d02900640fbc1129245540db16944a0898b8be81694f4bf04b6c985bed9048 + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.1.5" + flutter_keyboard_visibility_windows: + dependency: transitive + description: + name: flutter_keyboard_visibility_windows + sha256: fc4b0f0b6be9b93ae527f3d527fb56ee2d918cd88bbca438c478af7bcfd0ef73 + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.0" flutter_lints: dependency: "direct dev" description: name: flutter_lints - sha256: b543301ad291598523947dc534aaddc5aaad597b709d2426d3a0e0d44c5cb493 + sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" url: "https://pub.flutter-io.cn" source: hosted - version: "1.0.4" + version: "4.0.0" flutter_localizations: dependency: "direct main" description: flutter @@ -249,26 +311,42 @@ packages: dependency: "direct main" description: name: flutter_markdown - sha256: "981442432b632237ffc1cf8092b4173b9e9f2278b5740637287c3069b51c8f09" + sha256: "08fb8315236099ff8e90cb87bb2b935e0a724a3af1623000a9cec930468e0f27" url: "https://pub.flutter-io.cn" source: hosted - version: "0.6.13" + version: "0.7.7+1" flutter_plugin_android_lifecycle: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: "950e77c2bbe1692bc0874fc7fb491b96a4dc340457f4ea1641443d0a6c1ea360" + sha256: f948e346c12f8d5480d2825e03de228d0eb8c3a737e4cdaa122267b89c022b5e + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.28" + flutter_quill: + dependency: transitive + description: + name: flutter_quill + sha256: "7e60963632bbc8615627f0bae8e178515f69ecb378ad49fa68c43c2aabf33e21" url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.15" + version: "11.4.1" + flutter_quill_delta_from_html: + dependency: transitive + description: + name: flutter_quill_delta_from_html + sha256: "4597bd0853a704696837aa6b05cffd851f587b176204c234edddfed1c1862a09" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.5.2" flutter_spinkit: dependency: "direct main" description: name: flutter_spinkit - sha256: b39c753e909d4796906c5696a14daf33639a76e017136c8d82bf3e620ce5bb8e + sha256: d2696eed13732831414595b98863260e33e8882fc069ee80ec35d4ac9ddb0472 url: "https://pub.flutter-io.cn" source: hosted - version: "5.2.0" + version: "5.2.1" flutter_star: dependency: "direct main" description: @@ -281,10 +359,10 @@ packages: dependency: "direct main" description: name: flutter_svg - sha256: "8c5d68a82add3ca76d792f058b186a0599414f279f00ece4830b9b231b570338" + sha256: d44bf546b13025ec7353091516f6881f1d4c633993cb109c3916c3a0159dadf1 url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.7" + version: "2.1.0" flutter_test: dependency: "direct dev" description: flutter @@ -295,46 +373,126 @@ packages: description: flutter source: sdk version: "0.0.0" + fx_boot_starter: + dependency: "direct main" + description: + name: fx_boot_starter + sha256: cdc3d6e31a2609214468cb858f071b7f8f8de5731093b1a11305a457557bdf96 + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.1.1" + fx_dao: + dependency: "direct main" + description: + name: fx_dao + sha256: "253381288b921b9ad5e193d5e48531b71ad0c4511be66146603c65d36d2e995b" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.0.3+4" + fx_dio: + dependency: "direct main" + description: + name: fx_dio + sha256: "395549518d055596d58ffcff59b4da0c147d855f6258f2769213c7230c090525" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.0.4+3" + fx_env: + dependency: transitive + description: + name: fx_env + sha256: c95836ab108c498d53f43c464e08a5ce64975efdf586fb46f1a4c37bb2c400bf + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.0.1" + fx_go_router_ext: + dependency: "direct main" + description: + name: fx_go_router_ext + sha256: dc65ac677f2058b8192ca50bdcd508a12ef4b6c150f64ca8d595b536ac6e5d1b + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.0.6+1" + fx_platform_adapter: + dependency: "direct main" + description: + name: fx_platform_adapter + sha256: e6d5ca554a1fd019a695a63bbb3e4eee6efe492b5d926542f22d6b64cea99415 + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.0.3" + fx_string: + dependency: transitive + description: + name: fx_string + sha256: "3350be2fa11cdb0d4107e4657431d05088a88795ef3145ad37902f6066a5b124" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.0.1" + fx_trace: + dependency: "direct main" + description: + name: fx_trace + sha256: a1fb64b1a6bfc53609fe55e6d56a9c00e76250818eb5a8cfac280a051e33911c + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.0.5+5" + go_router: + dependency: "direct main" + description: + name: go_router + sha256: "04539267a740931c6d4479a10d466717ca5901c6fdfd3fcda09391bbb8ebd651" + url: "https://pub.flutter-io.cn" + source: hosted + version: "14.8.0" + html: + dependency: transitive + description: + name: html + sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.15.6" http: dependency: transitive description: name: http - sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482" + sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" url: "https://pub.flutter-io.cn" source: hosted - version: "0.13.5" + version: "1.4.0" http_parser: dependency: transitive description: name: http_parser - sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" url: "https://pub.flutter-io.cn" source: hosted - version: "4.0.2" + version: "4.1.2" image: dependency: "direct main" description: name: image - sha256: a72242c9a0ffb65d03de1b7113bc4e189686fc07c7147b8b41811d0dd0e0d9bf + sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928" url: "https://pub.flutter-io.cn" source: hosted - version: "4.0.17" + version: "4.5.4" intl: - dependency: "direct main" + dependency: "direct overridden" description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" url: "https://pub.flutter-io.cn" source: hosted - version: "0.18.1" - js: + version: "0.20.2" + json_annotation: dependency: transitive description: - name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" url: "https://pub.flutter-io.cn" source: hosted - version: "0.6.7" + version: "4.9.0" jwt_decoder: dependency: "direct main" description: @@ -343,54 +501,86 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "2.0.1" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0" + url: "https://pub.flutter-io.cn" + source: hosted + version: "11.0.1" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.10" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.2" lints: dependency: transitive description: name: lints - sha256: a2c3d198cb5ea2e179926622d433331d8b58374ab8f29cdda6e863bd62fd369c + sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" url: "https://pub.flutter-io.cn" source: hosted - version: "1.0.1" + version: "4.0.0" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.3.0" markdown: dependency: transitive description: name: markdown - sha256: c2b81e184067b41d0264d514f7cdaa2c02d38511e39d6521a1ccc238f6d7b3f2 + sha256: "935e23e1ff3bc02d390bad4d4be001208ee92cc217cb5b5a6c19bc14aaa318c1" url: "https://pub.flutter-io.cn" source: hosted - version: "6.0.1" + version: "7.3.0" matcher: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.flutter-io.cn" source: hosted - version: "0.12.16" + version: "0.12.17" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.flutter-io.cn" source: hosted - version: "0.5.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.flutter-io.cn" source: hosted - version: "1.10.0" + version: "1.16.0" mime: dependency: transitive description: name: mime - sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" url: "https://pub.flutter-io.cn" source: hosted - version: "1.0.4" + version: "2.0.0" nested: dependency: transitive description: @@ -399,70 +589,126 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.0.0" - nm: + open_file: + dependency: transitive + description: + name: open_file + sha256: d17e2bddf5b278cb2ae18393d0496aa4f162142ba97d1a9e0c30d476adf99c0e + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.5.10" + open_file_android: + dependency: transitive + description: + name: open_file_android + sha256: "58141fcaece2f453a9684509a7275f231ac0e3d6ceb9a5e6de310a7dff9084aa" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.6" + open_file_ios: + dependency: transitive + description: + name: open_file_ios + sha256: "02996f01e5f6863832068e97f8f3a5ef9b613516db6897f373b43b79849e4d07" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.3" + open_file_linux: + dependency: transitive + description: + name: open_file_linux + sha256: d189f799eecbb139c97f8bc7d303f9e720954fa4e0fa1b0b7294767e5f2d7550 + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.0.5" + open_file_mac: + dependency: transitive + description: + name: open_file_mac + sha256: "1440b1e37ceb0642208cfeb2c659c6cda27b25187a90635c9d1acb7d0584d324" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.3" + open_file_platform_interface: + dependency: transitive + description: + name: open_file_platform_interface + sha256: "101b424ca359632699a7e1213e83d025722ab668b9fd1412338221bf9b0e5757" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.3" + open_file_web: + dependency: transitive + description: + name: open_file_web + sha256: e3dbc9584856283dcb30aef5720558b90f88036360bd078e494ab80a80130c4f + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.0.4" + open_file_windows: dependency: transitive description: - name: nm - sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254" + name: open_file_windows + sha256: d26c31ddf935a94a1a3aa43a23f4fff8a5ff4eea395fe7a8cb819cf55431c875 url: "https://pub.flutter-io.cn" source: hosted - version: "0.5.0" + version: "0.0.3" package_info_plus: dependency: "direct main" description: name: package_info_plus - sha256: "6ff267fcd9d48cb61c8df74a82680e8b82e940231bb5f68356672fde0397334a" + sha256: b15fad91c4d3d1f2b48c053dd41cb82da007c27407dc9ab5f9aa59881d0e39d4 url: "https://pub.flutter-io.cn" source: hosted - version: "4.1.0" + version: "8.1.4" package_info_plus_platform_interface: dependency: transitive description: name: package_info_plus_platform_interface - sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6" + sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c" url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.1" + version: "3.2.0" path: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.flutter-io.cn" source: hosted - version: "1.8.3" + version: "1.9.1" path_parsing: dependency: transitive description: name: path_parsing - sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf + sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca" url: "https://pub.flutter-io.cn" source: hosted - version: "1.0.1" + version: "1.1.0" path_provider: dependency: "direct main" description: name: path_provider - sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa + sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.1" + version: "2.1.5" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "6b8b19bd80da4f11ce91b2d1fb931f3006911477cec227cce23d3253d80df3f1" + sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 url: "https://pub.flutter-io.cn" source: hosted - version: "2.2.0" + version: "2.2.17" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" + sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" url: "https://pub.flutter-io.cn" source: hosted - version: "2.3.1" + version: "2.4.1" path_provider_linux: dependency: transitive description: @@ -475,270 +721,414 @@ packages: dependency: transitive description: name: path_provider_platform_interface - sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.1" + version: "2.1.2" path_provider_windows: dependency: transitive description: name: path_provider_windows - sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 url: "https://pub.flutter-io.cn" source: hosted - version: "2.2.1" + version: "2.3.0" petitparser: dependency: transitive description: name: petitparser - sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 + sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646" url: "https://pub.flutter-io.cn" source: hosted - version: "5.4.0" + version: "6.1.0" platform: - dependency: "direct main" + dependency: transitive description: name: platform - sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" url: "https://pub.flutter-io.cn" source: hosted - version: "3.1.0" + version: "3.1.6" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc" + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.4" - pointycastle: + version: "2.1.8" + pool: dependency: transitive description: - name: pointycastle - sha256: db7306cf0249f838d1a24af52b5a5887c5bf7f31d8bb4e827d071dc0939ad346 + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" url: "https://pub.flutter-io.cn" source: hosted - version: "3.6.2" - process: + version: "1.5.1" + posix: dependency: transitive description: - name: process - sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" + name: posix + sha256: f0d7856b6ca1887cfa6d1d394056a296ae33489db914e365e2044fdada449e62 url: "https://pub.flutter-io.cn" source: hosted - version: "4.2.4" + version: "6.0.2" + process_run: + dependency: transitive + description: + name: process_run + sha256: "6ec839cdd3e6de4685318e7686cd4abb523c3d3a55af0e8d32a12ae19bc66622" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.2.4" provider: dependency: transitive description: name: provider - sha256: e1e7413d70444ea3096815a60fe5da1b11bda8a9dc4769252cc82c53536f8bcc + sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84" url: "https://pub.flutter-io.cn" source: hosted - version: "6.0.4" - r_upgrade: - dependency: "direct main" + version: "6.1.5" + pub_semver: + dependency: transitive description: - name: r_upgrade - sha256: "14337dd29cf67265ec86e1d518212cb6b6eafe9167fd366b050ccb1656461c55" + name: pub_semver + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" url: "https://pub.flutter-io.cn" source: hosted - version: "0.4.2" - refresh: - dependency: "direct main" + version: "2.2.0" + quill_native_bridge: + dependency: transitive description: - name: refresh - sha256: cbf2254cd1a54135855c9a544af1e1c25472781a9f2c973e56b2068907d669dc + name: quill_native_bridge + sha256: "00752aca7d67cbd3254709a47558be78427750cb81aa42cfbed354d4a079bcfa" url: "https://pub.flutter-io.cn" source: hosted - version: "1.0.1" + version: "11.0.1" + quill_native_bridge_android: + dependency: transitive + description: + name: quill_native_bridge_android + sha256: b75c7e6ede362a7007f545118e756b1f19053994144ec9eda932ce5e54a57569 + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.0.1+2" + quill_native_bridge_ios: + dependency: transitive + description: + name: quill_native_bridge_ios + sha256: d23de3cd7724d482fe2b514617f8eedc8f296e120fb297368917ac3b59d8099f + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.0.1" + quill_native_bridge_linux: + dependency: transitive + description: + name: quill_native_bridge_linux + sha256: "5fcc60cab2ab9079e0746941f05c5ca5fec85cc050b738c8c8b9da7c09da17eb" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.0.1" + quill_native_bridge_macos: + dependency: transitive + description: + name: quill_native_bridge_macos + sha256: "1c0631bd1e2eee765a8b06017c5286a4e829778f4585736e048eb67c97af8a77" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.0.1" + quill_native_bridge_platform_interface: + dependency: transitive + description: + name: quill_native_bridge_platform_interface + sha256: "8264a2bdb8a294c31377a27b46c0f8717fa9f968cf113f7dc52d332ed9c84526" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.0.2+1" + quill_native_bridge_web: + dependency: transitive + description: + name: quill_native_bridge_web + sha256: "7c723f6824b0250d7f33e8b6c23f2f8eb0103fe48ee7ebf47ab6786b64d5c05d" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.0.2" + quill_native_bridge_windows: + dependency: transitive + description: + name: quill_native_bridge_windows + sha256: "60e50d74238f22ceb43113d9a42b6627451dab9fc27f527b979a32051cf1da45" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.0.1" + quiver: + dependency: transitive + description: + name: quiver + sha256: ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2 + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.2.2" + r_upgrade: + dependency: transitive + description: + path: "modules/ability/r_upgrade-0.4.2" + relative: true + source: path + version: "0.4.2" screen_retriever: dependency: transitive description: name: screen_retriever - sha256: "6ee02c8a1158e6dae7ca430da79436e3b1c9563c8cf02f524af997c201ac2b90" + sha256: "570dbc8e4f70bac451e0efc9c9bb19fa2d6799a11e6ef04f946d7886d2e23d0c" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.2.0" + screen_retriever_linux: + dependency: transitive + description: + name: screen_retriever_linux + sha256: f7f8120c92ef0784e58491ab664d01efda79a922b025ff286e29aa123ea3dd18 + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.2.0" + screen_retriever_macos: + dependency: transitive + description: + name: screen_retriever_macos + sha256: "71f956e65c97315dd661d71f828708bd97b6d358e776f1a30d5aa7d22d78a149" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.2.0" + screen_retriever_platform_interface: + dependency: transitive + description: + name: screen_retriever_platform_interface + sha256: ee197f4581ff0d5608587819af40490748e1e39e648d7680ecf95c05197240c0 url: "https://pub.flutter-io.cn" source: hosted - version: "0.1.9" + version: "0.2.0" + screen_retriever_windows: + dependency: transitive + description: + name: screen_retriever_windows + sha256: "449ee257f03ca98a57288ee526a301a430a344a161f9202b4fcc38576716fe13" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.2.0" share_plus: dependency: "direct main" description: name: share_plus - sha256: "6cec740fa0943a826951223e76218df002804adb588235a8910dc3d6b0654e11" + sha256: fce43200aa03ea87b91ce4c3ac79f0cecd52e2a7a56c7a4185023c271fbfa6da url: "https://pub.flutter-io.cn" source: hosted - version: "7.1.0" + version: "10.1.4" share_plus_platform_interface: dependency: transitive description: name: share_plus_platform_interface - sha256: "357412af4178d8e11d14f41723f80f12caea54cf0d5cd29af9dcdab85d58aea7" + sha256: cc012a23fc2d479854e6c80150696c4a5f5bb62cb89af4de1c505cf78d0a5d0b url: "https://pub.flutter-io.cn" source: hosted - version: "3.3.0" + version: "5.0.2" shared_preferences: dependency: "direct main" description: name: shared_preferences - sha256: b7f41bad7e521d205998772545de63ff4e6c97714775902c199353f8bf1511ac + sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" url: "https://pub.flutter-io.cn" source: hosted - version: "2.2.1" + version: "2.5.3" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06" + sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" url: "https://pub.flutter-io.cn" source: hosted - version: "2.2.1" + version: "2.4.10" shared_preferences_foundation: dependency: transitive description: name: shared_preferences_foundation - sha256: "7bf53a9f2d007329ee6f3df7268fd498f8373602f943c975598bbb34649b62a7" + sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03" url: "https://pub.flutter-io.cn" source: hosted - version: "2.3.4" + version: "2.5.4" shared_preferences_linux: dependency: transitive description: name: shared_preferences_linux - sha256: c2eb5bf57a2fe9ad6988121609e47d3e07bb3bdca5b6f8444e4cf302428a128a + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" url: "https://pub.flutter-io.cn" source: hosted - version: "2.3.1" + version: "2.4.1" shared_preferences_platform_interface: dependency: transitive description: name: shared_preferences_platform_interface - sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" url: "https://pub.flutter-io.cn" source: hosted - version: "2.3.1" + version: "2.4.1" shared_preferences_web: dependency: transitive description: name: shared_preferences_web - sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 url: "https://pub.flutter-io.cn" source: hosted - version: "2.2.1" + version: "2.4.3" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows - sha256: f763a101313bd3be87edffe0560037500967de9c394a714cd598d945517f694f + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" url: "https://pub.flutter-io.cn" source: hosted - version: "2.3.1" + version: "2.4.1" sky_engine: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_span: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" url: "https://pub.flutter-io.cn" source: hosted - version: "1.10.0" + version: "1.10.1" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.flutter-io.cn" + source: hosted + version: "7.0.0" sqflite: - dependency: "direct main" + dependency: transitive description: name: sqflite - sha256: "591f1602816e9c31377d5f008c2d9ef7b8aca8941c3f89cc5fd9d84da0c38a9a" + sha256: e2297b1da52f127bc7a3da11439985d9b536f75070f3325e62ada69a5c585d03 url: "https://pub.flutter-io.cn" source: hosted - version: "2.3.0" + version: "2.4.2" + sqflite_android: + dependency: transitive + description: + name: sqflite_android + sha256: "2b3070c5fa881839f8b402ee4a39c1b4d561704d4ebbbcfb808a119bc2a1701b" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.4.1" sqflite_common: dependency: transitive description: name: sqflite_common - sha256: "1b92f368f44b0dee2425bb861cfa17b6f6cf3961f762ff6f941d20b33355660a" + sha256: "84731e8bfd8303a3389903e01fb2141b6e59b5973cacbb0929021df08dddbe8b" url: "https://pub.flutter-io.cn" source: hosted - version: "2.5.0" + version: "2.5.5" sqflite_common_ffi: - dependency: "direct main" + dependency: transitive description: name: sqflite_common_ffi - sha256: "0d5cc1be2eb18400ac6701c31211d44164393aa75886093002ecdd947be04f93" + sha256: "1f3ef3888d3bfbb47785cc1dda0dc7dd7ebd8c1955d32a9e8e9dae1e38d1c4c1" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.3.5" + sqflite_common_ffi_web: + dependency: transitive + description: + name: sqflite_common_ffi_web + sha256: "61ea702e7aba727f28be7ead00b84c19c745cd4a4934d0c41473303df11ac9ea" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.4.5+4" + sqflite_darwin: + dependency: transitive + description: + name: sqflite_darwin + sha256: "279832e5cde3fe99e8571879498c9211f3ca6391b0d818df4e17d9fff5c6ccb3" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.4.2" + sqflite_platform_interface: + dependency: transitive + description: + name: sqflite_platform_interface + sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920" url: "https://pub.flutter-io.cn" source: hosted - version: "2.3.0+2" + version: "2.4.0" sqlite3: dependency: transitive description: name: sqlite3 - sha256: db65233e6b99e99b2548932f55a987961bc06d82a31a0665451fa0b4fff4c3fb + sha256: c0503c69b44d5714e6abbf4c1f51a3c3cc42b75ce785f44404765e4635481d38 url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.0" + version: "2.7.6" stack_trace: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.flutter-io.cn" source: hosted - version: "1.11.1" - storage: - dependency: "direct main" - description: - path: "packages/storage" - relative: true - source: path - version: "0.0.1" + version: "1.12.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.2" + version: "2.1.4" stream_transform: - dependency: "direct main" + dependency: transitive description: name: stream_transform - sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.0" + version: "2.1.1" string_scanner: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" url: "https://pub.flutter-io.cn" source: hosted - version: "1.2.0" + version: "1.4.1" synchronized: dependency: transitive description: name: synchronized - sha256: "7b530acd9cb7c71b0019a1e7fa22c4105e675557a4400b6a401c71c5e0ade1ac" + sha256: "0669c70faae6270521ee4f05bffd2919892d42d1276e6c495be80174b6bc0ef6" url: "https://pub.flutter-io.cn" source: hosted - version: "3.0.0+3" + version: "3.3.1" term_glyph: dependency: transitive description: name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" url: "https://pub.flutter-io.cn" source: hosted - version: "1.2.1" + version: "1.2.2" test_api: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" url: "https://pub.flutter-io.cn" source: hosted - version: "0.6.1" + version: "0.7.6" toggle_rotate: dependency: "direct main" description: @@ -747,183 +1137,265 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.0.1" + tolyui: + dependency: "direct main" + description: + name: tolyui + sha256: "800e95b470fd013adb1d8f84944b5498cbca59307cd50ff2b3affc98c57af51e" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.0.4+10" + tolyui_color: + dependency: transitive + description: + name: tolyui_color + sha256: e79eed0a525a584dc51df9ee5445bee190d9a9b5f14690038800c4983b2c744a + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.0.2" + tolyui_feedback: + dependency: transitive + description: + name: tolyui_feedback + sha256: b6d67fd448a8d39762fd14266c706fc8da7671eaaaeb81f42a12e467ff4dfbf7 + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.3.6+6" + tolyui_message: + dependency: transitive + description: + name: tolyui_message + sha256: "3811b9666d90d0088c35cacfe77ef990ed6019f801000d0e39ed8f500817b3bf" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.2.6+1" + tolyui_meta: + dependency: transitive + description: + name: tolyui_meta + sha256: "449bca06271d6c5d2a6b49192fd9176fa14b86fcd57c7317c4466a9d0b334674" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.0.2+1" + tolyui_navigation: + dependency: transitive + description: + name: tolyui_navigation + sha256: "9fe5eddb1b8f9c178f81a507937815e9e7e7b4872ac70dd567428cd41c84e2e1" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.2.0+3" + tolyui_refresh: + dependency: "direct main" + description: + name: tolyui_refresh + sha256: "7249643bbe67e12af38841737c1968e27a21436070acd254097c8b279bc643a9" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.0.1+1" + tolyui_rx_layout: + dependency: transitive + description: + name: tolyui_rx_layout + sha256: "559198f0bc1d2b3d2beb75be54ae17b8d9fc4276d7ff099967e8007450ce0719" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.0" + tolyui_text: + dependency: transitive + description: + name: tolyui_text + sha256: "881c40567724ed059377d3a9c58bf4e840017ad3e7b5f5476df2bc7a147cb7d5" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.0.1+4" + two_dimensional_scrollables: + dependency: transitive + description: + name: two_dimensional_scrollables + sha256: "1b0b29095d86df509d115ac668f85c3fcb0bd1554ccf89c289ca4769bead49db" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.3.4" typed_data: dependency: transitive description: name: typed_data - sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.4.0" + unit_widgets_display: + dependency: transitive + description: + name: unit_widgets_display + sha256: bd1cfdef49c8a9cb56c9d60eb9007b35fd07228ecfd9cbc51397b00dc9efcad6 url: "https://pub.flutter-io.cn" source: hosted - version: "1.3.1" + version: "0.0.1+2" url_launcher: dependency: "direct main" description: name: url_launcher - sha256: "47e208a6711459d813ba18af120d9663c20bdf6985d6ad39fe165d2538378d27" + sha256: "9d06212b1362abc2f0f0d78e6f09f726608c74e3b9462e8368bb03314aa8d603" url: "https://pub.flutter-io.cn" source: hosted - version: "6.1.14" + version: "6.3.1" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: "6f91d30ce9060c204b2dbe728adb300750fa4b228e8f7ed1b961aa1ceb728799" + sha256: "8582d7f6fe14d2652b4c45c9b6c14c0b678c2af2d083a11b604caeba51930d79" url: "https://pub.flutter-io.cn" source: hosted - version: "6.0.22" + version: "6.3.16" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: "6ba7dddee26c9fae27c9203c424631109d73c8fa26cfa7bc3e35e751cb87f62e" + sha256: "7f2022359d4c099eea7df3fdf739f7d3d3b9faf3166fb1dd390775176e0b76cb" url: "https://pub.flutter-io.cn" source: hosted - version: "6.0.17" + version: "6.3.3" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - sha256: "207f4ddda99b95b4d4868320a352d374b0b7e05eefad95a4a26f57da413443f5" + sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935" url: "https://pub.flutter-io.cn" source: hosted - version: "3.0.5" + version: "3.2.1" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - sha256: a9b3ea9043eabfaadfa3fb89de67a11210d85569086d22b3854484beab8b3978 + sha256: "17ba2000b847f334f16626a574c702b196723af2a289e7a93ffcb79acff855c2" url: "https://pub.flutter-io.cn" source: hosted - version: "3.0.1" + version: "3.2.2" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface - sha256: "6c9ca697a5ae218ce56cece69d46128169a58aa8653c1b01d26fcd4aad8c4370" + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.2" + version: "2.3.2" url_launcher_web: dependency: transitive description: name: url_launcher_web - sha256: "81fe91b6c4f84f222d186a9d23c73157dc4c8e1c71489c4d08be1ad3b228f1aa" + sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2" url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.16" + version: "2.4.1" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: "254708f17f7c20a9c8c471f67d86d76d4a3f9c1591aad1e15292008aceb82771" + sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77" url: "https://pub.flutter-io.cn" source: hosted - version: "3.0.6" - utils: - dependency: "direct main" - description: - path: "packages/utils" - relative: true - source: path - version: "0.0.1" + version: "3.1.4" uuid: - dependency: transitive + dependency: "direct main" description: name: uuid - sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff url: "https://pub.flutter-io.cn" source: hosted - version: "3.0.7" + version: "4.5.1" vector_graphics: dependency: transitive description: name: vector_graphics - sha256: "670f6e07aca990b4a2bcdc08a784193c4ccdd1932620244c3a86bb72a0eac67f" + sha256: "44cc7104ff32563122a929e4620cf3efd584194eec6d1d913eb5ba593dbcf6de" url: "https://pub.flutter-io.cn" source: hosted - version: "1.1.7" + version: "1.1.18" vector_graphics_codec: dependency: transitive description: name: vector_graphics_codec - sha256: "7451721781d967db9933b63f5733b1c4533022c0ba373a01bdd79d1a5457f69f" + sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146" url: "https://pub.flutter-io.cn" source: hosted - version: "1.1.7" + version: "1.1.13" vector_graphics_compiler: dependency: transitive description: name: vector_graphics_compiler - sha256: "80a13c613c8bde758b1464a1755a7b3a8f2b6cec61fbf0f5a53c94c30f03ba2e" + sha256: "557a315b7d2a6dbb0aaaff84d857967ce6bdc96a63dc6ee2a57ce5a6ee5d3331" url: "https://pub.flutter-io.cn" source: hosted - version: "1.1.7" + version: "1.1.17" vector_math: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.4" - web: + version: "2.2.0" + vm_service: dependency: transitive + description: + name: vm_service + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + url: "https://pub.flutter-io.cn" + source: hosted + version: "15.0.0" + web: + dependency: "direct overridden" description: name: web - sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" url: "https://pub.flutter-io.cn" source: hosted - version: "0.3.0" + version: "1.1.1" webview_flutter: dependency: "direct main" description: name: webview_flutter - sha256: "82f6787d5df55907aa01e49bd9644f4ed1cc82af7a8257dd9947815959d2e755" + sha256: c3e4fe614b1c814950ad07186007eff2f2e5dd2935eba7b9a9a1af8e5885f1ba url: "https://pub.flutter-io.cn" source: hosted - version: "4.2.4" + version: "4.13.0" webview_flutter_android: dependency: transitive description: name: webview_flutter_android - sha256: "69787707339e7588aad256386475c18223cc0da87d9812343bc27c33c01eeab4" + sha256: f6e6afef6e234801da77170f7a1847ded8450778caf2fe13979d140484be3678 url: "https://pub.flutter-io.cn" source: hosted - version: "3.8.1" + version: "4.7.0" webview_flutter_platform_interface: dependency: transitive description: name: webview_flutter_platform_interface - sha256: "656e2aeaef318900fffd21468b6ddc7958c7092a642f0e7220bac328b70d4a81" + sha256: "7cb32b21825bd65569665c32bb00a34ded5779786d6201f5350979d2d529940d" url: "https://pub.flutter-io.cn" source: hosted - version: "2.3.1" + version: "2.13.0" webview_flutter_wkwebview: dependency: transitive description: name: webview_flutter_wkwebview - sha256: "7ae75a1a76b9463ae9278de7759ead68aa0eeb70f8d0175e3f9a6c43e911fe76" + sha256: a3d461fe3467014e05f3ac4962e5fdde2a4bf44c561cb53e9ae5c586600fdbc3 url: "https://pub.flutter-io.cn" source: hosted - version: "3.6.1" - widget_module: - dependency: "direct main" - description: - path: "packages/widget_module" - relative: true - source: path - version: "0.0.1" + version: "3.22.0" widget_repository: - dependency: "direct main" + dependency: transitive description: - path: "packages/widget_repository" + path: "modules/widget_system/widget_repository" relative: true source: path version: "0.0.1" - widgets: - dependency: "direct main" + widget_ui: + dependency: transitive description: - path: "packages/widgets" + path: "modules/widget_system/widget_ui" relative: true source: path version: "0.0.1" @@ -931,18 +1403,18 @@ packages: dependency: transitive description: name: win32 - sha256: "7dacfda1edcca378031db9905ad7d7bd56b29fd1a90b0908b71a52a12c41e36b" + sha256: "329edf97fdd893e0f1e3b9e88d6a0e627128cc17cc316a8d67fda8f1451178ba" url: "https://pub.flutter-io.cn" source: hosted - version: "5.0.3" + version: "5.13.0" window_manager: dependency: transitive description: name: window_manager - sha256: dcc865277f26a7dad263a47d0e405d77e21f12cb71f30333a52710a408690bd7 + sha256: "732896e1416297c63c9e3fb95aea72d0355f61390263982a47fd519169dc5059" url: "https://pub.flutter-io.cn" source: hosted - version: "0.3.7" + version: "0.4.3" wrapper: dependency: "direct main" description: @@ -955,34 +1427,26 @@ packages: dependency: transitive description: name: xdg_directories - sha256: "11541eedefbcaec9de35aa82650b695297ce668662bbd6e3911a7fabdbde589f" + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" url: "https://pub.flutter-io.cn" source: hosted - version: "0.2.0+2" + version: "1.1.0" xml: dependency: transitive description: name: xml - sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 url: "https://pub.flutter-io.cn" source: hosted - version: "6.3.0" + version: "6.5.0" yaml: dependency: transitive description: name: yaml - sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370" - url: "https://pub.flutter-io.cn" - source: hosted - version: "3.1.1" - yaml_modify: - dependency: "direct main" - description: - name: yaml_modify - sha256: "0c67ba263546f44b738c9ae14bd602bb1ef75c74a61a25b9111708a7a7e9af94" + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce url: "https://pub.flutter-io.cn" source: hosted - version: "1.0.1" + version: "3.1.3" sdks: - dart: ">=3.2.0-194.0.dev <4.0.0" - flutter: ">=3.10.0" + dart: ">=3.8.0-0 <4.0.0" + flutter: ">=3.27.0" diff --git a/pubspec.yaml b/pubspec.yaml index acec41429..cec27c193 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,13 +1,33 @@ name: flutter_unit -description: A new Flutter application. +description: All Platform Flutter Experience App. publish_to: none -version: 2.9.3+1 +version: 3.2.3+2006 author: 张风捷特烈 <1981462002@qq.com> homepage: https://juejin.cn/user/149189281194766/posts - environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.5.0 <4.0.0" +workspace: + - modules/basic_system/app + - modules/basic_system/app_update + - modules/basic_system/authentication + - modules/basic_system/components + - modules/basic_system/l10n + - modules/basic_system/storage + - modules/basic_system/toly_ui + - modules/basic_system/utils + + - modules/knowledge_system/algorithm + - modules/knowledge_system/artifact + - modules/knowledge_system/awesome + - modules/knowledge_system/layout + + - modules/painting_system/draw_system + + - modules/tools_system/treasure_tools + + - modules/widget_system/widget_module + - modules/knowledge_system/note dependencies: flutter: @@ -16,82 +36,69 @@ dependencies: sdk: flutter cupertino_icons: ^1.0.4 - - flutter_bloc: ^8.1.3 # 状态管理 - equatable: ^2.0.5 # 相等辅助 - stream_transform: ^2.1.0 - - archive: ^3.3.9 # 解压 - package_info_plus: ^4.1.0 # 应用包信息 - r_upgrade: ^0.4.2 # 应用升级 - webview_flutter: ^4.2.4 - - sqflite: ^2.3.0 # 数据库 - sqflite_common_ffi: ^2.3.0+2 # 数据库 - shared_preferences: ^2.2.1 # xml 固化 - path_provider: ^2.1.1 # 路径 - - dio: ^5.3.2 # 网络请求 + # 路由与状态管理 + go_router: ^14.2.0 # 路由管理 + flutter_bloc: ^8.1.6 # 状态管理 + + ## fx 架构 + fx_platform_adapter: 0.0.3 # 平台适配器 + fx_go_router_ext: 0.0.6+1 # 路由 + fx_dao: 0.0.3+4 + fx_dio: 0.0.4+3 + fx_boot_starter: 0.1.1 # app 启动器 + fx_trace: 0.0.5+5 # 异常追踪/监听 + + # 数据与持久化 + dio: ^5.4.3+1 # 网络请求 + shared_preferences: ^2.5.3 # xml 固化 jwt_decoder: ^2.0.1 # jwt 解析 - connectivity_plus: ^4.0.2 + path_provider: ^2.1.5 # 路径 - refresh: ^1.0.1 - toggle_rotate: ^1.0.1 - flutter_star: ^1.0.2 # 星星组件 - dash_painter: ^1.0.2 - wrapper: ^1.0.2 - yaml_modify: ^1.0.1 + # 平台功能 +# connectivity_plus: ^6.1.4 # 网络状态 + url_launcher: ^6.3.1 # url + archive: ^4.0.6 # 解压 + file_picker: ^10.1.9 # 文件选择器 + share_plus: ^10.1.4 # 文字分享 + package_info_plus: 8.1.4 - url_launcher: ^6.1.14 # url - share_plus: ^7.1.0 # 文字分享 + # 视图展示 + tolyui: 0.0.4+10 # tolyui + tolyui_refresh: 0.0.1+1 # 下拉刷新 - flutter_svg: ^2.0.7 - intl: ^0.18.0 - platform: ^3.1.0 - image: ^4.0.17 + dash_painter: ^1.0.2 # 虚线 + flutter_star: ^1.0.2 # 星星组件 flutter_spinkit: ^5.2.0 # loading - flutter_markdown: ^0.6.4 # markdown -# file_selector: ^0.9.2+2 - file_picker: ^5.5.0 - - widget_repository: - path: packages/widget_repository - utils: - path: packages/utils - storage: - path: packages/storage - widgets: - path: packages/widgets - artifact: - path: packages/artifact - algorithm: - path: packages/algorithm - authentication: - path: packages/authentication - app: - path: packages/app - app_update: - path: packages/app_update - components: - path: packages/components - widget_module: - path: packages/widget_module -# old_fancy_mobile_ui: -# path: packages/old_fancy_mobile_ui - + toggle_rotate: ^1.0.1 # 点击旋转 + wrapper: ^1.0.2 # 气泡包裹 + webview_flutter: ^4.2.4 # webview + flutter_markdown: ^0.7.2+1 # markdown + flutter_svg: ^2.0.17 # svg 展示 + + # 逻辑处理 + image: ^4.0.17 # 图像处理 + equatable: ^2.0.5 # 相等辅助 + uuid: ^4.5.1 dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^1.0.0 + flutter_lints: ^4.0.0 + +dependency_overrides: + web: ^1.0.0 + intl: 0.20.2 flutter: + generate: true uses-material-design: true assets: - assets/images/ - assets/data/ +# - assets/data/web/ - assets/images/head_icon/ - assets/images/widgets/ - assets/flutter.db + - assets/article.db - assets/version.json fonts: # 配置字体,可配置多个,支持ttf和otf,ttc等字体资源 @@ -115,9 +122,13 @@ flutter: - asset: assets/fonts/ComicNeue-Regular.ttf - family: CHOPS fonts: - - asset: assets/fonts/CHOPS.TTF - + - asset: assets/fonts/CHOPS.ttf +toly: + icon: + src_zip: '' + assets_dir: 'assets/iconfont' + file_dist: 'packages/app/lib/app/res/toly_icon.dart' diff --git a/test/app_update_test.dart b/test/app_update_test.dart new file mode 100644 index 000000000..235abe890 --- /dev/null +++ b/test/app_update_test.dart @@ -0,0 +1,11 @@ +import 'package:app/app.dart'; +import 'package:app_update/app_update.dart'; +import 'package:fx_dio/fx_dio.dart'; + +void main() async { + FxDio().register(const ScienceHost(), repInterceptor: ScienceRepInterceptor()); + + UpgradeApi api = UnitUpgradeApi(); + ApiRet info = await api.fetch(1,'zh'); + print(info.data); +} diff --git a/test/size.dart b/test/size.dart new file mode 100644 index 000000000..d942eabe6 --- /dev/null +++ b/test/size.dart @@ -0,0 +1,14 @@ +import 'dart:io'; +import 'package:path/path.dart' as p; +void main() async{ + Directory directory=Directory(r'D:\Projects\Flutter\Github\FlutterUnit\build_tools\output'); + List files = directory.listSync(); + Map map = {}; + for(FileSystemEntity file in files){ + if(file is File){ + map[p.basename(file.path)] = file.statSync().size; + } + } + + print(map); +} diff --git a/test/yaml_parser_test.dart b/test/yaml_parser_test.dart deleted file mode 100644 index 727198b34..000000000 --- a/test/yaml_parser_test.dart +++ /dev/null @@ -1,52 +0,0 @@ - -import 'dart:convert'; -import 'dart:io'; - -import 'package:flutter_unit/code_gen/icon_font_gen/icon_font_class_parser.dart'; -// import 'package:yaml/yaml.dart'; -import 'package:yaml_modify/yaml_modify.dart'; - -void main(){ - final String filePath = r'E:\Projects\Flutter\FlutterUnit\pubspec.yaml'; - File pubspecFile = File(filePath); - final String pubspec = pubspecFile.readAsStringSync(); - print(pubspec); - - // YamlEditor doc = YamlEditor(pubspec); - // print(doc); - final doc = loadYaml(pubspec); - print(doc); - - YamlList fontsList = doc['flutter']['fonts'] as YamlList; - - final modifiableDoc = getModifiableNode(doc); - final modifiableList = getModifiableNode(fontsList); - modifiableList.removeWhere((e) => e['family'] == 'TolyIcon'); - modifiableList.add( - YamlMap.wrap({ - 'family': 'TolyIcon3', - 'fonts':YamlList.wrap([YamlMap.wrap({'asset':'assets/iconfont/iconfont.ttf'})]) - }) - ); - modifiableDoc['flutter']['fonts'] = modifiableList; - final strYaml = toYamlString(modifiableDoc); - print(modifiableList); - - // // YamlMap flutterNode = doc[]; - // doc.update('flutter', (value) { - // // YamlList fontsNode = value as YamlList; - // // List filter = fontsNode.where((e) => e['family']!='TolyIcon').toList(); - // - // return YamlMap.wrap({ - // 'family': 'TolyIcon3', - // 'fonts':YamlList.wrap([YamlMap.wrap({'asset':'assets/iconfont/iconfont.ttf'})]) - // }); - // }); - - // YamlList fontsList = doc['flutter']['fonts'] as YamlList; - // - - // // doc. - print(doc); - -} \ No newline at end of file diff --git a/test/yaml_parser_test2.dart b/test/yaml_parser_test2.dart deleted file mode 100644 index da19f8870..000000000 --- a/test/yaml_parser_test2.dart +++ /dev/null @@ -1,65 +0,0 @@ - -import 'dart:convert'; -import 'dart:io'; - -import 'package:flutter_unit/code_gen/icon_font_gen/icon_font_class_parser.dart'; -// import 'package:yaml/yaml.dart'; -import 'package:yaml_modify/yaml_modify.dart'; - -void main(){ - String familyName = 'TolyIcon'; - String fontAssetsDist = 'assets/iconfont/iconfont.ttf'; - - // final String filePath = r'E:\Projects\Flutter\FlutterUnit\pubspec.yaml'; - final String filePath = r'E:\Projects\Flutter\Work\toly_image_edit\pubspec.yaml'; - File pubspecFile = File(filePath); - - List lines = pubspecFile.readAsLinesSync(); - - RegExp fontsRegex = RegExp(r'^ fonts:',multiLine: true); - bool hasFonts = fontsRegex.hasMatch(lines.join('\n')); - - if(!hasFonts){ - // 当前没有 fonts 节点,需要添加到 flutter 节点下 - int index = lines.indexWhere((e) => e.startsWith('flutter:')); - - List fonts = [ - ' fonts:', - ' - family: TolyIcon', - ' fonts:', - ' - asset: assets/iconfont/iconfont.ttf', - ]; - - lines.insertAll(index+1, fonts); - print(lines); - pubspecFile.writeAsStringSync(lines.join('\n')); - return; - } - // 存在 fonts 节点,查询 family ,有没有当前字体图标 - bool hasTargetFamily = false; - RegExp regExp = RegExp(r'^ +- family: +(\w+)'); - - for(int i=0;i e.startsWith(fontsRegex)); - List fonts = [ - ' - family: TolyIcon', - ' fonts:', - ' - asset: $fontAssetsDist', - ]; - lines.insertAll(index+1, fonts); - print(lines); - pubspecFile.writeAsStringSync(lines.join('\n')); - return; - } - -} \ No newline at end of file diff --git a/web/favicon.png b/web/favicon.png new file mode 100644 index 000000000..8aaa46ac1 Binary files /dev/null and b/web/favicon.png differ diff --git a/web/icons/Icon-192.png b/web/icons/Icon-192.png new file mode 100644 index 000000000..b749bfef0 Binary files /dev/null and b/web/icons/Icon-192.png differ diff --git a/web/icons/Icon-512.png b/web/icons/Icon-512.png new file mode 100644 index 000000000..88cfd48df Binary files /dev/null and b/web/icons/Icon-512.png differ diff --git a/web/icons/Icon-maskable-192.png b/web/icons/Icon-maskable-192.png new file mode 100644 index 000000000..eb9b4d76e Binary files /dev/null and b/web/icons/Icon-maskable-192.png differ diff --git a/web/icons/Icon-maskable-512.png b/web/icons/Icon-maskable-512.png new file mode 100644 index 000000000..d69c56691 Binary files /dev/null and b/web/icons/Icon-maskable-512.png differ diff --git a/web/index.html b/web/index.html new file mode 100644 index 000000000..437e36b39 --- /dev/null +++ b/web/index.html @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + flutter_unit + + + + + +
+
+
+ +
Flutter Unit Loading...
+
+
+
+ + + + + + + diff --git a/web/manifest.json b/web/manifest.json new file mode 100644 index 000000000..04dec196e --- /dev/null +++ b/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "flutter_unit", + "short_name": "flutter_unit", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} diff --git a/web/splash.gif b/web/splash.gif new file mode 100644 index 000000000..36293e6bb Binary files /dev/null and b/web/splash.gif differ diff --git a/web/splash.js b/web/splash.js new file mode 100644 index 000000000..840303883 --- /dev/null +++ b/web/splash.js @@ -0,0 +1,16 @@ +window.addEventListener('load', function(ev) { + // Download main.dart.js + _flutter.loader.load({ + {{flutter_js}} + {{flutter_build_config}} + serviceWorker: { + serviceWorkerVersion: {{flutter_service_worker_version}}, + }, + onEntrypointLoaded: function(engineInitializer) { + engineInitializer.initializeEngine().then(function(appRunner) { + document.getElementById("app_splash")?.remove(); + appRunner.runApp(); + }); + } +}); +}) \ No newline at end of file diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 115100c02..d05a061a0 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -99,3 +99,6 @@ install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" CONFIGURATIONS Profile;Release COMPONENT Runtime) + +# add libs dll to release +file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/libs/ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/runner/Release) diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 6e00ae0cd..d9665f3a7 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,17 +6,14 @@ #include "generated_plugin_registrant.h" -#include -#include +#include #include #include #include void RegisterPlugins(flutter::PluginRegistry* registry) { - ConnectivityPlusWindowsPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); - ScreenRetrieverPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("ScreenRetrieverPlugin")); + ScreenRetrieverWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ScreenRetrieverWindowsPluginCApi")); SharePlusWindowsPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); UrlLauncherWindowsRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 1610a5e5f..0cefa2b7c 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,8 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST - connectivity_plus - screen_retriever + screen_retriever_windows share_plus url_launcher_windows window_manager diff --git a/sqlite3.dll b/windows/libs/sqlite3.dll similarity index 100% rename from sqlite3.dll rename to windows/libs/sqlite3.dll